forked from cerc-io/plugeth
cmd, common, core, eth, node, rpc, tests, whisper, xeth: use protocol stacks
This commit is contained in:
parent
8a44451edf
commit
1e806c4c77
@ -34,6 +34,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
re "github.com/ethereum/go-ethereum/jsre"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/rpc/api"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
@ -77,7 +78,7 @@ func (r dumbterm) AppendHistory(string) {}
|
||||
|
||||
type jsre struct {
|
||||
re *re.JSRE
|
||||
ethereum *eth.Ethereum
|
||||
stack *node.Node
|
||||
xeth *xeth.XEth
|
||||
wait chan *big.Int
|
||||
ps1 string
|
||||
@ -176,18 +177,18 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str
|
||||
return js
|
||||
}
|
||||
|
||||
func newJSRE(ethereum *eth.Ethereum, docRoot, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
|
||||
js := &jsre{ethereum: ethereum, ps1: "> "}
|
||||
func newJSRE(stack *node.Node, docRoot, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
|
||||
js := &jsre{stack: stack, ps1: "> "}
|
||||
// set default cors domain used by startRpc from CLI flag
|
||||
js.corsDomain = corsDomain
|
||||
if f == nil {
|
||||
f = js
|
||||
}
|
||||
js.xeth = xeth.New(ethereum, f)
|
||||
js.xeth = xeth.New(stack, f)
|
||||
js.wait = js.xeth.UpdateState()
|
||||
js.client = client
|
||||
if clt, ok := js.client.(*comms.InProcClient); ok {
|
||||
if offeredApis, err := api.ParseApiString(shared.AllApis, codec.JSON, js.xeth, ethereum); err == nil {
|
||||
if offeredApis, err := api.ParseApiString(shared.AllApis, codec.JSON, js.xeth, stack); err == nil {
|
||||
clt.Initialize(api.Merge(offeredApis...))
|
||||
}
|
||||
}
|
||||
@ -202,14 +203,14 @@ func newJSRE(ethereum *eth.Ethereum, docRoot, corsDomain string, client comms.Et
|
||||
js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
|
||||
} else {
|
||||
lr := liner.NewLiner()
|
||||
js.withHistory(ethereum.DataDir, func(hist *os.File) { lr.ReadHistory(hist) })
|
||||
js.withHistory(stack.DataDir(), func(hist *os.File) { lr.ReadHistory(hist) })
|
||||
lr.SetCtrlCAborts(true)
|
||||
js.loadAutoCompletion()
|
||||
lr.SetWordCompleter(apiWordCompleter)
|
||||
lr.SetTabCompletionStyle(liner.TabPrints)
|
||||
js.prompter = lr
|
||||
js.atexit = func() {
|
||||
js.withHistory(ethereum.DataDir, func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
|
||||
js.withHistory(stack.DataDir(), func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
|
||||
lr.Close()
|
||||
close(js.wait)
|
||||
}
|
||||
@ -276,7 +277,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
|
||||
apiNames = append(apiNames, a)
|
||||
}
|
||||
|
||||
apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.ethereum)
|
||||
apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.stack)
|
||||
if err != nil {
|
||||
utils.Fatalf("Unable to determine supported api's: %v", err)
|
||||
}
|
||||
@ -342,8 +343,14 @@ func (self *jsre) AskPassword() (string, bool) {
|
||||
}
|
||||
|
||||
func (self *jsre) ConfirmTransaction(tx string) bool {
|
||||
if self.ethereum.NatSpec {
|
||||
notice := natspec.GetNotice(self.xeth, tx, self.ethereum.HTTPClient())
|
||||
// Retrieve the Ethereum instance from the node
|
||||
var ethereum *eth.Ethereum
|
||||
if _, err := self.stack.SingletonService(ðereum); err != nil {
|
||||
return false
|
||||
}
|
||||
// If natspec is enabled, ask for permission
|
||||
if ethereum.NatSpec {
|
||||
notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient())
|
||||
fmt.Println(notice)
|
||||
answer, _ := self.Prompt("Confirm Transaction [y/n]")
|
||||
return strings.HasPrefix(strings.Trim(answer, " "), "y")
|
||||
@ -359,7 +366,11 @@ func (self *jsre) UnlockAccount(addr []byte) bool {
|
||||
return false
|
||||
}
|
||||
// TODO: allow retry
|
||||
if err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil {
|
||||
var ethereum *eth.Ethereum
|
||||
if _, err := self.stack.SingletonService(ðereum); err != nil {
|
||||
return false
|
||||
}
|
||||
if err := ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil {
|
||||
return false
|
||||
} else {
|
||||
fmt.Println("Account is now unlocked for this session.")
|
||||
|
@ -38,6 +38,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
)
|
||||
@ -66,7 +67,10 @@ type testjethre struct {
|
||||
}
|
||||
|
||||
func (self *testjethre) UnlockAccount(acc []byte) bool {
|
||||
err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
|
||||
var ethereum *eth.Ethereum
|
||||
self.stack.SingletonService(ðereum)
|
||||
|
||||
err := ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
|
||||
if err != nil {
|
||||
panic("unable to unlock")
|
||||
}
|
||||
@ -74,67 +78,74 @@ func (self *testjethre) UnlockAccount(acc []byte) bool {
|
||||
}
|
||||
|
||||
func (self *testjethre) ConfirmTransaction(tx string) bool {
|
||||
if self.ethereum.NatSpec {
|
||||
var ethereum *eth.Ethereum
|
||||
self.stack.SingletonService(ðereum)
|
||||
|
||||
if ethereum.NatSpec {
|
||||
self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
|
||||
func testJEthRE(t *testing.T) (string, *testjethre, *node.Node) {
|
||||
return testREPL(t, nil)
|
||||
}
|
||||
|
||||
func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth.Ethereum) {
|
||||
func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *node.Node) {
|
||||
tmp, err := ioutil.TempDir("", "geth-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Create a networkless protocol stack
|
||||
stack, err := node.New(&node.Config{PrivateKey: testNodeKey, Name: "test", NoDiscovery: true})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create node: %v", err)
|
||||
}
|
||||
// Initialize and register the Ethereum protocol
|
||||
keystore := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
|
||||
accman := accounts.NewManager(keystore)
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)})
|
||||
ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
|
||||
am := accounts.NewManager(ks)
|
||||
conf := ð.Config{
|
||||
NodeKey: testNodeKey,
|
||||
DataDir: tmp,
|
||||
AccountManager: am,
|
||||
MaxPeers: 0,
|
||||
Name: "test",
|
||||
|
||||
ethConf := ð.Config{
|
||||
TestGenesisState: db,
|
||||
AccountManager: accman,
|
||||
DocRoot: "/",
|
||||
SolcPath: testSolcPath,
|
||||
PowTest: true,
|
||||
NewDB: func(path string) (ethdb.Database, error) { return db, nil },
|
||||
}
|
||||
if config != nil {
|
||||
config(conf)
|
||||
config(ethConf)
|
||||
}
|
||||
ethereum, err := eth.New(conf)
|
||||
if err != nil {
|
||||
t.Fatal("%v", err)
|
||||
if err := stack.Register("ethereum", func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
|
||||
t.Fatalf("failed to register ethereum protocol: %v", err)
|
||||
}
|
||||
|
||||
// Initialize all the keys for testing
|
||||
keyb, err := crypto.HexToECDSA(testKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key := crypto.NewKeyFromECDSA(keyb)
|
||||
err = ks.StoreKey(key, "")
|
||||
if err != nil {
|
||||
if err := keystore.StoreKey(key, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = am.Unlock(key.Address, "")
|
||||
if err != nil {
|
||||
if err := accman.Unlock(key.Address, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Start the node and assemble the REPL tester
|
||||
if err := stack.Start(); err != nil {
|
||||
t.Fatalf("failed to start test stack: %v", err)
|
||||
}
|
||||
var ethereum *eth.Ethereum
|
||||
stack.SingletonService(ðereum)
|
||||
|
||||
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
|
||||
client := comms.NewInProcClient(codec.JSON)
|
||||
tf := &testjethre{client: ethereum.HTTPClient()}
|
||||
repl := newJSRE(ethereum, assetPath, "", client, false, tf)
|
||||
repl := newJSRE(stack, assetPath, "", client, false, tf)
|
||||
tf.jsre = repl
|
||||
return tmp, tf, ethereum
|
||||
return tmp, tf, stack
|
||||
}
|
||||
|
||||
func TestNodeInfo(t *testing.T) {
|
||||
@ -151,11 +162,8 @@ func TestNodeInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccounts(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
tmp, repl, node := testJEthRE(t)
|
||||
defer node.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`)
|
||||
@ -174,11 +182,8 @@ func TestAccounts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBlockChain(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
tmp, repl, node := testJEthRE(t)
|
||||
defer node.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
// get current block dump before export/import.
|
||||
val, err := repl.re.Run("JSON.stringify(debug.dumpBlock(eth.blockNumber))")
|
||||
@ -196,6 +201,8 @@ func TestBlockChain(t *testing.T) {
|
||||
tmpfile := filepath.Join(extmp, "export.chain")
|
||||
tmpfileq := strconv.Quote(tmpfile)
|
||||
|
||||
var ethereum *eth.Ethereum
|
||||
node.SingletonService(ðereum)
|
||||
ethereum.BlockChain().Reset()
|
||||
|
||||
checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`)
|
||||
@ -209,22 +216,15 @@ func TestBlockChain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMining(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
tmp, repl, node := testJEthRE(t)
|
||||
defer node.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
checkEvalJSON(t, repl, `eth.mining`, `false`)
|
||||
}
|
||||
|
||||
func TestRPC(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
tmp, repl, node := testJEthRE(t)
|
||||
defer node.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 5004, "*", "web3,eth,net")`, `true`)
|
||||
@ -234,12 +234,8 @@ func TestCheckTestAccountBalance(t *testing.T) {
|
||||
t.Skip() // i don't think it tests the correct behaviour here. it's actually testing
|
||||
// internals which shouldn't be tested. This now fails because of a change in the core
|
||||
// and i have no means to fix this, sorry - @obscuren
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
tmp, repl, node := testJEthRE(t)
|
||||
defer node.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
repl.re.Run(`primary = "` + testAddress + `"`)
|
||||
@ -247,12 +243,8 @@ func TestCheckTestAccountBalance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSignature(t *testing.T) {
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
tmp, repl, node := testJEthRE(t)
|
||||
defer node.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
val, err := repl.re.Run(`eth.sign("` + testAddress + `", "` + testHash + `")`)
|
||||
@ -443,7 +435,10 @@ multiply7 = Multiply7.at(contractaddress);
|
||||
}
|
||||
|
||||
func pendingTransactions(repl *testjethre, t *testing.T) (txc int64, err error) {
|
||||
txs := repl.ethereum.TxPool().GetTransactions()
|
||||
var ethereum *eth.Ethereum
|
||||
repl.stack.SingletonService(ðereum)
|
||||
|
||||
txs := ethereum.TxPool().GetTransactions()
|
||||
return int64(len(txs)), nil
|
||||
}
|
||||
|
||||
@ -468,12 +463,15 @@ func processTxs(repl *testjethre, t *testing.T, expTxc int) bool {
|
||||
t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc)
|
||||
return false
|
||||
}
|
||||
err = repl.ethereum.StartMining(runtime.NumCPU(), "")
|
||||
var ethereum *eth.Ethereum
|
||||
repl.stack.SingletonService(ðereum)
|
||||
|
||||
err = ethereum.StartMining(runtime.NumCPU(), "")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error mining: %v", err)
|
||||
return false
|
||||
}
|
||||
defer repl.ethereum.StopMining()
|
||||
defer ethereum.StopMining()
|
||||
|
||||
timer := time.NewTimer(100 * time.Second)
|
||||
height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1))
|
||||
|
303
cmd/geth/main.go
303
cmd/geth/main.go
@ -33,13 +33,11 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
@ -68,22 +66,9 @@ func init() {
|
||||
}
|
||||
|
||||
app = utils.NewApp(Version, "the go-ethereum command line interface")
|
||||
app.Action = run
|
||||
app.Action = geth
|
||||
app.HideVersion = true // we have a command to print the version
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Action: blockRecovery,
|
||||
Name: "recover",
|
||||
Usage: "Attempts to recover a corrupted database by setting a new block by number or hash",
|
||||
Description: `
|
||||
The recover commands will attempt to read out the last
|
||||
block based on that.
|
||||
|
||||
recover #number recovers by number
|
||||
recover <hex> recovers by hash
|
||||
`,
|
||||
},
|
||||
blocktestCommand,
|
||||
importCommand,
|
||||
exportCommand,
|
||||
upgradedbCommand,
|
||||
@ -285,7 +270,7 @@ This command allows to open a console on a running geth node.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: execJSFiles,
|
||||
Action: execScripts,
|
||||
Name: "js",
|
||||
Usage: `executes the given JavaScript files in the Geth JavaScript VM`,
|
||||
Description: `
|
||||
@ -376,14 +361,6 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// makeExtra resolves extradata for the miner from a flag or returns a default.
|
||||
func makeExtra(ctx *cli.Context) []byte {
|
||||
if ctx.GlobalIsSet(utils.ExtraDataFlag.Name) {
|
||||
return []byte(ctx.GlobalString(utils.ExtraDataFlag.Name))
|
||||
}
|
||||
return makeDefaultExtra()
|
||||
}
|
||||
|
||||
func makeDefaultExtra() []byte {
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
@ -404,18 +381,13 @@ func makeDefaultExtra() []byte {
|
||||
return extra
|
||||
}
|
||||
|
||||
func run(ctx *cli.Context) {
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
cfg.ExtraData = makeExtra(ctx)
|
||||
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
startEth(ctx, ethereum)
|
||||
// this blocks the thread
|
||||
ethereum.WaitForShutdown()
|
||||
// geth is the main entry point into the system if no special subcommand is ran.
|
||||
// It creates a default node based on the command line arguments and runs it in
|
||||
// blocking mode, waiting for it to be shut down.
|
||||
func geth(ctx *cli.Context) {
|
||||
node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx)
|
||||
startNode(ctx, node)
|
||||
node.Wait()
|
||||
}
|
||||
|
||||
func attach(ctx *cli.Context) {
|
||||
@ -449,156 +421,107 @@ func attach(ctx *cli.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// console starts a new geth node, attaching a JavaScript console to it at the
|
||||
// same time.
|
||||
func console(ctx *cli.Context) {
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
cfg.ExtraData = makeExtra(ctx)
|
||||
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
// Create and start the node based on the CLI flags
|
||||
node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx)
|
||||
startNode(ctx, node)
|
||||
|
||||
// Attach to the newly started node, and either execute script or become interactive
|
||||
client := comms.NewInProcClient(codec.JSON)
|
||||
|
||||
startEth(ctx, ethereum)
|
||||
repl := newJSRE(
|
||||
ethereum,
|
||||
repl := newJSRE(node,
|
||||
ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
||||
client,
|
||||
true,
|
||||
nil,
|
||||
)
|
||||
client, true, nil)
|
||||
|
||||
if ctx.GlobalString(utils.ExecFlag.Name) != "" {
|
||||
repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
|
||||
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
|
||||
repl.batch(script)
|
||||
} else {
|
||||
repl.welcome()
|
||||
repl.interactive()
|
||||
}
|
||||
|
||||
ethereum.Stop()
|
||||
ethereum.WaitForShutdown()
|
||||
node.Stop()
|
||||
}
|
||||
|
||||
func execJSFiles(ctx *cli.Context) {
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
ethereum, err := eth.New(cfg)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
// execScripts starts a new geth node based on the CLI flags, and executes each
|
||||
// of the JavaScript files specified as command arguments.
|
||||
func execScripts(ctx *cli.Context) {
|
||||
// Create and start the node based on the CLI flags
|
||||
node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx)
|
||||
startNode(ctx, node)
|
||||
|
||||
// Attach to the newly started node and execute the given scripts
|
||||
client := comms.NewInProcClient(codec.JSON)
|
||||
startEth(ctx, ethereum)
|
||||
repl := newJSRE(
|
||||
ethereum,
|
||||
repl := newJSRE(node,
|
||||
ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
||||
client,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
client, false, nil)
|
||||
|
||||
for _, file := range ctx.Args() {
|
||||
repl.exec(file)
|
||||
}
|
||||
|
||||
ethereum.Stop()
|
||||
ethereum.WaitForShutdown()
|
||||
node.Stop()
|
||||
}
|
||||
|
||||
func unlockAccount(ctx *cli.Context, am *accounts.Manager, addr string, i int, inputpassphrases []string) (addrHex, auth string, passphrases []string) {
|
||||
var err error
|
||||
passphrases = inputpassphrases
|
||||
addrHex, err = utils.ParamToAddress(addr, am)
|
||||
if err == nil {
|
||||
// Attempt to unlock the account 3 times
|
||||
attempts := 3
|
||||
for tries := 0; tries < attempts; tries++ {
|
||||
msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", addr, tries+1, attempts)
|
||||
auth, passphrases = getPassPhrase(ctx, msg, false, i, passphrases)
|
||||
err = am.Unlock(common.HexToAddress(addrHex), auth)
|
||||
if err == nil || passphrases != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (common.Address, string) {
|
||||
// Try to unlock the specified account a few times
|
||||
account := utils.MakeAddress(accman, address)
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Unlock account '%s' (%v) failed: %v", addr, addrHex, err)
|
||||
for trials := 0; trials < 3; trials++ {
|
||||
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
||||
password := getPassPhrase(prompt, false, i, passwords)
|
||||
if err := accman.Unlock(account, password); err == nil {
|
||||
return account, password
|
||||
}
|
||||
fmt.Printf("Account '%s' (%v) unlocked.\n", addr, addrHex)
|
||||
return
|
||||
}
|
||||
// All trials expended to unlock account, bail out
|
||||
utils.Fatalf("Failed to unlock account: %s", address)
|
||||
return common.Address{}, ""
|
||||
}
|
||||
|
||||
func blockRecovery(ctx *cli.Context) {
|
||||
if len(ctx.Args()) < 1 {
|
||||
glog.Fatal("recover requires block number or hash")
|
||||
// startNode boots up the system node and all registered protocols, after which
|
||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||
// miner.
|
||||
func startNode(ctx *cli.Context, stack *node.Node) {
|
||||
// Start up the node itself
|
||||
utils.StartNode(stack)
|
||||
|
||||
// Unlock any account specifically requested
|
||||
var ethereum *eth.Ethereum
|
||||
if _, err := stack.SingletonService(ðereum); err != nil {
|
||||
utils.Fatalf("ethereum service not running: %v", err)
|
||||
}
|
||||
arg := ctx.Args().First()
|
||||
accman := ethereum.AccountManager()
|
||||
passwords := utils.MakePasswordList(ctx)
|
||||
|
||||
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
|
||||
blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"), cfg.DatabaseCache)
|
||||
if err != nil {
|
||||
glog.Fatalln("could not open db:", err)
|
||||
}
|
||||
|
||||
var block *types.Block
|
||||
if arg[0] == '#' {
|
||||
block = core.GetBlock(blockDb, core.GetCanonicalHash(blockDb, common.String2Big(arg[1:]).Uint64()))
|
||||
} else {
|
||||
block = core.GetBlock(blockDb, common.HexToHash(arg))
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
glog.Fatalln("block not found. Recovery failed")
|
||||
}
|
||||
|
||||
if err = core.WriteHeadBlockHash(blockDb, block.Hash()); err != nil {
|
||||
glog.Fatalln("block write err", err)
|
||||
}
|
||||
glog.Infof("Recovery succesful. New HEAD %x\n", block.Hash())
|
||||
}
|
||||
|
||||
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
|
||||
// Start Ethereum itself
|
||||
utils.StartEthereum(eth)
|
||||
|
||||
am := eth.AccountManager()
|
||||
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
|
||||
accounts := strings.Split(account, " ")
|
||||
var passphrases []string
|
||||
accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
|
||||
for i, account := range accounts {
|
||||
if len(account) > 0 {
|
||||
if account == "primary" {
|
||||
utils.Fatalf("the 'primary' keyword is deprecated. You can use integer indexes, but the indexes are not permanent, they can change if you add external keys, export your keys or copy your keystore to another node.")
|
||||
}
|
||||
_, _, passphrases = unlockAccount(ctx, am, account, i, passphrases)
|
||||
if trimmed := strings.TrimSpace(account); trimmed != "" {
|
||||
unlockAccount(ctx, accman, trimmed, i, passwords)
|
||||
}
|
||||
}
|
||||
// Start auxiliary services if enabled.
|
||||
if !ctx.GlobalBool(utils.IPCDisabledFlag.Name) {
|
||||
if err := utils.StartIPC(eth, ctx); err != nil {
|
||||
utils.Fatalf("Error string IPC: %v", err)
|
||||
if err := utils.StartIPC(stack, ctx); err != nil {
|
||||
utils.Fatalf("Failed to start IPC: %v", err)
|
||||
}
|
||||
}
|
||||
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
|
||||
if err := utils.StartRPC(eth, ctx); err != nil {
|
||||
utils.Fatalf("Error starting RPC: %v", err)
|
||||
if err := utils.StartRPC(stack, ctx); err != nil {
|
||||
utils.Fatalf("Failed to start RPC: %v", err)
|
||||
}
|
||||
}
|
||||
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
|
||||
err := eth.StartMining(
|
||||
ctx.GlobalInt(utils.MinerThreadsFlag.Name),
|
||||
ctx.GlobalString(utils.MiningGPUFlag.Name))
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil {
|
||||
utils.Fatalf("Failed to start mining: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func accountList(ctx *cli.Context) {
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
accts, err := am.Accounts()
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
accts, err := accman.Accounts()
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not list accounts: %v", err)
|
||||
}
|
||||
@ -607,67 +530,57 @@ func accountList(ctx *cli.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func getPassPhrase(ctx *cli.Context, desc string, confirmation bool, i int, inputpassphrases []string) (passphrase string, passphrases []string) {
|
||||
passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
|
||||
if len(passfile) == 0 {
|
||||
fmt.Println(desc)
|
||||
auth, err := utils.PromptPassword("Passphrase: ", true)
|
||||
// getPassPhrase retrieves the passwor associated with an account, either fetched
|
||||
// from a list of preloaded passphrases, or requested interactively from the user.
|
||||
func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
|
||||
// If a list of passwords was supplied, retrieve from them
|
||||
if len(passwords) > 0 {
|
||||
if i < len(passwords) {
|
||||
return passwords[i]
|
||||
}
|
||||
return passwords[len(passwords)-1]
|
||||
}
|
||||
// Otherwise prompt the user for the password
|
||||
fmt.Println(prompt)
|
||||
password, err := utils.PromptPassword("Passphrase: ", true)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
utils.Fatalf("Failed to read passphrase: %v", err)
|
||||
}
|
||||
if confirmation {
|
||||
confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
|
||||
confirm, err := utils.PromptPassword("Repeat passphrase: ", false)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
utils.Fatalf("Failed to read passphrase confirmation: %v", err)
|
||||
}
|
||||
if auth != confirm {
|
||||
utils.Fatalf("Passphrases did not match.")
|
||||
if password != confirm {
|
||||
utils.Fatalf("Passphrases do not match")
|
||||
}
|
||||
}
|
||||
passphrase = auth
|
||||
|
||||
} else {
|
||||
passphrases = inputpassphrases
|
||||
if passphrases == nil {
|
||||
passbytes, err := ioutil.ReadFile(passfile)
|
||||
if err != nil {
|
||||
utils.Fatalf("Unable to read password file '%s': %v", passfile, err)
|
||||
}
|
||||
// this is backwards compatible if the same password unlocks several accounts
|
||||
// it also has the consequence that trailing newlines will not count as part
|
||||
// of the password, so --password <(echo -n 'pass') will now work without -n
|
||||
passphrases = strings.Split(string(passbytes), "\n")
|
||||
}
|
||||
if i >= len(passphrases) {
|
||||
passphrase = passphrases[len(passphrases)-1]
|
||||
} else {
|
||||
passphrase = passphrases[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
return password
|
||||
}
|
||||
|
||||
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
||||
func accountCreate(ctx *cli.Context) {
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
passphrase, _ := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, nil)
|
||||
acct, err := am.NewAccount(passphrase)
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||
|
||||
account, err := accman.NewAccount(password)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
utils.Fatalf("Failed to create account: %v", err)
|
||||
}
|
||||
fmt.Printf("Address: %x\n", acct)
|
||||
fmt.Printf("Address: %x\n", account)
|
||||
}
|
||||
|
||||
// accountUpdate transitions an account from a previous format to the current
|
||||
// one, also providing the possibility to change the pass-phrase.
|
||||
func accountUpdate(ctx *cli.Context) {
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
arg := ctx.Args().First()
|
||||
if len(arg) == 0 {
|
||||
utils.Fatalf("account address or index must be given as argument")
|
||||
if len(ctx.Args()) == 0 {
|
||||
utils.Fatalf("No accounts specified to update")
|
||||
}
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
|
||||
addr, authFrom, passphrases := unlockAccount(ctx, am, arg, 0, nil)
|
||||
authTo, _ := getPassPhrase(ctx, "Please give a new password. Do not forget this password.", true, 0, passphrases)
|
||||
err := am.Update(common.HexToAddress(addr), authFrom, authTo)
|
||||
if err != nil {
|
||||
account, oldPassword := unlockAccount(ctx, accman, ctx.Args().First(), 0, nil)
|
||||
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
|
||||
if err := accman.Update(account, oldPassword, newPassword); err != nil {
|
||||
utils.Fatalf("Could not update the account: %v", err)
|
||||
}
|
||||
}
|
||||
@ -682,10 +595,10 @@ func importWallet(ctx *cli.Context) {
|
||||
utils.Fatalf("Could not read wallet file: %v", err)
|
||||
}
|
||||
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
passphrase, _ := getPassPhrase(ctx, "", false, 0, nil)
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
||||
|
||||
acct, err := am.ImportPreSaleKey(keyJson, passphrase)
|
||||
acct, err := accman.ImportPreSaleKey(keyJson, passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
}
|
||||
@ -697,9 +610,9 @@ func accountImport(ctx *cli.Context) {
|
||||
if len(keyfile) == 0 {
|
||||
utils.Fatalf("keyfile must be given as argument")
|
||||
}
|
||||
am := utils.MakeAccountManager(ctx)
|
||||
passphrase, _ := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, nil)
|
||||
acct, err := am.Import(keyfile, passphrase)
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||
acct, err := accman.Import(keyfile, passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
}
|
||||
|
@ -45,11 +45,6 @@ var (
|
||||
testKey = flag.String("key", defaultTestKey, "Private key of a test account to inject")
|
||||
)
|
||||
|
||||
var (
|
||||
ethereumServiceId = "ethereum"
|
||||
whisperServiceId = "whisper"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
@ -131,11 +126,11 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node
|
||||
TestGenesisBlock: test.Genesis,
|
||||
AccountManager: accman,
|
||||
}
|
||||
if err := stack.Register(ethereumServiceId, func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
|
||||
if err := stack.Register("ethereum", func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Initialize and register the Whisper protocol
|
||||
if err := stack.Register(whisperServiceId, func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
||||
if err := stack.Register("whisper", func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stack, nil
|
||||
@ -144,7 +139,9 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node
|
||||
// RunTest executes the specified test against an already pre-configured protocol
|
||||
// stack to ensure basic checks pass before running RPC tests.
|
||||
func RunTest(stack *node.Node, test *tests.BlockTest) error {
|
||||
blockchain := stack.Service(ethereumServiceId).(*eth.Ethereum).BlockChain()
|
||||
var ethereum *eth.Ethereum
|
||||
stack.SingletonService(ðereum)
|
||||
blockchain := ethereum.BlockChain()
|
||||
|
||||
// Process the blocks and verify the imported headers
|
||||
blocks, err := test.TryBlocksInsert(blockchain)
|
||||
|
41
cmd/utils/bootnodes.go
Normal file
41
cmd/utils/bootnodes.go
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utils
|
||||
|
||||
import "github.com/ethereum/go-ethereum/p2p/discover"
|
||||
|
||||
// FrontierBootNodes are the enode URLs of the P2P bootstrap nodes running on
|
||||
// the Frontier network.
|
||||
var FrontierBootNodes = []*discover.Node{
|
||||
// ETH/DEV Go Bootnodes
|
||||
discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE
|
||||
discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR
|
||||
discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG
|
||||
|
||||
// ETH/DEV Cpp Bootnodes
|
||||
discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"),
|
||||
}
|
||||
|
||||
// TestNetBootNodes are the enode URLs of the P2P bootstrap nodes running on the
|
||||
// Morden test network.
|
||||
var TestNetBootNodes = []*discover.Node{
|
||||
// ETH/DEV Go Bootnodes
|
||||
discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"),
|
||||
discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"),
|
||||
|
||||
// ETH/DEV Cpp Bootnodes
|
||||
}
|
@ -29,9 +29,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/peterh/liner"
|
||||
)
|
||||
@ -110,10 +110,9 @@ func Fatalf(format string, args ...interface{}) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func StartEthereum(ethereum *eth.Ethereum) {
|
||||
glog.V(logger.Info).Infoln("Starting", ethereum.Name())
|
||||
if err := ethereum.Start(); err != nil {
|
||||
Fatalf("Error starting Ethereum: %v", err)
|
||||
func StartNode(stack *node.Node) {
|
||||
if err := stack.Start(); err != nil {
|
||||
Fatalf("Error starting protocol stack: %v", err)
|
||||
}
|
||||
go func() {
|
||||
sigc := make(chan os.Signal, 1)
|
||||
@ -121,7 +120,7 @@ func StartEthereum(ethereum *eth.Ethereum) {
|
||||
defer signal.Stop(sigc)
|
||||
<-sigc
|
||||
glog.V(logger.Info).Infoln("Got interrupt, shutting down...")
|
||||
go ethereum.Stop()
|
||||
go stack.Stop()
|
||||
logger.Flush()
|
||||
for i := 10; i > 0; i-- {
|
||||
<-sigc
|
||||
|
@ -19,6 +19,7 @@ package utils
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
@ -28,12 +29,14 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
@ -42,6 +45,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc/api"
|
||||
@ -49,6 +54,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
@ -192,12 +198,12 @@ var (
|
||||
// Account settings
|
||||
UnlockedAccountFlag = cli.StringFlag{
|
||||
Name: "unlock",
|
||||
Usage: "Unlock an account (may be creation index) until this program exits (prompts for password)",
|
||||
Usage: "Comma separated list of accounts to unlock",
|
||||
Value: "",
|
||||
}
|
||||
PasswordFileFlag = cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "Password file to use with options/subcommands needing a pass phrase",
|
||||
Usage: "Password file to use for non-inteactive password input",
|
||||
Value: "",
|
||||
}
|
||||
|
||||
@ -316,7 +322,7 @@ var (
|
||||
}
|
||||
BootnodesFlag = cli.StringFlag{
|
||||
Name: "bootnodes",
|
||||
Usage: "Space-separated enode URLs for P2P discovery bootstrap",
|
||||
Usage: "Comma separated enode URLs for P2P discovery bootstrap",
|
||||
Value: "",
|
||||
}
|
||||
NodeKeyFileFlag = cli.StringFlag{
|
||||
@ -385,6 +391,90 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// MustMakeDataDir retrieves the currently requested data directory, terminating
|
||||
// if none (or the empty string) is specified. If the node is starting a testnet,
|
||||
// the a subdirectory of the specified datadir will be used.
|
||||
func MustMakeDataDir(ctx *cli.Context) string {
|
||||
if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
return filepath.Join(path, "/testnet")
|
||||
}
|
||||
return path
|
||||
}
|
||||
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
|
||||
return ""
|
||||
}
|
||||
|
||||
// MakeNodeKey creates a node key from set command line flags, either loading it
|
||||
// from a file or as a specified hex value. If neither flags were provided, this
|
||||
// method returns nil and an emphemeral key is to be generated.
|
||||
func MakeNodeKey(ctx *cli.Context) *ecdsa.PrivateKey {
|
||||
var (
|
||||
hex = ctx.GlobalString(NodeKeyHexFlag.Name)
|
||||
file = ctx.GlobalString(NodeKeyFileFlag.Name)
|
||||
|
||||
key *ecdsa.PrivateKey
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
case file != "" && hex != "":
|
||||
Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
|
||||
|
||||
case file != "":
|
||||
if key, err = crypto.LoadECDSA(file); err != nil {
|
||||
Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
|
||||
}
|
||||
|
||||
case hex != "":
|
||||
if key, err = crypto.HexToECDSA(hex); err != nil {
|
||||
Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// MakeNodeName creates a node name from a base set and the command line flags.
|
||||
func MakeNodeName(client, version string, ctx *cli.Context) string {
|
||||
name := common.MakeName(client, version)
|
||||
if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 {
|
||||
name += "/" + identity
|
||||
}
|
||||
if ctx.GlobalBool(VMEnableJitFlag.Name) {
|
||||
name += "/JIT"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// MakeBootstrapNodes creates a list of bootstrap nodes from the command line
|
||||
// flags, reverting to pre-configured ones if none have been specified.
|
||||
func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
|
||||
// Return pre-configured nodes if none were manually requested
|
||||
if !ctx.GlobalIsSet(BootnodesFlag.Name) {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
return TestNetBootNodes
|
||||
}
|
||||
return FrontierBootNodes
|
||||
}
|
||||
// Otherwise parse and use the CLI bootstrap nodes
|
||||
bootnodes := []*discover.Node{}
|
||||
|
||||
for _, url := range strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") {
|
||||
node, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
|
||||
continue
|
||||
}
|
||||
bootnodes = append(bootnodes, node)
|
||||
}
|
||||
return bootnodes
|
||||
}
|
||||
|
||||
// MakeListenAddress creates a TCP listening address string from set command
|
||||
// line flags.
|
||||
func MakeListenAddress(ctx *cli.Context) string {
|
||||
return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name))
|
||||
}
|
||||
|
||||
// MakeNAT creates a port mapper from set command line flags.
|
||||
func MakeNAT(ctx *cli.Context) nat.Interface {
|
||||
natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
|
||||
@ -394,64 +484,145 @@ func MakeNAT(ctx *cli.Context) nat.Interface {
|
||||
return natif
|
||||
}
|
||||
|
||||
// MakeNodeKey creates a node key from set command line flags.
|
||||
func MakeNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
|
||||
hex, file := ctx.GlobalString(NodeKeyHexFlag.Name), ctx.GlobalString(NodeKeyFileFlag.Name)
|
||||
var err error
|
||||
switch {
|
||||
case file != "" && hex != "":
|
||||
Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
|
||||
case file != "":
|
||||
if key, err = crypto.LoadECDSA(file); err != nil {
|
||||
Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
|
||||
// MakeGenesisBlock loads up a genesis block from an input file specified in the
|
||||
// command line, or returns the empty string if none set.
|
||||
func MakeGenesisBlock(ctx *cli.Context) string {
|
||||
genesis := ctx.GlobalString(GenesisFileFlag.Name)
|
||||
if genesis == "" {
|
||||
return ""
|
||||
}
|
||||
case hex != "":
|
||||
if key, err = crypto.HexToECDSA(hex); err != nil {
|
||||
Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
|
||||
data, err := ioutil.ReadFile(genesis)
|
||||
if err != nil {
|
||||
Fatalf("Failed to load custom genesis file: %v", err)
|
||||
}
|
||||
}
|
||||
return key
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// MakeEthConfig creates ethereum options from set command line flags.
|
||||
func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
|
||||
customName := ctx.GlobalString(IdentityFlag.Name)
|
||||
if len(customName) > 0 {
|
||||
clientID += "/" + customName
|
||||
// MakeAccountManager creates an account manager from set command line flags.
|
||||
func MakeAccountManager(ctx *cli.Context) *accounts.Manager {
|
||||
// Create the keystore crypto primitive, light if requested
|
||||
scryptN := crypto.StandardScryptN
|
||||
scryptP := crypto.StandardScryptP
|
||||
|
||||
if ctx.GlobalBool(LightKDFFlag.Name) {
|
||||
scryptN = crypto.LightScryptN
|
||||
scryptP = crypto.LightScryptP
|
||||
}
|
||||
am := MakeAccountManager(ctx)
|
||||
etherbase, err := ParamToAddress(ctx.GlobalString(EtherbaseFlag.Name), am)
|
||||
// Assemble an account manager using the configured datadir
|
||||
var (
|
||||
datadir = MustMakeDataDir(ctx)
|
||||
keystore = crypto.NewKeyStorePassphrase(filepath.Join(datadir, "keystore"), scryptN, scryptP)
|
||||
)
|
||||
return accounts.NewManager(keystore)
|
||||
}
|
||||
|
||||
// MakeAddress converts an account specified directly as a hex encoded string or
|
||||
// a key index in the key store to an internal account representation.
|
||||
func MakeAddress(accman *accounts.Manager, account string) common.Address {
|
||||
// If the specified account is a valid address, return it
|
||||
if common.IsHexAddress(account) {
|
||||
return common.HexToAddress(account)
|
||||
}
|
||||
// Otherwise try to interpret the account as a keystore index
|
||||
index, err := strconv.Atoi(account)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default")
|
||||
Fatalf("Invalid account address or index: '%s'", account)
|
||||
}
|
||||
// Assemble the entire eth configuration and return
|
||||
cfg := ð.Config{
|
||||
Name: common.MakeName(clientID, version),
|
||||
DataDir: MustDataDir(ctx),
|
||||
GenesisFile: ctx.GlobalString(GenesisFileFlag.Name),
|
||||
hex, err := accman.AddressByIndex(index)
|
||||
if err != nil {
|
||||
Fatalf("Failed to retrieve requested account #%d: %v", index, err)
|
||||
}
|
||||
return common.HexToAddress(hex)
|
||||
}
|
||||
|
||||
// MakeEtherbase retrieves the etherbase either from the directly specified
|
||||
// command line flags or from the keystore if CLI indexed.
|
||||
func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address {
|
||||
// If the specified etherbase is a valid address, return it
|
||||
etherbase := ctx.GlobalString(EtherbaseFlag.Name)
|
||||
if common.IsHexAddress(etherbase) {
|
||||
return common.HexToAddress(etherbase)
|
||||
}
|
||||
// If no etherbase was specified and no accounts are known, bail out
|
||||
accounts, _ := accman.Accounts()
|
||||
if etherbase == "" && len(accounts) == 0 {
|
||||
glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default")
|
||||
return common.Address{}
|
||||
}
|
||||
// Otherwise try to interpret the parameter as a keystore index
|
||||
index, err := strconv.Atoi(etherbase)
|
||||
if err != nil {
|
||||
Fatalf("Invalid account address or index: '%s'", etherbase)
|
||||
}
|
||||
hex, err := accman.AddressByIndex(index)
|
||||
if err != nil {
|
||||
Fatalf("Failed to set requested account #%d as etherbase: %v", index, err)
|
||||
}
|
||||
return common.HexToAddress(hex)
|
||||
}
|
||||
|
||||
// MakeMinerExtra resolves extradata for the miner from the set command line flags
|
||||
// or returns a default one composed on the client, runtime and OS metadata.
|
||||
func MakeMinerExtra(extra []byte, ctx *cli.Context) []byte {
|
||||
if ctx.GlobalIsSet(ExtraDataFlag.Name) {
|
||||
return []byte(ctx.GlobalString(ExtraDataFlag.Name))
|
||||
}
|
||||
return extra
|
||||
}
|
||||
|
||||
// MakePasswordList loads up a list of password from a file specified by the
|
||||
// command line flags.
|
||||
func MakePasswordList(ctx *cli.Context) []string {
|
||||
if path := ctx.GlobalString(PasswordFileFlag.Name); path != "" {
|
||||
blob, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
Fatalf("Failed to read password file: %v", err)
|
||||
}
|
||||
return strings.Split(string(blob), "\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeSystemNode sets up a local node, configures the services to launch and
|
||||
// assembles the P2P protocol stack.
|
||||
func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node.Node {
|
||||
// Avoid conflicting network flags
|
||||
networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag, OlympicFlag}
|
||||
for _, flag := range netFlags {
|
||||
if ctx.GlobalBool(flag.Name) {
|
||||
networks++
|
||||
}
|
||||
}
|
||||
if networks > 1 {
|
||||
Fatalf("The %v flags are mutually exclusive", netFlags)
|
||||
}
|
||||
// Configure the node's service container
|
||||
stackConf := &node.Config{
|
||||
DataDir: MustMakeDataDir(ctx),
|
||||
PrivateKey: MakeNodeKey(ctx),
|
||||
Name: MakeNodeName(name, version, ctx),
|
||||
NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name),
|
||||
BootstrapNodes: MakeBootstrapNodes(ctx),
|
||||
ListenAddr: MakeListenAddress(ctx),
|
||||
NAT: MakeNAT(ctx),
|
||||
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
||||
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
|
||||
}
|
||||
// Configure the Ethereum service
|
||||
accman := MakeAccountManager(ctx)
|
||||
|
||||
ethConf := ð.Config{
|
||||
Genesis: MakeGenesisBlock(ctx),
|
||||
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
||||
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
|
||||
DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
|
||||
SkipBcVersionCheck: false,
|
||||
NetworkId: ctx.GlobalInt(NetworkIdFlag.Name),
|
||||
LogFile: ctx.GlobalString(LogFileFlag.Name),
|
||||
Verbosity: ctx.GlobalInt(VerbosityFlag.Name),
|
||||
Etherbase: common.HexToAddress(etherbase),
|
||||
AccountManager: accman,
|
||||
Etherbase: MakeEtherbase(accman, ctx),
|
||||
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
|
||||
AccountManager: am,
|
||||
VmDebug: ctx.GlobalBool(VMDebugFlag.Name),
|
||||
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
||||
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
|
||||
Port: ctx.GlobalString(ListenPortFlag.Name),
|
||||
Olympic: ctx.GlobalBool(OlympicFlag.Name),
|
||||
NAT: MakeNAT(ctx),
|
||||
ExtraData: MakeMinerExtra(extra, ctx),
|
||||
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
|
||||
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
||||
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
|
||||
NodeKey: MakeNodeKey(ctx),
|
||||
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
|
||||
Dial: true,
|
||||
BootNodes: ctx.GlobalString(BootnodesFlag.Name),
|
||||
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
|
||||
GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)),
|
||||
GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)),
|
||||
@ -462,46 +633,70 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
|
||||
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
|
||||
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
|
||||
}
|
||||
// Configure the Whisper service
|
||||
shhEnable := ctx.GlobalBool(WhisperEnabledFlag.Name)
|
||||
|
||||
if ctx.GlobalBool(DevModeFlag.Name) && ctx.GlobalBool(TestNetFlag.Name) {
|
||||
glog.Fatalf("%s and %s are mutually exclusive\n", DevModeFlag.Name, TestNetFlag.Name)
|
||||
// Override any default configs in dev mode or the test net
|
||||
switch {
|
||||
case ctx.GlobalBool(OlympicFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
ethConf.NetworkId = 1
|
||||
}
|
||||
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
|
||||
ethConf.Genesis = core.OlympicGenesisBlock()
|
||||
}
|
||||
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
// testnet is always stored in the testnet folder
|
||||
cfg.DataDir += "/testnet"
|
||||
cfg.NetworkId = 2
|
||||
cfg.TestNet = true
|
||||
case ctx.GlobalBool(TestNetFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
ethConf.NetworkId = 2
|
||||
}
|
||||
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
|
||||
ethConf.Genesis = core.TestNetGenesisBlock()
|
||||
}
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
|
||||
if ctx.GlobalBool(VMEnableJitFlag.Name) {
|
||||
cfg.Name += "/JIT"
|
||||
}
|
||||
if ctx.GlobalBool(DevModeFlag.Name) {
|
||||
if !ctx.GlobalIsSet(VMDebugFlag.Name) {
|
||||
cfg.VmDebug = true
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
// Override the base network stack configs
|
||||
if !ctx.GlobalIsSet(DataDirFlag.Name) {
|
||||
stackConf.DataDir = filepath.Join(os.TempDir(), "/ethereum_dev_mode")
|
||||
}
|
||||
if !ctx.GlobalIsSet(MaxPeersFlag.Name) {
|
||||
cfg.MaxPeers = 0
|
||||
}
|
||||
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||
cfg.GasPrice = new(big.Int)
|
||||
stackConf.MaxPeers = 0
|
||||
}
|
||||
if !ctx.GlobalIsSet(ListenPortFlag.Name) {
|
||||
cfg.Port = "0" // auto port
|
||||
stackConf.ListenAddr = ":0"
|
||||
}
|
||||
// Override the Ethereum protocol configs
|
||||
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
|
||||
ethConf.Genesis = core.OlympicGenesisBlock()
|
||||
}
|
||||
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||
ethConf.GasPrice = new(big.Int)
|
||||
}
|
||||
if !ctx.GlobalIsSet(WhisperEnabledFlag.Name) {
|
||||
cfg.Shh = true
|
||||
shhEnable = true
|
||||
}
|
||||
if !ctx.GlobalIsSet(DataDirFlag.Name) {
|
||||
cfg.DataDir = os.TempDir() + "/ethereum_dev_mode"
|
||||
if !ctx.GlobalIsSet(VMDebugFlag.Name) {
|
||||
vm.Debug = true
|
||||
}
|
||||
cfg.PowTest = true
|
||||
cfg.DevMode = true
|
||||
|
||||
glog.V(logger.Info).Infoln("dev mode enabled")
|
||||
ethConf.PowTest = true
|
||||
}
|
||||
return cfg
|
||||
// Assemble and return the protocol stack
|
||||
stack, err := node.New(stackConf)
|
||||
if err != nil {
|
||||
Fatalf("Failed to create the protocol stack: %v", err)
|
||||
}
|
||||
if err := stack.Register("eth", func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
return eth.New(ctx, ethConf)
|
||||
}); err != nil {
|
||||
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||
}
|
||||
if shhEnable {
|
||||
if err := stack.Register("shh", func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
||||
Fatalf("Failed to register the Whisper service: %v", err)
|
||||
}
|
||||
}
|
||||
return stack
|
||||
}
|
||||
|
||||
// SetupLogger configures glog from the logging-related command line flags.
|
||||
@ -509,7 +704,12 @@ func SetupLogger(ctx *cli.Context) {
|
||||
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
|
||||
glog.CopyStandardLogTo("INFO")
|
||||
glog.SetToStderr(true)
|
||||
glog.SetLogDir(ctx.GlobalString(LogFileFlag.Name))
|
||||
if ctx.GlobalIsSet(LogFileFlag.Name) {
|
||||
logger.New("", ctx.GlobalString(LogFileFlag.Name), ctx.GlobalInt(VerbosityFlag.Name))
|
||||
}
|
||||
if ctx.GlobalIsSet(VMDebugFlag.Name) {
|
||||
vm.Debug = ctx.GlobalBool(VMDebugFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// SetupNetwork configures the system for either the main net or some test network.
|
||||
@ -535,7 +735,7 @@ func SetupVM(ctx *cli.Context) {
|
||||
|
||||
// MakeChain creates a chain manager from set command line flags.
|
||||
func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) {
|
||||
datadir := MustDataDir(ctx)
|
||||
datadir := MustMakeDataDir(ctx)
|
||||
cache := ctx.GlobalInt(CacheFlag.Name)
|
||||
|
||||
var err error
|
||||
@ -543,7 +743,7 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
|
||||
Fatalf("Could not open database: %v", err)
|
||||
}
|
||||
if ctx.GlobalBool(OlympicFlag.Name) {
|
||||
_, err := core.WriteTestNetGenesisBlock(chainDb, 42)
|
||||
_, err := core.WriteTestNetGenesisBlock(chainDb)
|
||||
if err != nil {
|
||||
glog.Fatalln(err)
|
||||
}
|
||||
@ -560,32 +760,6 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
|
||||
return chain, chainDb
|
||||
}
|
||||
|
||||
// MakeChain creates an account manager from set command line flags.
|
||||
func MakeAccountManager(ctx *cli.Context) *accounts.Manager {
|
||||
dataDir := MustDataDir(ctx)
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
dataDir += "/testnet"
|
||||
}
|
||||
scryptN := crypto.StandardScryptN
|
||||
scryptP := crypto.StandardScryptP
|
||||
if ctx.GlobalBool(LightKDFFlag.Name) {
|
||||
scryptN = crypto.LightScryptN
|
||||
scryptP = crypto.LightScryptP
|
||||
}
|
||||
ks := crypto.NewKeyStorePassphrase(filepath.Join(dataDir, "keystore"), scryptN, scryptP)
|
||||
return accounts.NewManager(ks)
|
||||
}
|
||||
|
||||
// MustDataDir retrieves the currently requested data directory, terminating if
|
||||
// none (or the empty string) is specified.
|
||||
func MustDataDir(ctx *cli.Context) string {
|
||||
if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
|
||||
return path
|
||||
}
|
||||
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
|
||||
return ""
|
||||
}
|
||||
|
||||
func IpcSocketPath(ctx *cli.Context) (ipcpath string) {
|
||||
if runtime.GOOS == "windows" {
|
||||
ipcpath = common.DefaultIpcPath()
|
||||
@ -605,39 +779,39 @@ func IpcSocketPath(ctx *cli.Context) (ipcpath string) {
|
||||
return
|
||||
}
|
||||
|
||||
func StartIPC(eth *eth.Ethereum, ctx *cli.Context) error {
|
||||
// StartIPC starts a IPC JSON-RPC API server.
|
||||
func StartIPC(stack *node.Node, ctx *cli.Context) error {
|
||||
config := comms.IpcConfig{
|
||||
Endpoint: IpcSocketPath(ctx),
|
||||
}
|
||||
|
||||
initializer := func(conn net.Conn) (comms.Stopper, shared.EthereumApi, error) {
|
||||
fe := useragent.NewRemoteFrontend(conn, eth.AccountManager())
|
||||
xeth := xeth.New(eth, fe)
|
||||
apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec.JSON, xeth, eth)
|
||||
fe := useragent.NewRemoteFrontend(conn, stack.Service("eth").(*eth.Ethereum).AccountManager())
|
||||
xeth := xeth.New(stack, fe)
|
||||
apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec.JSON, xeth, stack)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return xeth, api.Merge(apis...), nil
|
||||
}
|
||||
|
||||
return comms.StartIpc(config, codec.JSON, initializer)
|
||||
}
|
||||
|
||||
func StartRPC(eth *eth.Ethereum, ctx *cli.Context) error {
|
||||
// StartRPC starts a HTTP JSON-RPC API server.
|
||||
func StartRPC(stack *node.Node, ctx *cli.Context) error {
|
||||
config := comms.HttpConfig{
|
||||
ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name),
|
||||
ListenPort: uint(ctx.GlobalInt(RPCPortFlag.Name)),
|
||||
CorsDomain: ctx.GlobalString(RPCCORSDomainFlag.Name),
|
||||
}
|
||||
|
||||
xeth := xeth.New(eth, nil)
|
||||
xeth := xeth.New(stack, nil)
|
||||
codec := codec.JSON
|
||||
|
||||
apis, err := api.ParseApiString(ctx.GlobalString(RpcApiFlag.Name), codec, xeth, eth)
|
||||
apis, err := api.ParseApiString(ctx.GlobalString(RpcApiFlag.Name), codec, xeth, stack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return comms.StartHttp(config, codec, api.Merge(apis...))
|
||||
}
|
||||
|
||||
@ -647,20 +821,3 @@ func StartPProf(ctx *cli.Context) {
|
||||
log.Println(http.ListenAndServe(address, nil))
|
||||
}()
|
||||
}
|
||||
|
||||
func ParamToAddress(addr string, am *accounts.Manager) (addrHex string, err error) {
|
||||
if !((len(addr) == 40) || (len(addr) == 42)) { // with or without 0x
|
||||
index, err := strconv.Atoi(addr)
|
||||
if err != nil {
|
||||
Fatalf("Invalid account address '%s'", addr)
|
||||
}
|
||||
|
||||
addrHex, err = am.AddressByIndex(index)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
addrHex = addr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
xe "github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
@ -146,13 +148,11 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
|
||||
}
|
||||
|
||||
// only use minimalistic stack with no networking
|
||||
return eth.New(ð.Config{
|
||||
DataDir: tmp,
|
||||
return eth.New(&node.ServiceContext{EventMux: new(event.TypeMux)}, ð.Config{
|
||||
AccountManager: am,
|
||||
Etherbase: common.HexToAddress(testAddress),
|
||||
MaxPeers: 0,
|
||||
PowTest: true,
|
||||
NewDB: func(path string) (ethdb.Database, error) { return db, nil },
|
||||
TestGenesisState: db,
|
||||
GpoMinGasPrice: common.Big1,
|
||||
GpobaseCorrectionFactor: 1,
|
||||
GpoMaxGasPrice: common.Big1,
|
||||
@ -166,7 +166,7 @@ func testInit(t *testing.T) (self *testFrontend) {
|
||||
t.Errorf("error creating ethereum: %v", err)
|
||||
return
|
||||
}
|
||||
err = ethereum.Start()
|
||||
err = ethereum.Start(nil)
|
||||
if err != nil {
|
||||
t.Errorf("error starting ethereum: %v", err)
|
||||
return
|
||||
@ -174,7 +174,7 @@ func testInit(t *testing.T) (self *testFrontend) {
|
||||
|
||||
// mock frontend
|
||||
self = &testFrontend{t: t, ethereum: ethereum}
|
||||
self.xeth = xe.New(ethereum, self)
|
||||
self.xeth = xe.New(nil, self)
|
||||
self.wait = self.xeth.UpdateState()
|
||||
addr, _ := self.ethereum.Etherbase()
|
||||
|
||||
|
@ -24,13 +24,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
hashLength = 32
|
||||
addressLength = 20
|
||||
HashLength = 32
|
||||
AddressLength = 20
|
||||
)
|
||||
|
||||
type (
|
||||
Hash [hashLength]byte
|
||||
Address [addressLength]byte
|
||||
Hash [HashLength]byte
|
||||
Address [AddressLength]byte
|
||||
)
|
||||
|
||||
func BytesToHash(b []byte) Hash {
|
||||
@ -53,10 +53,10 @@ func (h Hash) Hex() string { return "0x" + Bytes2Hex(h[:]) }
|
||||
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
||||
func (h *Hash) SetBytes(b []byte) {
|
||||
if len(b) > len(h) {
|
||||
b = b[len(b)-hashLength:]
|
||||
b = b[len(b)-HashLength:]
|
||||
}
|
||||
|
||||
copy(h[hashLength-len(b):], b)
|
||||
copy(h[HashLength-len(b):], b)
|
||||
}
|
||||
|
||||
// Set string `s` to h. If s is larger than len(h) it will panic
|
||||
@ -92,6 +92,18 @@ func StringToAddress(s string) Address { return BytesToAddress([]byte(s)) }
|
||||
func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) }
|
||||
func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
|
||||
|
||||
// IsHexAddress verifies whether a string can represent a valid hex-encoded
|
||||
// Ethereum address or not.
|
||||
func IsHexAddress(s string) bool {
|
||||
if len(s) == 2+2*AddressLength && IsHex(s[2:]) {
|
||||
return true
|
||||
}
|
||||
if len(s) == 2*AddressLength && IsHex(s) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get the string representation of the underlying address
|
||||
func (a Address) Str() string { return string(a[:]) }
|
||||
func (a Address) Bytes() []byte { return a[:] }
|
||||
@ -102,9 +114,9 @@ func (a Address) Hex() string { return "0x" + Bytes2Hex(a[:]) }
|
||||
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
||||
func (a *Address) SetBytes(b []byte) {
|
||||
if len(b) > len(a) {
|
||||
b = b[len(b)-addressLength:]
|
||||
b = b[len(b)-AddressLength:]
|
||||
}
|
||||
copy(a[addressLength-len(b):], b)
|
||||
copy(a[AddressLength-len(b):], b)
|
||||
}
|
||||
|
||||
// Set string `s` to a. If s is larger than len(a) it will panic
|
||||
|
@ -34,7 +34,7 @@ func proc() (Validator, *BlockChain) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
var mux event.TypeMux
|
||||
|
||||
WriteTestNetGenesisBlock(db, 0)
|
||||
WriteTestNetGenesisBlock(db)
|
||||
blockchain, err := NewBlockChain(db, thePow(), &mux)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
@ -149,11 +149,7 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
|
||||
|
||||
bc.genesisBlock = bc.GetBlockByNumber(0)
|
||||
if bc.genesisBlock == nil {
|
||||
reader, err := NewDefaultGenesisReader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bc.genesisBlock, err = WriteGenesisBlock(chainDb, reader)
|
||||
bc.genesisBlock, err = WriteDefaultGenesisBlock(chainDb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func thePow() pow.PoW {
|
||||
|
||||
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
|
||||
var eventMux event.TypeMux
|
||||
WriteTestNetGenesisBlock(db, 0)
|
||||
WriteTestNetGenesisBlock(db)
|
||||
blockchain, err := NewBlockChain(db, thePow(), &eventMux)
|
||||
if err != nil {
|
||||
t.Error("failed creating blockchain:", err)
|
||||
@ -506,7 +506,7 @@ func testReorgShort(t *testing.T, full bool) {
|
||||
func testReorg(t *testing.T, first, second []int, td int64, full bool) {
|
||||
// Create a pristine block chain
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, _ := WriteTestNetGenesisBlock(db, 0)
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
bc := chm(genesis, db)
|
||||
|
||||
// Insert an easy and a difficult chain afterwards
|
||||
@ -553,7 +553,7 @@ func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
|
||||
func testBadHashes(t *testing.T, full bool) {
|
||||
// Create a pristine block chain
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, _ := WriteTestNetGenesisBlock(db, 0)
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
bc := chm(genesis, db)
|
||||
|
||||
// Create a chain, ban a hash and try to import
|
||||
@ -580,7 +580,7 @@ func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
|
||||
func testReorgBadHashes(t *testing.T, full bool) {
|
||||
// Create a pristine block chain
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, _ := WriteTestNetGenesisBlock(db, 0)
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
bc := chm(genesis, db)
|
||||
|
||||
// Create a chain, import and ban aferwards
|
||||
|
@ -220,7 +220,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||
evmux := &event.TypeMux{}
|
||||
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
genesis, _ := WriteTestNetGenesisBlock(db, 0)
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||
// Create and inject the requested chain
|
||||
|
File diff suppressed because one or more lines are too long
@ -17,6 +17,8 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -158,29 +160,42 @@ func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount)
|
||||
return block
|
||||
}
|
||||
|
||||
func WriteTestNetGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Block, error) {
|
||||
testGenesis := fmt.Sprintf(`{
|
||||
"nonce": "0x%x",
|
||||
"difficulty": "0x20000",
|
||||
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2FEFD8",
|
||||
"alloc": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1" },
|
||||
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||
}
|
||||
}`, types.EncodeNonce(nonce))
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(testGenesis))
|
||||
// WriteDefaultGenesisBlock assembles the official Ethereum genesis block and
|
||||
// writes it - along with all associated state - into a chain database.
|
||||
func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultGenesisBlock()))
|
||||
}
|
||||
|
||||
func WriteOlympicGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Block, error) {
|
||||
testGenesis := fmt.Sprintf(`{
|
||||
// WriteTestNetGenesisBlock assembles the Morden test network genesis block and
|
||||
// writes it - along with all associated state - into a chain database.
|
||||
func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(TestNetGenesisBlock()))
|
||||
}
|
||||
|
||||
// WriteOlympicGenesisBlock assembles the Olympic genesis block and writes it
|
||||
// along with all associated state into a chain database.
|
||||
func WriteOlympicGenesisBlock(db ethdb.Database) (*types.Block, error) {
|
||||
return WriteGenesisBlock(db, strings.NewReader(OlympicGenesisBlock()))
|
||||
}
|
||||
|
||||
// DefaultGenesisBlock assembles a JSON string representing the default Ethereum
|
||||
// genesis block.
|
||||
func DefaultGenesisBlock() string {
|
||||
reader, err := gzip.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultGenesisBlock)))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to access default genesis: %v", err))
|
||||
}
|
||||
blob, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to load default genesis: %v", err))
|
||||
}
|
||||
return string(blob)
|
||||
}
|
||||
|
||||
// OlympicGenesisBlock assembles a JSON string representing the Olympic genesis
|
||||
// block.
|
||||
func OlympicGenesisBlock() string {
|
||||
return fmt.Sprintf(`{
|
||||
"nonce":"0x%x",
|
||||
"gasLimit":"0x%x",
|
||||
"difficulty":"0x%x",
|
||||
@ -198,6 +213,27 @@ func WriteOlympicGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Bloc
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
|
||||
}
|
||||
}`, types.EncodeNonce(nonce), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(testGenesis))
|
||||
}`, types.EncodeNonce(42), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
|
||||
}
|
||||
|
||||
// TestNetGenesisBlock assembles a JSON string representing the Morden test net
|
||||
// genenis block.
|
||||
func TestNetGenesisBlock() string {
|
||||
return fmt.Sprintf(`{
|
||||
"nonce": "0x%x",
|
||||
"difficulty": "0x20000",
|
||||
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2FEFD8",
|
||||
"alloc": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1" },
|
||||
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||
}
|
||||
}`, types.EncodeNonce(0x6d6f7264656e))
|
||||
}
|
||||
|
329
eth/backend.go
329
eth/backend.go
@ -19,16 +19,12 @@ package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
@ -37,21 +33,16 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -63,74 +54,29 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
jsonlogger = logger.NewJsonLogger()
|
||||
|
||||
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
|
||||
portInUseErrRE = regexp.MustCompile("address already in use")
|
||||
|
||||
defaultBootNodes = []*discover.Node{
|
||||
// ETH/DEV Go Bootnodes
|
||||
discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE
|
||||
discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR
|
||||
discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG
|
||||
// ETH/DEV cpp-ethereum (poc-9.ethdev.com)
|
||||
discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"),
|
||||
}
|
||||
|
||||
defaultTestNetBootNodes = []*discover.Node{
|
||||
discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"),
|
||||
discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"),
|
||||
}
|
||||
|
||||
staticNodes = "static-nodes.json" // Path within <datadir> to search for the static node list
|
||||
trustedNodes = "trusted-nodes.json" // Path within <datadir> to search for the trusted node list
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DevMode bool
|
||||
TestNet bool
|
||||
|
||||
Name string
|
||||
NetworkId int
|
||||
GenesisFile string
|
||||
GenesisBlock *types.Block // used by block tests
|
||||
FastSync bool
|
||||
Olympic bool
|
||||
NetworkId int // Network ID to use for selecting peers to connect to
|
||||
Genesis string // Genesis JSON to seed the chain database with
|
||||
FastSync bool // Enables the state download based fast synchronisation algorithm
|
||||
|
||||
BlockChainVersion int
|
||||
SkipBcVersionCheck bool // e.g. blockchain export
|
||||
DatabaseCache int
|
||||
|
||||
DataDir string
|
||||
LogFile string
|
||||
Verbosity int
|
||||
VmDebug bool
|
||||
NatSpec bool
|
||||
DocRoot string
|
||||
AutoDAG bool
|
||||
PowTest bool
|
||||
ExtraData []byte
|
||||
|
||||
MaxPeers int
|
||||
MaxPendingPeers int
|
||||
Discovery bool
|
||||
Port string
|
||||
|
||||
// Space-separated list of discovery node URLs
|
||||
BootNodes string
|
||||
|
||||
// This key is used to identify the node on the network.
|
||||
// If nil, an ephemeral key is used.
|
||||
NodeKey *ecdsa.PrivateKey
|
||||
|
||||
NAT nat.Interface
|
||||
Shh bool
|
||||
Dial bool
|
||||
|
||||
AccountManager *accounts.Manager
|
||||
Etherbase common.Address
|
||||
GasPrice *big.Int
|
||||
MinerThreads int
|
||||
AccountManager *accounts.Manager
|
||||
SolcPath string
|
||||
|
||||
GpoMinGasPrice *big.Int
|
||||
@ -140,87 +86,8 @@ type Config struct {
|
||||
GpobaseStepUp int
|
||||
GpobaseCorrectionFactor int
|
||||
|
||||
// NewDB is used to create databases.
|
||||
// If nil, the default is to create leveldb databases on disk.
|
||||
NewDB func(path string) (ethdb.Database, error)
|
||||
}
|
||||
|
||||
func (cfg *Config) parseBootNodes() []*discover.Node {
|
||||
if cfg.BootNodes == "" {
|
||||
if cfg.TestNet {
|
||||
return defaultTestNetBootNodes
|
||||
}
|
||||
|
||||
return defaultBootNodes
|
||||
}
|
||||
var ns []*discover.Node
|
||||
for _, url := range strings.Split(cfg.BootNodes, " ") {
|
||||
if url == "" {
|
||||
continue
|
||||
}
|
||||
n, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
|
||||
continue
|
||||
}
|
||||
ns = append(ns, n)
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
// parseNodes parses a list of discovery node URLs loaded from a .json file.
|
||||
func (cfg *Config) parseNodes(file string) []*discover.Node {
|
||||
// Short circuit if no node config is present
|
||||
path := filepath.Join(cfg.DataDir, file)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return nil
|
||||
}
|
||||
// Load the nodes from the config file
|
||||
blob, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to access nodes: %v", err)
|
||||
return nil
|
||||
}
|
||||
nodelist := []string{}
|
||||
if err := json.Unmarshal(blob, &nodelist); err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to load nodes: %v", err)
|
||||
return nil
|
||||
}
|
||||
// Interpret the list as a discovery node array
|
||||
var nodes []*discover.Node
|
||||
for _, url := range nodelist {
|
||||
if url == "" {
|
||||
continue
|
||||
}
|
||||
node, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Node URL %s: %v\n", url, err)
|
||||
continue
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) {
|
||||
// use explicit key from command line args if set
|
||||
if cfg.NodeKey != nil {
|
||||
return cfg.NodeKey, nil
|
||||
}
|
||||
// use persistent key if present
|
||||
keyfile := filepath.Join(cfg.DataDir, "nodekey")
|
||||
key, err := crypto.LoadECDSA(keyfile)
|
||||
if err == nil {
|
||||
return key, nil
|
||||
}
|
||||
// no persistent key, generate and store a new one
|
||||
if key, err = crypto.GenerateKey(); err != nil {
|
||||
return nil, fmt.Errorf("could not generate server key: %v", err)
|
||||
}
|
||||
if err := crypto.SaveECDSA(keyfile, key); err != nil {
|
||||
glog.V(logger.Error).Infoln("could not persist nodekey: ", err)
|
||||
}
|
||||
return key, nil
|
||||
TestGenesisBlock *types.Block // Genesis block to seed the chain database with (testing only!)
|
||||
TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!)
|
||||
}
|
||||
|
||||
type Ethereum struct {
|
||||
@ -235,7 +102,6 @@ type Ethereum struct {
|
||||
txPool *core.TxPool
|
||||
blockchain *core.BlockChain
|
||||
accountManager *accounts.Manager
|
||||
whisper *whisper.Whisper
|
||||
pow *ethash.Ethash
|
||||
protocolManager *ProtocolManager
|
||||
SolcPath string
|
||||
@ -250,44 +116,28 @@ type Ethereum struct {
|
||||
|
||||
httpclient *httpclient.HTTPClient
|
||||
|
||||
net *p2p.Server
|
||||
eventMux *event.TypeMux
|
||||
miner *miner.Miner
|
||||
|
||||
// logger logger.LogSystem
|
||||
|
||||
Mining bool
|
||||
MinerThreads int
|
||||
NatSpec bool
|
||||
DataDir string
|
||||
AutoDAG bool
|
||||
PowTest bool
|
||||
autodagquit chan bool
|
||||
etherbase common.Address
|
||||
clientVersion string
|
||||
netVersionId int
|
||||
shhVersionId int
|
||||
}
|
||||
|
||||
func New(config *Config) (*Ethereum, error) {
|
||||
logger.New(config.DataDir, config.LogFile, config.Verbosity)
|
||||
|
||||
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
// Let the database take 3/4 of the max open files (TODO figure out a way to get the actual limit of the open files)
|
||||
const dbCount = 3
|
||||
ethdb.OpenFileLimit = 128 / (dbCount + 1)
|
||||
|
||||
newdb := config.NewDB
|
||||
if newdb == nil {
|
||||
newdb = func(path string) (ethdb.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) }
|
||||
}
|
||||
|
||||
// Open the chain database and perform any upgrades needed
|
||||
chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata"))
|
||||
chainDb, err := ctx.Database("chaindata", config.DatabaseCache)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
|
||||
err = fmt.Errorf("%v (check if another instance of geth is already running with the same data directory '%s')", err, config.DataDir)
|
||||
}
|
||||
return nil, fmt.Errorf("blockchain db err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
|
||||
db.Meter("eth/db/chaindata/")
|
||||
@ -299,56 +149,32 @@ func New(config *Config) (*Ethereum, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
|
||||
dappDb, err := ctx.Database("dapp", config.DatabaseCache)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
|
||||
err = fmt.Errorf("%v (check if another instance of geth is already running with the same data directory '%s')", err, config.DataDir)
|
||||
}
|
||||
return nil, fmt.Errorf("dapp db err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if db, ok := dappDb.(*ethdb.LDBDatabase); ok {
|
||||
db.Meter("eth/db/dapp/")
|
||||
}
|
||||
|
||||
nodeDb := filepath.Join(config.DataDir, "nodes")
|
||||
glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId)
|
||||
|
||||
if len(config.GenesisFile) > 0 {
|
||||
fr, err := os.Open(config.GenesisFile)
|
||||
// Load up any custom genesis block if requested
|
||||
if len(config.Genesis) > 0 {
|
||||
block, err := core.WriteGenesisBlock(chainDb, strings.NewReader(config.Genesis))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := core.WriteGenesisBlock(chainDb, fr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash())
|
||||
}
|
||||
glog.V(logger.Info).Infof("Successfully wrote genesis block. New genesis hash = %x\n", block.Hash())
|
||||
// Load up a test setup if directly injected
|
||||
if config.TestGenesisState != nil {
|
||||
chainDb = config.TestGenesisState
|
||||
}
|
||||
|
||||
// different modes
|
||||
switch {
|
||||
case config.Olympic:
|
||||
glog.V(logger.Error).Infoln("Starting Olympic network")
|
||||
fallthrough
|
||||
case config.DevMode:
|
||||
_, err := core.WriteOlympicGenesisBlock(chainDb, 42)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case config.TestNet:
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
_, err := core.WriteTestNetGenesisBlock(chainDb, 0x6d6f7264656e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// This is for testing only.
|
||||
if config.GenesisBlock != nil {
|
||||
core.WriteTd(chainDb, config.GenesisBlock.Hash(), config.GenesisBlock.Difficulty())
|
||||
core.WriteBlock(chainDb, config.GenesisBlock)
|
||||
core.WriteCanonicalHash(chainDb, config.GenesisBlock.Hash(), config.GenesisBlock.NumberU64())
|
||||
core.WriteHeadBlockHash(chainDb, config.GenesisBlock.Hash())
|
||||
if config.TestGenesisBlock != nil {
|
||||
core.WriteTd(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.Difficulty())
|
||||
core.WriteBlock(chainDb, config.TestGenesisBlock)
|
||||
core.WriteCanonicalHash(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64())
|
||||
core.WriteHeadBlockHash(chainDb, config.TestGenesisBlock.Hash())
|
||||
}
|
||||
|
||||
if !config.SkipBcVersionCheck {
|
||||
@ -367,9 +193,7 @@ func New(config *Config) (*Ethereum, error) {
|
||||
dappDb: dappDb,
|
||||
eventMux: &event.TypeMux{},
|
||||
accountManager: config.AccountManager,
|
||||
DataDir: config.DataDir,
|
||||
etherbase: config.Etherbase,
|
||||
clientVersion: config.Name, // TODO should separate from Name
|
||||
netVersionId: config.NetworkId,
|
||||
NatSpec: config.NatSpec,
|
||||
MinerThreads: config.MinerThreads,
|
||||
@ -412,48 +236,9 @@ func New(config *Config) (*Ethereum, error) {
|
||||
eth.miner.SetGasPrice(config.GasPrice)
|
||||
eth.miner.SetExtra(config.ExtraData)
|
||||
|
||||
if config.Shh {
|
||||
eth.whisper = whisper.New()
|
||||
eth.shhVersionId = int(eth.whisper.Version())
|
||||
}
|
||||
|
||||
netprv, err := config.nodeKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocols := append([]p2p.Protocol{}, eth.protocolManager.SubProtocols...)
|
||||
if config.Shh {
|
||||
protocols = append(protocols, eth.whisper.Protocol())
|
||||
}
|
||||
eth.net = &p2p.Server{
|
||||
PrivateKey: netprv,
|
||||
Name: config.Name,
|
||||
MaxPeers: config.MaxPeers,
|
||||
MaxPendingPeers: config.MaxPendingPeers,
|
||||
Discovery: config.Discovery,
|
||||
Protocols: protocols,
|
||||
NAT: config.NAT,
|
||||
NoDial: !config.Dial,
|
||||
BootstrapNodes: config.parseBootNodes(),
|
||||
StaticNodes: config.parseNodes(staticNodes),
|
||||
TrustedNodes: config.parseNodes(trustedNodes),
|
||||
NodeDatabase: nodeDb,
|
||||
}
|
||||
if len(config.Port) > 0 {
|
||||
eth.net.ListenAddr = ":" + config.Port
|
||||
}
|
||||
|
||||
vm.Debug = config.VmDebug
|
||||
|
||||
return eth, nil
|
||||
}
|
||||
|
||||
// Network retrieves the underlying P2P network server. This should eventually
|
||||
// be moved out into a protocol independent package, but for now use an accessor.
|
||||
func (s *Ethereum) Network() *p2p.Server {
|
||||
return s.net
|
||||
}
|
||||
|
||||
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
|
||||
s.blockchain.ResetWithGenesisBlock(gb)
|
||||
}
|
||||
@ -480,86 +265,48 @@ func (s *Ethereum) StopMining() { s.miner.Stop() }
|
||||
func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
|
||||
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
||||
|
||||
// func (s *Ethereum) Logger() logger.LogSystem { return s.logger }
|
||||
func (s *Ethereum) Name() string { return s.net.Name }
|
||||
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
|
||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
|
||||
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
|
||||
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
|
||||
func (s *Ethereum) ClientVersion() string { return s.clientVersion }
|
||||
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
||||
func (s *Ethereum) NetVersion() int { return s.netVersionId }
|
||||
func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
|
||||
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
||||
|
||||
// Start the ethereum
|
||||
func (s *Ethereum) Start() error {
|
||||
jsonlogger.LogJson(&logger.LogStarting{
|
||||
ClientString: s.net.Name,
|
||||
ProtocolVersion: s.EthVersion(),
|
||||
})
|
||||
err := s.net.Start()
|
||||
if err != nil {
|
||||
if portInUseErrRE.MatchString(err.Error()) {
|
||||
err = fmt.Errorf("%v (possibly another instance of geth is using the same port)", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Protocols implements node.Service, returning all the currently configured
|
||||
// network protocols to start.
|
||||
func (s *Ethereum) Protocols() []p2p.Protocol {
|
||||
return s.protocolManager.SubProtocols
|
||||
}
|
||||
|
||||
// Start implements node.Service, starting all internal goroutines needed by the
|
||||
// Ethereum protocol implementation.
|
||||
func (s *Ethereum) Start(*p2p.Server) error {
|
||||
if s.AutoDAG {
|
||||
s.StartAutoDAG()
|
||||
}
|
||||
|
||||
s.protocolManager.Start()
|
||||
|
||||
if s.whisper != nil {
|
||||
s.whisper.Start()
|
||||
}
|
||||
|
||||
glog.V(logger.Info).Infoln("Server started")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) StartForTest() {
|
||||
jsonlogger.LogJson(&logger.LogStarting{
|
||||
ClientString: s.net.Name,
|
||||
ProtocolVersion: s.EthVersion(),
|
||||
})
|
||||
}
|
||||
|
||||
// AddPeer connects to the given node and maintains the connection until the
|
||||
// server is shut down. If the connection fails for any reason, the server will
|
||||
// attempt to reconnect the peer.
|
||||
func (self *Ethereum) AddPeer(nodeURL string) error {
|
||||
n, err := discover.ParseNode(nodeURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid node URL: %v", err)
|
||||
}
|
||||
self.net.AddPeer(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) Stop() {
|
||||
s.net.Stop()
|
||||
// Stop implements node.Service, terminating all internal goroutines used by the
|
||||
// Ethereum protocol.
|
||||
func (s *Ethereum) Stop() error {
|
||||
s.blockchain.Stop()
|
||||
s.protocolManager.Stop()
|
||||
s.txPool.Stop()
|
||||
s.eventMux.Stop()
|
||||
if s.whisper != nil {
|
||||
s.whisper.Stop()
|
||||
}
|
||||
|
||||
s.StopAutoDAG()
|
||||
|
||||
s.chainDb.Close()
|
||||
s.dappDb.Close()
|
||||
close(s.shutdownChan)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This function will wait for a shutdown and resumes main thread execution
|
||||
|
120
node/node.go
120
node/node.go
@ -21,6 +21,7 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
@ -41,13 +42,16 @@ var (
|
||||
// Node represents a P2P node into which arbitrary services might be registered.
|
||||
type Node struct {
|
||||
datadir string // Path to the currently used data directory
|
||||
config *p2p.Server // Configuration of the underlying P2P networking layer
|
||||
stack map[string]ServiceConstructor // Protocol stack registered into this node
|
||||
emux *event.TypeMux // Event multiplexer used between the services of a stack
|
||||
eventmux *event.TypeMux // Event multiplexer used between the services of a stack
|
||||
|
||||
running *p2p.Server // Currently running P2P networking layer
|
||||
serverConfig *p2p.Server // Configuration of the underlying P2P networking layer
|
||||
server *p2p.Server // Currently running P2P networking layer
|
||||
|
||||
serviceIndex map[string]ServiceConstructor // Set of services currently registered in the node
|
||||
serviceOrder []string // Service construction order to handle dependencies
|
||||
services map[string]Service // Currently running services
|
||||
|
||||
stop chan struct{} // Channel to wait for termination notifications
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
@ -66,7 +70,7 @@ func New(conf *Config) (*Node, error) {
|
||||
}
|
||||
return &Node{
|
||||
datadir: conf.DataDir,
|
||||
config: &p2p.Server{
|
||||
serverConfig: &p2p.Server{
|
||||
PrivateKey: conf.NodeKey(),
|
||||
Name: conf.Name,
|
||||
Discovery: !conf.NoDiscovery,
|
||||
@ -81,8 +85,9 @@ func New(conf *Config) (*Node, error) {
|
||||
MaxPeers: conf.MaxPeers,
|
||||
MaxPendingPeers: conf.MaxPendingPeers,
|
||||
},
|
||||
stack: make(map[string]ServiceConstructor),
|
||||
emux: new(event.TypeMux),
|
||||
serviceIndex: make(map[string]ServiceConstructor),
|
||||
serviceOrder: []string{},
|
||||
eventmux: new(event.TypeMux),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -92,14 +97,15 @@ func (n *Node) Register(id string, constructor ServiceConstructor) error {
|
||||
defer n.lock.Unlock()
|
||||
|
||||
// Short circuit if the node is running or if the id is taken
|
||||
if n.running != nil {
|
||||
if n.server != nil {
|
||||
return ErrNodeRunning
|
||||
}
|
||||
if _, ok := n.stack[id]; ok {
|
||||
if _, ok := n.serviceIndex[id]; ok {
|
||||
return ErrServiceRegistered
|
||||
}
|
||||
// Otherwise register the service and return
|
||||
n.stack[id] = constructor
|
||||
n.serviceOrder = append(n.serviceOrder, id)
|
||||
n.serviceIndex[id] = constructor
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -111,14 +117,20 @@ func (n *Node) Unregister(id string) error {
|
||||
defer n.lock.Unlock()
|
||||
|
||||
// Short circuit if the node is running, or if the service is unknown
|
||||
if n.running != nil {
|
||||
if n.server != nil {
|
||||
return ErrNodeRunning
|
||||
}
|
||||
if _, ok := n.stack[id]; !ok {
|
||||
if _, ok := n.serviceIndex[id]; !ok {
|
||||
return ErrServiceUnknown
|
||||
}
|
||||
// Otherwise drop the service and return
|
||||
delete(n.stack, id)
|
||||
delete(n.serviceIndex, id)
|
||||
for i, service := range n.serviceOrder {
|
||||
if service == id {
|
||||
n.serviceOrder = append(n.serviceOrder[:i], n.serviceOrder[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -128,19 +140,27 @@ func (n *Node) Start() error {
|
||||
defer n.lock.Unlock()
|
||||
|
||||
// Short circuit if the node's already running
|
||||
if n.running != nil {
|
||||
if n.server != nil {
|
||||
return ErrNodeRunning
|
||||
}
|
||||
// Otherwise copy and specialize the P2P configuration
|
||||
running := new(p2p.Server)
|
||||
*running = *n.config
|
||||
*running = *n.serverConfig
|
||||
|
||||
ctx := &ServiceContext{
|
||||
dataDir: n.datadir,
|
||||
EventMux: n.emux,
|
||||
}
|
||||
services := make(map[string]Service)
|
||||
for id, constructor := range n.stack {
|
||||
for _, id := range n.serviceOrder {
|
||||
constructor := n.serviceIndex[id]
|
||||
|
||||
// Create a new context for the particular service
|
||||
ctx := &ServiceContext{
|
||||
datadir: n.datadir,
|
||||
services: make(map[string]Service),
|
||||
EventMux: n.eventmux,
|
||||
}
|
||||
for id, s := range services { // copy needed for threaded access
|
||||
ctx.services[id] = s
|
||||
}
|
||||
// Construct and save the service
|
||||
service, err := constructor(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -161,10 +181,12 @@ func (n *Node) Start() error {
|
||||
started := []string{}
|
||||
for id, service := range services {
|
||||
// Start the next service, stopping all previous upon failure
|
||||
if err := service.Start(); err != nil {
|
||||
if err := service.Start(running); err != nil {
|
||||
for _, id := range started {
|
||||
services[id].Stop()
|
||||
}
|
||||
running.Stop()
|
||||
|
||||
return err
|
||||
}
|
||||
// Mark the service started for potential cleanup
|
||||
@ -172,7 +194,8 @@ func (n *Node) Start() error {
|
||||
}
|
||||
// Finish initializing the startup
|
||||
n.services = services
|
||||
n.running = running
|
||||
n.server = running
|
||||
n.stop = make(chan struct{})
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -184,7 +207,7 @@ func (n *Node) Stop() error {
|
||||
defer n.lock.Unlock()
|
||||
|
||||
// Short circuit if the node's not running
|
||||
if n.running == nil {
|
||||
if n.server == nil {
|
||||
return ErrNodeStopped
|
||||
}
|
||||
// Otherwise terminate all the services and the P2P server too
|
||||
@ -196,10 +219,11 @@ func (n *Node) Stop() error {
|
||||
failure.Services[id] = err
|
||||
}
|
||||
}
|
||||
n.running.Stop()
|
||||
n.server.Stop()
|
||||
|
||||
n.services = nil
|
||||
n.running = nil
|
||||
n.server = nil
|
||||
close(n.stop)
|
||||
|
||||
if len(failure.Services) > 0 {
|
||||
return failure
|
||||
@ -207,6 +231,19 @@ func (n *Node) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait blocks the thread until the node is stopped. If the node is not running
|
||||
// at the time of invocation, the method immediately returns.
|
||||
func (n *Node) Wait() {
|
||||
n.lock.RLock()
|
||||
if n.server == nil {
|
||||
return
|
||||
}
|
||||
stop := n.stop
|
||||
n.lock.RUnlock()
|
||||
|
||||
<-stop
|
||||
}
|
||||
|
||||
// Restart terminates a running node and boots up a new one in its place. If the
|
||||
// node isn't running, an error is returned.
|
||||
func (n *Node) Restart() error {
|
||||
@ -226,20 +263,45 @@ func (n *Node) Server() *p2p.Server {
|
||||
n.lock.RLock()
|
||||
defer n.lock.RUnlock()
|
||||
|
||||
return n.running
|
||||
return n.server
|
||||
}
|
||||
|
||||
// Service retrieves a currently running services registered under a given id.
|
||||
// Service retrieves a currently running service registered under a given id.
|
||||
func (n *Node) Service(id string) Service {
|
||||
n.lock.RLock()
|
||||
defer n.lock.RUnlock()
|
||||
|
||||
if n.services == nil {
|
||||
// Short circuit if the node's not running
|
||||
if n.server == nil {
|
||||
return nil
|
||||
}
|
||||
return n.services[id]
|
||||
}
|
||||
|
||||
// SingletonService retrieves a currently running service using a specific type
|
||||
// implementing the Service interface. This is a utility function for scenarios
|
||||
// where it is known that only one instance of a given service type is running,
|
||||
// allowing to access services without needing to know their specific id with
|
||||
// which they were registered. Note, this method uses reflection, so do not run
|
||||
// in a tight loop.
|
||||
func (n *Node) SingletonService(service interface{}) (string, error) {
|
||||
n.lock.RLock()
|
||||
defer n.lock.RUnlock()
|
||||
|
||||
// Short circuit if the node's not running
|
||||
if n.server == nil {
|
||||
return "", ErrServiceUnknown
|
||||
}
|
||||
// Otherwise try to find the service to return
|
||||
for id, running := range n.services {
|
||||
if reflect.TypeOf(running) == reflect.ValueOf(service).Elem().Type() {
|
||||
reflect.ValueOf(service).Elem().Set(reflect.ValueOf(running))
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
return "", ErrServiceUnknown
|
||||
}
|
||||
|
||||
// DataDir retrieves the current datadir used by the protocol stack.
|
||||
func (n *Node) DataDir() string {
|
||||
return n.datadir
|
||||
@ -248,5 +310,5 @@ func (n *Node) DataDir() string {
|
||||
// EventMux retrieves the event multiplexer used by all the network services in
|
||||
// the current protocol stack.
|
||||
func (n *Node) EventMux() *event.TypeMux {
|
||||
return n.emux
|
||||
return n.eventmux
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ import (
|
||||
type SampleService struct{}
|
||||
|
||||
func (s *SampleService) Protocols() []p2p.Protocol { return nil }
|
||||
func (s *SampleService) Start() error { fmt.Println("Sample service starting..."); return nil }
|
||||
func (s *SampleService) Stop() error { fmt.Println("Sample service stopping..."); return nil }
|
||||
func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil }
|
||||
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
|
||||
|
||||
func ExampleUsage() {
|
||||
// Create a network node to run protocols with the default values. The below list
|
||||
@ -80,8 +80,8 @@ func ExampleUsage() {
|
||||
log.Fatalf("Failed to stop the protocol stack: %v", err)
|
||||
}
|
||||
// Output:
|
||||
// Sample service starting...
|
||||
// Sample service stopping...
|
||||
// Sample service starting...
|
||||
// Sample service stopping...
|
||||
// Service starting...
|
||||
// Service stopping...
|
||||
// Service starting...
|
||||
// Service stopping...
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ func TestNodeUsedDataDir(t *testing.T) {
|
||||
type NoopService struct{}
|
||||
|
||||
func (s *NoopService) Protocols() []p2p.Protocol { return nil }
|
||||
func (s *NoopService) Start() error { return nil }
|
||||
func (s *NoopService) Start(*p2p.Server) error { return nil }
|
||||
func (s *NoopService) Stop() error { return nil }
|
||||
|
||||
func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil }
|
||||
@ -148,7 +148,7 @@ type InstrumentedService struct {
|
||||
stop error
|
||||
|
||||
protocolsHook func()
|
||||
startHook func()
|
||||
startHook func(*p2p.Server)
|
||||
stopHook func()
|
||||
}
|
||||
|
||||
@ -159,9 +159,9 @@ func (s *InstrumentedService) Protocols() []p2p.Protocol {
|
||||
return s.protocols
|
||||
}
|
||||
|
||||
func (s *InstrumentedService) Start() error {
|
||||
func (s *InstrumentedService) Start(server *p2p.Server) error {
|
||||
if s.startHook != nil {
|
||||
s.startHook()
|
||||
s.startHook(server)
|
||||
}
|
||||
return s.start
|
||||
}
|
||||
@ -189,7 +189,7 @@ func TestServiceLifeCycle(t *testing.T) {
|
||||
id := id // Closure for the constructor
|
||||
constructor := func(*ServiceContext) (Service, error) {
|
||||
return &InstrumentedService{
|
||||
startHook: func() { started[id] = true },
|
||||
startHook: func(*p2p.Server) { started[id] = true },
|
||||
stopHook: func() { stopped[id] = true },
|
||||
}, nil
|
||||
}
|
||||
@ -241,7 +241,7 @@ func TestServiceRestarts(t *testing.T) {
|
||||
running = false
|
||||
|
||||
return &InstrumentedService{
|
||||
startHook: func() {
|
||||
startHook: func(*p2p.Server) {
|
||||
if running {
|
||||
panic("already running")
|
||||
}
|
||||
@ -288,7 +288,7 @@ func TestServiceConstructionAbortion(t *testing.T) {
|
||||
id := id // Closure for the constructor
|
||||
constructor := func(*ServiceContext) (Service, error) {
|
||||
return &InstrumentedService{
|
||||
startHook: func() { started[id] = true },
|
||||
startHook: func(*p2p.Server) { started[id] = true },
|
||||
}, nil
|
||||
}
|
||||
if err := stack.Register(id, constructor); err != nil {
|
||||
@ -334,7 +334,7 @@ func TestServiceStartupAbortion(t *testing.T) {
|
||||
id := id // Closure for the constructor
|
||||
constructor := func(*ServiceContext) (Service, error) {
|
||||
return &InstrumentedService{
|
||||
startHook: func() { started[id] = true },
|
||||
startHook: func(*p2p.Server) { started[id] = true },
|
||||
stopHook: func() { stopped[id] = true },
|
||||
}, nil
|
||||
}
|
||||
@ -384,7 +384,7 @@ func TestServiceTerminationGuarantee(t *testing.T) {
|
||||
id := id // Closure for the constructor
|
||||
constructor := func(*ServiceContext) (Service, error) {
|
||||
return &InstrumentedService{
|
||||
startHook: func() { started[id] = true },
|
||||
startHook: func(*p2p.Server) { started[id] = true },
|
||||
stopHook: func() { stopped[id] = true },
|
||||
}, nil
|
||||
}
|
||||
@ -438,6 +438,42 @@ func TestServiceTerminationGuarantee(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSingletonServiceRetrieval tests that singleton services can be retrieved.
|
||||
func TestSingletonServiceRetrieval(t *testing.T) {
|
||||
// Create a simple stack and register two service types
|
||||
stack, err := New(testNodeConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create protocol stack: %v", err)
|
||||
}
|
||||
if err := stack.Register("noop", func(*ServiceContext) (Service, error) { return new(NoopService), nil }); err != nil {
|
||||
t.Fatalf("noop service registration failed: %v", err)
|
||||
}
|
||||
if err := stack.Register("instrumented", func(*ServiceContext) (Service, error) { return new(InstrumentedService), nil }); err != nil {
|
||||
t.Fatalf("instrumented service registration failed: %v", err)
|
||||
}
|
||||
// Make sure none of the services can be retrieved until started
|
||||
var noopServ *NoopService
|
||||
if id, err := stack.SingletonService(&noopServ); id != "" || err != ErrServiceUnknown {
|
||||
t.Fatalf("noop service retrieval mismatch: have %v/%v, want %v/%v", id, err, "", ErrServiceUnknown)
|
||||
}
|
||||
var instServ *InstrumentedService
|
||||
if id, err := stack.SingletonService(&instServ); id != "" || err != ErrServiceUnknown {
|
||||
t.Fatalf("instrumented service retrieval mismatch: have %v/%v, want %v/%v", id, err, "", ErrServiceUnknown)
|
||||
}
|
||||
// Start the stack and ensure everything is retrievable now
|
||||
if err := stack.Start(); err != nil {
|
||||
t.Fatalf("failed to start stack: %v", err)
|
||||
}
|
||||
defer stack.Stop()
|
||||
|
||||
if id, err := stack.SingletonService(&noopServ); id != "noop" || err != nil {
|
||||
t.Fatalf("noop service retrieval mismatch: have %v/%v, want %v/%v", id, err, "noop", nil)
|
||||
}
|
||||
if id, err := stack.SingletonService(&instServ); id != "instrumented" || err != nil {
|
||||
t.Fatalf("instrumented service retrieval mismatch: have %v/%v, want %v/%v", id, err, "instrumented", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that all protocols defined by individual services get launched.
|
||||
func TestProtocolGather(t *testing.T) {
|
||||
stack, err := New(testNodeConfig)
|
||||
|
@ -18,6 +18,7 @@ package node
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
@ -28,7 +29,8 @@ import (
|
||||
// the protocol stack, that is passed to all constructors to be optionally used;
|
||||
// as well as utility methods to operate on the service environment.
|
||||
type ServiceContext struct {
|
||||
dataDir string // Data directory for protocol persistence
|
||||
datadir string // Data directory for protocol persistence
|
||||
services map[string]Service // Index of the already constructed services
|
||||
EventMux *event.TypeMux // Event multiplexer used for decoupled notifications
|
||||
}
|
||||
|
||||
@ -36,10 +38,30 @@ type ServiceContext struct {
|
||||
// previous can be found) from within the node's data directory. If the node is
|
||||
// an ephemeral one, a memory database is returned.
|
||||
func (ctx *ServiceContext) Database(name string, cache int) (ethdb.Database, error) {
|
||||
if ctx.dataDir == "" {
|
||||
if ctx.datadir == "" {
|
||||
return ethdb.NewMemDatabase()
|
||||
}
|
||||
return ethdb.NewLDBDatabase(filepath.Join(ctx.dataDir, name), cache)
|
||||
return ethdb.NewLDBDatabase(filepath.Join(ctx.datadir, name), cache)
|
||||
}
|
||||
|
||||
// Service retrieves an already constructed service registered under a given id.
|
||||
func (ctx *ServiceContext) Service(id string) Service {
|
||||
return ctx.services[id]
|
||||
}
|
||||
|
||||
// SingletonService retrieves an already constructed service using a specific type
|
||||
// implementing the Service interface. This is a utility function for scenarios
|
||||
// where it is known that only one instance of a given service type is running,
|
||||
// allowing to access services without needing to know their specific id with
|
||||
// which they were registered.
|
||||
func (ctx *ServiceContext) SingletonService(service interface{}) (string, error) {
|
||||
for id, running := range ctx.services {
|
||||
if reflect.TypeOf(running) == reflect.ValueOf(service).Elem().Type() {
|
||||
reflect.ValueOf(service).Elem().Set(reflect.ValueOf(running))
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
return "", ErrServiceUnknown
|
||||
}
|
||||
|
||||
// ServiceConstructor is the function signature of the constructors needed to be
|
||||
@ -58,8 +80,9 @@ type Service interface {
|
||||
// Protocol retrieves the P2P protocols the service wishes to start.
|
||||
Protocols() []p2p.Protocol
|
||||
|
||||
// Start spawns any goroutines required by the service.
|
||||
Start() error
|
||||
// Start is called after all services have been constructed and the networking
|
||||
// layer was also initialized to spawn any goroutines required by the service.
|
||||
Start(server *p2p.Server) error
|
||||
|
||||
// Stop terminates all goroutines belonging to the service, blocking until they
|
||||
// are all terminated.
|
||||
|
@ -17,14 +17,16 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests that service context methods work properly.
|
||||
func TestServiceContext(t *testing.T) {
|
||||
// Tests that databases are correctly created persistent or ephemeral based on
|
||||
// the configured service context.
|
||||
func TestContextDatabases(t *testing.T) {
|
||||
// Create a temporary folder and ensure no database is contained within
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
@ -36,7 +38,7 @@ func TestServiceContext(t *testing.T) {
|
||||
t.Fatalf("non-created database already exists")
|
||||
}
|
||||
// Request the opening/creation of a database and ensure it persists to disk
|
||||
ctx := &ServiceContext{dataDir: dir}
|
||||
ctx := &ServiceContext{datadir: dir}
|
||||
db, err := ctx.Database("persistent", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open persistent database: %v", err)
|
||||
@ -47,7 +49,7 @@ func TestServiceContext(t *testing.T) {
|
||||
t.Fatalf("persistent database doesn't exists: %v", err)
|
||||
}
|
||||
// Request th opening/creation of an ephemeral database and ensure it's not persisted
|
||||
ctx = &ServiceContext{dataDir: ""}
|
||||
ctx = &ServiceContext{datadir: ""}
|
||||
db, err = ctx.Database("ephemeral", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open ephemeral database: %v", err)
|
||||
@ -58,3 +60,47 @@ func TestServiceContext(t *testing.T) {
|
||||
t.Fatalf("ephemeral database exists")
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that already constructed services can be retrieves by later ones.
|
||||
func TestContextServices(t *testing.T) {
|
||||
stack, err := New(testNodeConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create protocol stack: %v", err)
|
||||
}
|
||||
// Define a set of services, constructed before/after a verifier
|
||||
formers := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"}
|
||||
latters := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
|
||||
verifier := func(ctx *ServiceContext) (Service, error) {
|
||||
for i, id := range formers {
|
||||
if ctx.Service(id) == nil {
|
||||
return nil, fmt.Errorf("former %d: service not found", i)
|
||||
}
|
||||
}
|
||||
for i, id := range latters {
|
||||
if ctx.Service(id) != nil {
|
||||
return nil, fmt.Errorf("latters %d: service found", i)
|
||||
}
|
||||
}
|
||||
return new(NoopService), nil
|
||||
}
|
||||
// Register the collection of services
|
||||
for i, id := range formers {
|
||||
if err := stack.Register(id, NewNoopService); err != nil {
|
||||
t.Fatalf("former #%d: failed to register service: %v", i, err)
|
||||
}
|
||||
}
|
||||
if err := stack.Register("verifier", verifier); err != nil {
|
||||
t.Fatalf("failed to register service verifier: %v", err)
|
||||
}
|
||||
for i, id := range latters {
|
||||
if err := stack.Register(id, NewNoopService); err != nil {
|
||||
t.Fatalf("latter #%d: failed to register service: %v", i, err)
|
||||
}
|
||||
}
|
||||
// Start the protocol stack and ensure services are constructed in order
|
||||
if err := stack.Start(); err != nil {
|
||||
t.Fatalf("failed to start stack: %v", err)
|
||||
}
|
||||
defer stack.Stop()
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
@ -80,19 +82,24 @@ type adminhandler func(*adminApi, *shared.Request) (interface{}, error)
|
||||
// admin api provider
|
||||
type adminApi struct {
|
||||
xeth *xeth.XEth
|
||||
stack *node.Node
|
||||
ethereum *eth.Ethereum
|
||||
codec codec.Codec
|
||||
coder codec.ApiCoder
|
||||
}
|
||||
|
||||
// create a new admin api instance
|
||||
func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *adminApi {
|
||||
return &adminApi{
|
||||
func NewAdminApi(xeth *xeth.XEth, stack *node.Node, codec codec.Codec) *adminApi {
|
||||
api := &adminApi{
|
||||
xeth: xeth,
|
||||
ethereum: ethereum,
|
||||
stack: stack,
|
||||
codec: codec,
|
||||
coder: codec.New(nil),
|
||||
}
|
||||
if stack != nil {
|
||||
stack.SingletonService(&api.ethereum)
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
// collection with supported methods
|
||||
@ -128,24 +135,24 @@ func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
err := self.ethereum.AddPeer(args.Url)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
node, err := discover.ParseNode(args.Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid node URL: %v", err)
|
||||
}
|
||||
return false, err
|
||||
self.stack.Server().AddPeer(node)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) Peers(req *shared.Request) (interface{}, error) {
|
||||
return self.ethereum.Network().PeersInfo(), nil
|
||||
return self.stack.Server().PeersInfo(), nil
|
||||
}
|
||||
|
||||
func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) {
|
||||
return self.ethereum.Network().NodeInfo(), nil
|
||||
return self.stack.Server().NodeInfo(), nil
|
||||
}
|
||||
|
||||
func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) {
|
||||
return self.ethereum.DataDir, nil
|
||||
return self.stack.DataDir(), nil
|
||||
}
|
||||
|
||||
func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
|
||||
@ -253,7 +260,7 @@ func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) {
|
||||
CorsDomain: args.CorsDomain,
|
||||
}
|
||||
|
||||
apis, err := ParseApiString(args.Apis, self.codec, self.xeth, self.ethereum)
|
||||
apis, err := ParseApiString(args.Apis, self.codec, self.xeth, self.stack)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ func TestCompileSolidity(t *testing.T) {
|
||||
expSource := source
|
||||
|
||||
eth := ð.Ethereum{}
|
||||
xeth := xeth.NewTest(eth, nil)
|
||||
xeth := xeth.NewTest(nil, nil)
|
||||
api := NewEthApi(xeth, eth, codec.JSON)
|
||||
|
||||
var rpcRequest shared.Request
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
@ -154,7 +155,7 @@ var (
|
||||
)
|
||||
|
||||
// Parse a comma separated API string to individual api's
|
||||
func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.Ethereum) ([]shared.EthereumApi, error) {
|
||||
func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, stack *node.Node) ([]shared.EthereumApi, error) {
|
||||
if len(strings.TrimSpace(apistr)) == 0 {
|
||||
return nil, fmt.Errorf("Empty apistr provided")
|
||||
}
|
||||
@ -162,10 +163,16 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
|
||||
names := strings.Split(apistr, ",")
|
||||
apis := make([]shared.EthereumApi, len(names))
|
||||
|
||||
var eth *eth.Ethereum
|
||||
if stack != nil {
|
||||
if _, err := stack.SingletonService(ð); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for i, name := range names {
|
||||
switch strings.ToLower(strings.TrimSpace(name)) {
|
||||
case shared.AdminApiName:
|
||||
apis[i] = NewAdminApi(xeth, eth, codec)
|
||||
apis[i] = NewAdminApi(xeth, stack, codec)
|
||||
case shared.DebugApiName:
|
||||
apis[i] = NewDebugApi(xeth, eth, codec)
|
||||
case shared.DbApiName:
|
||||
@ -188,7 +195,6 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
|
||||
return nil, fmt.Errorf("Unknown API '%s'", name)
|
||||
}
|
||||
}
|
||||
|
||||
return apis, nil
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@ -165,15 +167,6 @@ func runBlockTest(test *BlockTest) error {
|
||||
ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"), crypto.StandardScryptN, crypto.StandardScryptP)
|
||||
am := accounts.NewManager(ks)
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
cfg := ð.Config{
|
||||
DataDir: common.DefaultDataDir(),
|
||||
Verbosity: 5,
|
||||
Etherbase: common.Address{},
|
||||
AccountManager: am,
|
||||
NewDB: func(path string) (ethdb.Database, error) { return db, nil },
|
||||
}
|
||||
|
||||
cfg.GenesisBlock = test.Genesis
|
||||
|
||||
// import pre accounts & construct test genesis block & state root
|
||||
_, err := test.InsertPreState(db, am)
|
||||
@ -181,16 +174,16 @@ func runBlockTest(test *BlockTest) error {
|
||||
return fmt.Errorf("InsertPreState: %v", err)
|
||||
}
|
||||
|
||||
ethereum, err := eth.New(cfg)
|
||||
cfg := ð.Config{
|
||||
TestGenesisState: db,
|
||||
TestGenesisBlock: test.Genesis,
|
||||
Etherbase: common.Address{},
|
||||
AccountManager: am,
|
||||
}
|
||||
ethereum, err := eth.New(&node.ServiceContext{EventMux: new(event.TypeMux)}, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ethereum.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cm := ethereum.BlockChain()
|
||||
validBlocks, err := test.TryBlocksInsert(cm)
|
||||
if err != nil {
|
||||
|
@ -37,7 +37,7 @@ func startTestPeer() *testPeer {
|
||||
|
||||
// Create a whisper client and connect with it to the tester peer
|
||||
client := New()
|
||||
client.Start()
|
||||
client.Start(nil)
|
||||
|
||||
termed := make(chan struct{})
|
||||
go func() {
|
||||
|
@ -98,9 +98,9 @@ func New() *Whisper {
|
||||
return whisper
|
||||
}
|
||||
|
||||
// Protocol returns the whisper sub-protocol handler for this particular client.
|
||||
func (self *Whisper) Protocol() p2p.Protocol {
|
||||
return self.protocol
|
||||
// Protocols returns the whisper sub-protocols ran by this particular client.
|
||||
func (self *Whisper) Protocols() []p2p.Protocol {
|
||||
return []p2p.Protocol{self.protocol}
|
||||
}
|
||||
|
||||
// Version returns the whisper sub-protocols version number.
|
||||
@ -156,14 +156,20 @@ func (self *Whisper) Send(envelope *Envelope) error {
|
||||
return self.add(envelope)
|
||||
}
|
||||
|
||||
func (self *Whisper) Start() {
|
||||
// Start implements node.Service, starting the background data propagation thread
|
||||
// of the Whisper protocol.
|
||||
func (self *Whisper) Start(*p2p.Server) error {
|
||||
glog.V(logger.Info).Infoln("Whisper started")
|
||||
go self.update()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Whisper) Stop() {
|
||||
// Stop implements node.Service, stopping the background data propagation thread
|
||||
// of the Whisper protocol.
|
||||
func (self *Whisper) Stop() error {
|
||||
close(self.quit)
|
||||
glog.V(logger.Info).Infoln("Whisper stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Messages retrieves all the currently pooled messages matching a filter id.
|
||||
|
@ -33,7 +33,7 @@ func startTestCluster(n int) []*Whisper {
|
||||
whispers := make([]*Whisper, n)
|
||||
for i := 0; i < n; i++ {
|
||||
whispers[i] = New()
|
||||
whispers[i].Start()
|
||||
whispers[i].Start(nil)
|
||||
}
|
||||
// Wire all the peers to the root one
|
||||
for i := 1; i < n; i++ {
|
||||
|
@ -19,6 +19,7 @@ package xeth
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
)
|
||||
|
||||
type State struct {
|
||||
@ -45,8 +46,7 @@ func (self *State) SafeGet(addr string) *Object {
|
||||
func (self *State) safeGet(addr string) *state.StateObject {
|
||||
object := self.state.GetStateObject(common.HexToAddress(addr))
|
||||
if object == nil {
|
||||
object = state.NewStateObject(common.HexToAddress(addr), self.xeth.backend.ChainDb())
|
||||
object = state.NewStateObject(common.HexToAddress(addr), self.xeth.backend.Service("eth").(*eth.Ethereum).ChainDb())
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
135
xeth/xeth.go
135
xeth/xeth.go
@ -40,7 +40,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -77,7 +79,7 @@ type XEth struct {
|
||||
transactMu sync.Mutex
|
||||
|
||||
// read-only fields
|
||||
backend *eth.Ethereum
|
||||
backend *node.Node
|
||||
frontend Frontend
|
||||
agent *miner.RemoteAgent
|
||||
gpo *eth.GasPriceOracle
|
||||
@ -86,19 +88,26 @@ type XEth struct {
|
||||
filterManager *filters.FilterSystem
|
||||
}
|
||||
|
||||
func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth {
|
||||
return &XEth{backend: eth, frontend: frontend}
|
||||
func NewTest(stack *node.Node, frontend Frontend) *XEth {
|
||||
return &XEth{backend: stack, frontend: frontend}
|
||||
}
|
||||
|
||||
// New creates an XEth that uses the given frontend.
|
||||
// If a nil Frontend is provided, a default frontend which
|
||||
// confirms all transactions will be used.
|
||||
func New(ethereum *eth.Ethereum, frontend Frontend) *XEth {
|
||||
func New(stack *node.Node, frontend Frontend) *XEth {
|
||||
var (
|
||||
ethereum *eth.Ethereum
|
||||
whisper *whisper.Whisper
|
||||
)
|
||||
stack.SingletonService(ðereum)
|
||||
stack.SingletonService(&whisper)
|
||||
|
||||
xeth := &XEth{
|
||||
backend: ethereum,
|
||||
backend: stack,
|
||||
frontend: frontend,
|
||||
quit: make(chan struct{}),
|
||||
filterManager: filters.NewFilterSystem(ethereum.EventMux()),
|
||||
filterManager: filters.NewFilterSystem(stack.EventMux()),
|
||||
logQueue: make(map[int]*logQueue),
|
||||
blockQueue: make(map[int]*hashQueue),
|
||||
transactionQueue: make(map[int]*hashQueue),
|
||||
@ -106,19 +115,35 @@ func New(ethereum *eth.Ethereum, frontend Frontend) *XEth {
|
||||
agent: miner.NewRemoteAgent(),
|
||||
gpo: eth.NewGasPriceOracle(ethereum),
|
||||
}
|
||||
if ethereum.Whisper() != nil {
|
||||
xeth.whisper = NewWhisper(ethereum.Whisper())
|
||||
if whisper != nil {
|
||||
xeth.whisper = NewWhisper(whisper)
|
||||
}
|
||||
ethereum.Miner().Register(xeth.agent)
|
||||
if frontend == nil {
|
||||
xeth.frontend = dummyFrontend{}
|
||||
}
|
||||
state, _ := xeth.backend.BlockChain().State()
|
||||
state, _ := ethereum.BlockChain().State()
|
||||
xeth.state = NewState(xeth, state)
|
||||
go xeth.start()
|
||||
return xeth
|
||||
}
|
||||
|
||||
func (self *XEth) EthereumService() *eth.Ethereum {
|
||||
var ethereum *eth.Ethereum
|
||||
if _, err := self.backend.SingletonService(ðereum); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ethereum
|
||||
}
|
||||
|
||||
func (self *XEth) WhisperService() *whisper.Whisper {
|
||||
var whisper *whisper.Whisper
|
||||
if _, err := self.backend.SingletonService(&whisper); err != nil {
|
||||
return nil
|
||||
}
|
||||
return whisper
|
||||
}
|
||||
|
||||
func (self *XEth) start() {
|
||||
timer := time.NewTicker(2 * time.Second)
|
||||
defer timer.Stop()
|
||||
@ -172,7 +197,7 @@ done:
|
||||
func (self *XEth) Stop() {
|
||||
close(self.quit)
|
||||
self.filterManager.Stop()
|
||||
self.backend.Miner().Unregister(self.agent)
|
||||
self.EthereumService().Miner().Unregister(self.agent)
|
||||
}
|
||||
|
||||
func cAddress(a []string) []common.Address {
|
||||
@ -207,21 +232,20 @@ func (self *XEth) AtStateNum(num int64) *XEth {
|
||||
var err error
|
||||
switch num {
|
||||
case -2:
|
||||
st = self.backend.Miner().PendingState().Copy()
|
||||
st = self.EthereumService().Miner().PendingState().Copy()
|
||||
default:
|
||||
if block := self.getBlockByHeight(num); block != nil {
|
||||
st, err = state.New(block.Root(), self.backend.ChainDb())
|
||||
st, err = state.New(block.Root(), self.EthereumService().ChainDb())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
st, err = state.New(self.backend.BlockChain().GetBlockByNumber(0).Root(), self.backend.ChainDb())
|
||||
st, err = state.New(self.EthereumService().BlockChain().GetBlockByNumber(0).Root(), self.EthereumService().ChainDb())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self.WithState(st)
|
||||
}
|
||||
|
||||
@ -270,7 +294,7 @@ func (self *XEth) UpdateState() (wait chan *big.Int) {
|
||||
wait <- n
|
||||
n = nil
|
||||
}
|
||||
statedb, err := state.New(event.Block.Root(), self.backend.ChainDb())
|
||||
statedb, err := state.New(event.Block.Root(), self.EthereumService().ChainDb())
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infoln("Could not create new state: %v", err)
|
||||
return
|
||||
@ -294,7 +318,7 @@ func (self *XEth) getBlockByHeight(height int64) *types.Block {
|
||||
|
||||
switch height {
|
||||
case -2:
|
||||
return self.backend.Miner().PendingBlock()
|
||||
return self.EthereumService().Miner().PendingBlock()
|
||||
case -1:
|
||||
return self.CurrentBlock()
|
||||
default:
|
||||
@ -305,28 +329,29 @@ func (self *XEth) getBlockByHeight(height int64) *types.Block {
|
||||
num = uint64(height)
|
||||
}
|
||||
|
||||
return self.backend.BlockChain().GetBlockByNumber(num)
|
||||
return self.EthereumService().BlockChain().GetBlockByNumber(num)
|
||||
}
|
||||
|
||||
func (self *XEth) BlockByHash(strHash string) *Block {
|
||||
hash := common.HexToHash(strHash)
|
||||
block := self.backend.BlockChain().GetBlock(hash)
|
||||
block := self.EthereumService().BlockChain().GetBlock(hash)
|
||||
|
||||
return NewBlock(block)
|
||||
}
|
||||
|
||||
func (self *XEth) EthBlockByHash(strHash string) *types.Block {
|
||||
hash := common.HexToHash(strHash)
|
||||
block := self.backend.BlockChain().GetBlock(hash)
|
||||
block := self.EthereumService().BlockChain().GetBlock(hash)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (self *XEth) EthTransactionByHash(hash string) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||
if tx, hash, number, index := core.GetTransaction(self.backend.ChainDb(), common.HexToHash(hash)); tx != nil {
|
||||
ethereum := self.EthereumService()
|
||||
if tx, hash, number, index := core.GetTransaction(ethereum.ChainDb(), common.HexToHash(hash)); tx != nil {
|
||||
return tx, hash, number, index
|
||||
}
|
||||
return self.backend.TxPool().GetTransaction(common.HexToHash(hash)), common.Hash{}, 0, 0
|
||||
return ethereum.TxPool().GetTransaction(common.HexToHash(hash)), common.Hash{}, 0, 0
|
||||
}
|
||||
|
||||
func (self *XEth) BlockByNumber(num int64) *Block {
|
||||
@ -338,23 +363,23 @@ func (self *XEth) EthBlockByNumber(num int64) *types.Block {
|
||||
}
|
||||
|
||||
func (self *XEth) Td(hash common.Hash) *big.Int {
|
||||
return self.backend.BlockChain().GetTd(hash)
|
||||
return self.EthereumService().BlockChain().GetTd(hash)
|
||||
}
|
||||
|
||||
func (self *XEth) CurrentBlock() *types.Block {
|
||||
return self.backend.BlockChain().CurrentBlock()
|
||||
return self.EthereumService().BlockChain().CurrentBlock()
|
||||
}
|
||||
|
||||
func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts {
|
||||
return core.GetBlockReceipts(self.backend.ChainDb(), bhash)
|
||||
return core.GetBlockReceipts(self.EthereumService().ChainDb(), bhash)
|
||||
}
|
||||
|
||||
func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
||||
return core.GetReceipt(self.backend.ChainDb(), txhash)
|
||||
return core.GetReceipt(self.EthereumService().ChainDb(), txhash)
|
||||
}
|
||||
|
||||
func (self *XEth) GasLimit() *big.Int {
|
||||
return self.backend.BlockChain().GasLimit()
|
||||
return self.EthereumService().BlockChain().GasLimit()
|
||||
}
|
||||
|
||||
func (self *XEth) Block(v interface{}) *Block {
|
||||
@ -371,7 +396,7 @@ func (self *XEth) Block(v interface{}) *Block {
|
||||
|
||||
func (self *XEth) Accounts() []string {
|
||||
// TODO: check err?
|
||||
accounts, _ := self.backend.AccountManager().Accounts()
|
||||
accounts, _ := self.EthereumService().AccountManager().Accounts()
|
||||
accountAddresses := make([]string, len(accounts))
|
||||
for i, ac := range accounts {
|
||||
accountAddresses[i] = ac.Address.Hex()
|
||||
@ -382,73 +407,73 @@ func (self *XEth) Accounts() []string {
|
||||
// accessor for solidity compiler.
|
||||
// memoized if available, retried on-demand if not
|
||||
func (self *XEth) Solc() (*compiler.Solidity, error) {
|
||||
return self.backend.Solc()
|
||||
return self.EthereumService().Solc()
|
||||
}
|
||||
|
||||
// set in js console via admin interface or wrapper from cli flags
|
||||
func (self *XEth) SetSolc(solcPath string) (*compiler.Solidity, error) {
|
||||
self.backend.SetSolc(solcPath)
|
||||
self.EthereumService().SetSolc(solcPath)
|
||||
return self.Solc()
|
||||
}
|
||||
|
||||
// store DApp value in extra database
|
||||
func (self *XEth) DbPut(key, val []byte) bool {
|
||||
self.backend.DappDb().Put(append(dappStorePre, key...), val)
|
||||
self.EthereumService().DappDb().Put(append(dappStorePre, key...), val)
|
||||
return true
|
||||
}
|
||||
|
||||
// retrieve DApp value from extra database
|
||||
func (self *XEth) DbGet(key []byte) ([]byte, error) {
|
||||
val, err := self.backend.DappDb().Get(append(dappStorePre, key...))
|
||||
val, err := self.EthereumService().DappDb().Get(append(dappStorePre, key...))
|
||||
return val, err
|
||||
}
|
||||
|
||||
func (self *XEth) PeerCount() int {
|
||||
return self.backend.PeerCount()
|
||||
return self.backend.Server().PeerCount()
|
||||
}
|
||||
|
||||
func (self *XEth) IsMining() bool {
|
||||
return self.backend.IsMining()
|
||||
return self.EthereumService().IsMining()
|
||||
}
|
||||
|
||||
func (self *XEth) HashRate() int64 {
|
||||
return self.backend.Miner().HashRate()
|
||||
return self.EthereumService().Miner().HashRate()
|
||||
}
|
||||
|
||||
func (self *XEth) EthVersion() string {
|
||||
return fmt.Sprintf("%d", self.backend.EthVersion())
|
||||
return fmt.Sprintf("%d", self.EthereumService().EthVersion())
|
||||
}
|
||||
|
||||
func (self *XEth) NetworkVersion() string {
|
||||
return fmt.Sprintf("%d", self.backend.NetVersion())
|
||||
return fmt.Sprintf("%d", self.EthereumService().NetVersion())
|
||||
}
|
||||
|
||||
func (self *XEth) WhisperVersion() string {
|
||||
return fmt.Sprintf("%d", self.backend.ShhVersion())
|
||||
return fmt.Sprintf("%d", self.WhisperService().Version())
|
||||
}
|
||||
|
||||
func (self *XEth) ClientVersion() string {
|
||||
return self.backend.ClientVersion()
|
||||
return self.backend.Server().Name
|
||||
}
|
||||
|
||||
func (self *XEth) SetMining(shouldmine bool, threads int) bool {
|
||||
ismining := self.backend.IsMining()
|
||||
ismining := self.EthereumService().IsMining()
|
||||
if shouldmine && !ismining {
|
||||
err := self.backend.StartMining(threads, "")
|
||||
err := self.EthereumService().StartMining(threads, "")
|
||||
return err == nil
|
||||
}
|
||||
if ismining && !shouldmine {
|
||||
self.backend.StopMining()
|
||||
self.EthereumService().StopMining()
|
||||
}
|
||||
return self.backend.IsMining()
|
||||
return self.EthereumService().IsMining()
|
||||
}
|
||||
|
||||
func (self *XEth) IsListening() bool {
|
||||
return self.backend.IsListening()
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *XEth) Coinbase() string {
|
||||
eb, err := self.backend.Etherbase()
|
||||
eb, err := self.EthereumService().Etherbase()
|
||||
if err != nil {
|
||||
return "0x0"
|
||||
}
|
||||
@ -514,7 +539,7 @@ func (self *XEth) NewLogFilter(earliest, latest int64, skip, max int, address []
|
||||
self.logMu.Lock()
|
||||
defer self.logMu.Unlock()
|
||||
|
||||
filter := filters.New(self.backend.ChainDb())
|
||||
filter := filters.New(self.EthereumService().ChainDb())
|
||||
id := self.filterManager.Add(filter)
|
||||
self.logQueue[id] = &logQueue{timeout: time.Now()}
|
||||
|
||||
@ -538,7 +563,7 @@ func (self *XEth) NewTransactionFilter() int {
|
||||
self.transactionMu.Lock()
|
||||
defer self.transactionMu.Unlock()
|
||||
|
||||
filter := filters.New(self.backend.ChainDb())
|
||||
filter := filters.New(self.EthereumService().ChainDb())
|
||||
id := self.filterManager.Add(filter)
|
||||
self.transactionQueue[id] = &hashQueue{timeout: time.Now()}
|
||||
|
||||
@ -557,7 +582,7 @@ func (self *XEth) NewBlockFilter() int {
|
||||
self.blockMu.Lock()
|
||||
defer self.blockMu.Unlock()
|
||||
|
||||
filter := filters.New(self.backend.ChainDb())
|
||||
filter := filters.New(self.EthereumService().ChainDb())
|
||||
id := self.filterManager.Add(filter)
|
||||
self.blockQueue[id] = &hashQueue{timeout: time.Now()}
|
||||
|
||||
@ -624,7 +649,7 @@ func (self *XEth) Logs(id int) vm.Logs {
|
||||
}
|
||||
|
||||
func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) vm.Logs {
|
||||
filter := filters.New(self.backend.ChainDb())
|
||||
filter := filters.New(self.EthereumService().ChainDb())
|
||||
filter.SetBeginBlock(earliest)
|
||||
filter.SetEndBlock(latest)
|
||||
filter.SetAddresses(cAddress(address))
|
||||
@ -775,7 +800,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = self.backend.TxPool().Add(tx)
|
||||
err = self.EthereumService().TxPool().Add(tx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -799,7 +824,7 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
|
||||
statedb := self.State().State().Copy()
|
||||
var from *state.StateObject
|
||||
if len(fromStr) == 0 {
|
||||
accounts, err := self.backend.AccountManager().Accounts()
|
||||
accounts, err := self.EthereumService().AccountManager().Accounts()
|
||||
if err != nil || len(accounts) == 0 {
|
||||
from = statedb.GetOrNewStateObject(common.Address{})
|
||||
} else {
|
||||
@ -832,7 +857,7 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
|
||||
}
|
||||
|
||||
header := self.CurrentBlock().Header()
|
||||
vmenv := core.NewEnv(statedb, self.backend.BlockChain(), msg, header)
|
||||
vmenv := core.NewEnv(statedb, self.EthereumService().BlockChain(), msg, header)
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
||||
return common.ToHex(res), gas.String(), err
|
||||
@ -843,7 +868,7 @@ func (self *XEth) ConfirmTransaction(tx string) bool {
|
||||
}
|
||||
|
||||
func (self *XEth) doSign(from common.Address, hash common.Hash, didUnlock bool) ([]byte, error) {
|
||||
sig, err := self.backend.AccountManager().Sign(accounts.Account{Address: from}, hash.Bytes())
|
||||
sig, err := self.EthereumService().AccountManager().Sign(accounts.Account{Address: from}, hash.Bytes())
|
||||
if err == accounts.ErrLocked {
|
||||
if didUnlock {
|
||||
return nil, fmt.Errorf("signer account still locked after successful unlock")
|
||||
@ -915,7 +940,7 @@ func (self *XEth) SignTransaction(fromStr, toStr, nonceStr, valueStr, gasStr, ga
|
||||
if len(nonceStr) != 0 {
|
||||
nonce = common.Big(nonceStr).Uint64()
|
||||
} else {
|
||||
state := self.backend.TxPool().State()
|
||||
state := self.EthereumService().TxPool().State()
|
||||
nonce = state.GetNonce(from)
|
||||
}
|
||||
var tx *types.Transaction
|
||||
@ -1003,7 +1028,7 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
|
||||
if len(nonceStr) != 0 {
|
||||
nonce = common.Big(nonceStr).Uint64()
|
||||
} else {
|
||||
state := self.backend.TxPool().State()
|
||||
state := self.EthereumService().TxPool().State()
|
||||
nonce = state.GetNonce(from)
|
||||
}
|
||||
var tx *types.Transaction
|
||||
@ -1017,7 +1042,7 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = self.backend.TxPool().Add(signed); err != nil {
|
||||
if err = self.EthereumService().TxPool().Add(signed); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user