Merge pull request #1970 from karalabe/customizable-protocol-stacks

Customizable protocol stacks
release/1.4
Jeffrey Wilcke 9 years ago
commit 7dde2b902c
  1. 135
      cmd/geth/blocktestcmd.go
  2. 33
      cmd/geth/js.go
  3. 128
      cmd/geth/js_test.go
  4. 307
      cmd/geth/main.go
  5. 179
      cmd/gethrpctest/main.go
  6. 41
      cmd/utils/bootnodes.go
  7. 11
      cmd/utils/cmd.go
  8. 409
      cmd/utils/flags.go
  9. 12
      common/natspec/natspec_e2e_test.go
  10. 28
      common/types.go
  11. 2
      core/block_validator_test.go
  12. 6
      core/blockchain.go
  13. 8
      core/blockchain_test.go
  14. 2
      core/chain_makers.go
  15. 13
      core/default_genesis.go
  16. 114
      core/genesis.go
  17. 345
      eth/backend.go
  18. 171
      node/config.go
  19. 120
      node/config_test.go
  20. 45
      node/errors.go
  21. 266
      node/node.go
  22. 87
      node/node_example_test.go
  23. 496
      node/node_test.go
  24. 80
      node/service.go
  25. 97
      node/service_test.go
  26. 117
      node/utils_test.go
  27. 7
      p2p/discover/table.go
  28. 8
      p2p/discover/table_test.go
  29. 16
      p2p/discover/udp.go
  30. 2
      p2p/discover/udp_test.go
  31. 37
      rpc/api/admin.go
  32. 2
      rpc/api/api_test.go
  33. 12
      rpc/api/utils.go
  34. 23
      tests/block_test_util.go
  35. 2
      whisper/peer_test.go
  36. 16
      whisper/whisper.go
  37. 2
      whisper/whisper_test.go
  38. 3
      xeth/state.go
  39. 135
      xeth/xeth.go

@ -1,135 +0,0 @@
// 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 main
import (
"fmt"
"os"
"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/tests"
)
var blocktestCommand = cli.Command{
Action: runBlockTest,
Name: "blocktest",
Usage: `loads a block test file`,
Description: `
The first argument should be a block test file.
The second argument is the name of a block test from the file.
The block test will be loaded into an in-memory database.
If loading succeeds, the RPC server is started. Clients will
be able to interact with the chain defined by the test.
`,
}
func runBlockTest(ctx *cli.Context) {
var (
file, testname string
rpc bool
)
args := ctx.Args()
switch {
case len(args) == 1:
file = args[0]
case len(args) == 2:
file, testname = args[0], args[1]
case len(args) == 3:
file, testname = args[0], args[1]
rpc = true
default:
utils.Fatalf(`Usage: ethereum blocktest <path-to-test-file> [ <test-name> [ "rpc" ] ]`)
}
bt, err := tests.LoadBlockTests(file)
if err != nil {
utils.Fatalf("%v", err)
}
// run all tests if no test name is specified
if testname == "" {
ecode := 0
for name, test := range bt {
fmt.Printf("----------------- Running Block Test %q\n", name)
ethereum, err := runOneBlockTest(ctx, test)
if err != nil {
fmt.Println(err)
fmt.Println("FAIL")
ecode = 1
}
if ethereum != nil {
ethereum.Stop()
ethereum.WaitForShutdown()
}
}
os.Exit(ecode)
return
}
// otherwise, run the given test
test, ok := bt[testname]
if !ok {
utils.Fatalf("Test file does not contain test named %q", testname)
}
ethereum, err := runOneBlockTest(ctx, test)
if err != nil {
utils.Fatalf("%v", err)
}
if rpc {
fmt.Println("Block Test post state validated, starting RPC interface.")
startEth(ctx, ethereum)
utils.StartRPC(ethereum, ctx)
ethereum.WaitForShutdown()
}
}
func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) {
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
db, _ := ethdb.NewMemDatabase()
cfg.NewDB = func(path string) (ethdb.Database, error) { return db, nil }
cfg.MaxPeers = 0 // disable network
cfg.Shh = false // disable whisper
cfg.NAT = nil // disable port mapping
ethereum, err := eth.New(cfg)
if err != nil {
return nil, err
}
// import the genesis block
ethereum.ResetWithGenesisBlock(test.Genesis)
// import pre accounts
_, err = test.InsertPreState(db, cfg.AccountManager)
if err != nil {
return ethereum, fmt.Errorf("InsertPreState: %v", err)
}
cm := ethereum.BlockChain()
validBlocks, err := test.TryBlocksInsert(cm)
if err != nil {
return ethereum, fmt.Errorf("Block Test load error: %v", err)
}
newDB, err := cm.State()
if err != nil {
return ethereum, fmt.Errorf("Block Test get state error: %v", err)
}
if err := test.ValidatePostState(newDB); err != nil {
return ethereum, fmt.Errorf("post state validation failed: %v", err)
}
return ethereum, test.ValidateImportedHeaders(cm, validBlocks)
}

@ -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.Service(&ethereum); 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.Service(&ethereum); 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.Service(&ethereum)
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.Service(&ethereum)
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 := &eth.Config{
NodeKey: testNodeKey,
DataDir: tmp,
AccountManager: am,
MaxPeers: 0,
Name: "test",
DocRoot: "/",
SolcPath: testSolcPath,
PowTest: true,
NewDB: func(path string) (ethdb.Database, error) { return db, nil },
ethConf := &eth.Config{
TestGenesisState: db,
AccountManager: accman,
DocRoot: "/",
SolcPath: testSolcPath,
PowTest: true,
}
if config != nil {
config(conf)
config(ethConf)
}
ethereum, err := eth.New(conf)
if err != nil {
t.Fatal("%v", err)
if err := stack.Register(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.Service(&ethereum)
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.Service(&ethereum)
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.Service(&ethereum)
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.Service(&ethereum)
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))

@ -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")
}
arg := ctx.Args().First()
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")
}
// 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)
if err = core.WriteHeadBlockHash(blockDb, block.Hash()); err != nil {
glog.Fatalln("block write err", err)
// Unlock any account specifically requested
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
utils.Fatalf("ethereum service not running: %v", 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)
accman := ethereum.AccountManager()
passwords := utils.MakePasswordList(ctx)
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)
if err != nil {
utils.Fatalf("%v", err)
}
if confirmation {
confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
if err != nil {
utils.Fatalf("%v", err)
}
if auth != confirm {
utils.Fatalf("Passphrases did not match.")
}
// 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]
}
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")
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("Failed to read passphrase: %v", err)
}
if confirmation {
confirm, err := utils.PromptPassword("Repeat passphrase: ", false)
if err != nil {
utils.Fatalf("Failed to read passphrase confirmation: %v", err)
}
if i >= len(passphrases) {
passphrase = passphrases[len(passphrases)-1]
} else {
passphrase = passphrases[i]
if password != confirm {
utils.Fatalf("Passphrases do not match")
}
}
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)
}

@ -0,0 +1,179 @@
// 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/>.
// gethrpctest is a command to run the external RPC tests.
package main
import (
"flag"
"io/ioutil"
"log"
"os"
"os/signal"
"github.com/ethereum/go-ethereum/accounts"
"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/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/tests"
"github.com/ethereum/go-ethereum/whisper"
"github.com/ethereum/go-ethereum/xeth"
)
const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
var (
testFile = flag.String("json", "", "Path to the .json test file to load")
testName = flag.String("test", "", "Name of the test from the .json file to run")
testKey = flag.String("key", defaultTestKey, "Private key of a test account to inject")
)
func main() {
flag.Parse()
// Load the test suite to run the RPC against
tests, err := tests.LoadBlockTests(*testFile)
if err != nil {
log.Fatalf("Failed to load test suite: %v", err)
}
test, found := tests[*testName]
if !found {
log.Fatalf("Requested test (%s) not found within suite", *testName)
}
// Create the protocol stack to run the test with
keydir, err := ioutil.TempDir("", "")
if err != nil {
log.Fatalf("Failed to create temporary keystore directory: %v", err)
}
defer os.RemoveAll(keydir)
stack, err := MakeSystemNode(keydir, *testKey, test)
if err != nil {
log.Fatalf("Failed to assemble test stack: %v", err)
}
if err := stack.Start(); err != nil {
log.Fatalf("Failed to start test node: %v", err)
}
defer stack.Stop()
log.Println("Test node started...")
// Make sure the tests contained within the suite pass
if err := RunTest(stack, test); err != nil {
log.Fatalf("Failed to run the pre-configured test: %v", err)
}
log.Println("Initial test suite passed...")
// Start the RPC interface and wait until terminated
if err := StartRPC(stack); err != nil {
log.Fatalf("Failed to start RPC instarface: %v", err)
}
log.Println("RPC Interface started, accepting requests...")
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
}
// MakeSystemNode configures a protocol stack for the RPC tests based on a given
// keystore path and initial pre-state.
func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node.Node, error) {
// Create a networkless protocol stack
stack, err := node.New(&node.Config{NoDiscovery: true})
if err != nil {
return nil, err
}
// Create the keystore and inject an unlocked account if requested
keystore := crypto.NewKeyStorePassphrase(keydir, crypto.StandardScryptN, crypto.StandardScryptP)
accman := accounts.NewManager(keystore)
if len(privkey) > 0 {
key, err := crypto.HexToECDSA(privkey)
if err != nil {
return nil, err
}
if err := keystore.StoreKey(crypto.NewKeyFromECDSA(key), ""); err != nil {
return nil, err
}
if err := accman.Unlock(crypto.NewKeyFromECDSA(key).Address, ""); err != nil {
return nil, err
}
}
// Initialize and register the Ethereum protocol
db, _ := ethdb.NewMemDatabase()
if _, err := test.InsertPreState(db, accman); err != nil {
return nil, err
}
ethConf := &eth.Config{
TestGenesisState: db,
TestGenesisBlock: test.Genesis,
AccountManager: accman,
}
if err := stack.Register(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(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
return nil, err
}
return stack, nil
}
// 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 {
var ethereum *eth.Ethereum
stack.Service(&ethereum)
blockchain := ethereum.BlockChain()
// Process the blocks and verify the imported headers
blocks, err := test.TryBlocksInsert(blockchain)
if err != nil {
return err
}
if err := test.ValidateImportedHeaders(blockchain, blocks); err != nil {
return err
}
// Retrieve the assembled state and validate it
stateDb, err := blockchain.State()
if err != nil {
return err
}
if err := test.ValidatePostState(stateDb); err != nil {
return err
}
return nil
}
// StartRPC initializes an RPC interface to the given protocol stack.
func StartRPC(stack *node.Node) error {
config := comms.HttpConfig{
ListenAddress: "127.0.0.1",
ListenPort: 8545,
}
xeth := xeth.New(stack, nil)
codec := codec.JSON
apis, err := api.ParseApiString(comms.DefaultHttpRpcApis, codec, xeth, stack)
if err != nil {
return err
}
return comms.StartHttp(config, codec, api.Merge(apis...))
}

@ -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,26 +391,40 @@ var (
}
)
// 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))
if err != nil {
Fatalf("Option %s: %v", NATFlag.Name, err)
// 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
}
return natif
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
return ""
}
// 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
// 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)
@ -413,45 +433,196 @@ func MakeNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
return key
}
// 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
// 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))
if err != nil {
Fatalf("Option %s: %v", NATFlag.Name, err)
}
return natif
}
// 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 ""
}
am := MakeAccountManager(ctx)
etherbase, err := ParamToAddress(ctx.GlobalString(EtherbaseFlag.Name), am)
data, err := ioutil.ReadFile(genesis)
if err != nil {
Fatalf("Failed to load custom genesis file: %v", err)
}
return string(data)
}
// 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
}
// 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 {
Fatalf("Invalid account address or index: '%s'", account)
}
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")
}
// Assemble the entire eth configuration and return
cfg := &eth.Config{
Name: common.MakeName(clientID, version),
DataDir: MustDataDir(ctx),
GenesisFile: ctx.GlobalString(GenesisFileFlag.Name),
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 := &eth.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
}
ethConf.PowTest = true
}
// 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(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(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
Fatalf("Failed to register the Whisper service: %v", err)
}
cfg.PowTest = true
cfg.DevMode = true
glog.V(logger.Info).Infoln("dev mode enabled")
}
return cfg
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,43 @@ 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)
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
return nil, nil, err
}
fe := useragent.NewRemoteFrontend(conn, 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 +825,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(&eth.Config{
DataDir: tmp,
return eth.New(&node.ServiceContext{EventMux: new(event.TypeMux)}, &eth.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,46 +160,80 @@ 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(`{
"nonce":"0x%x",
"gasLimit":"0x%x",
"difficulty":"0x%x",
"alloc": {
"0000000000000000000000000000000000000001": {"balance": "1"},
"0000000000000000000000000000000000000002": {"balance": "1"},
"0000000000000000000000000000000000000003": {"balance": "1"},
"0000000000000000000000000000000000000004": {"balance": "1"},
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
// 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))
}
}`, types.EncodeNonce(nonce), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
return WriteGenesisBlock(chainDb, strings.NewReader(testGenesis))
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",
"alloc": {
"0000000000000000000000000000000000000001": {"balance": "1"},
"0000000000000000000000000000000000000002": {"balance": "1"},
"0000000000000000000000000000000000000003": {"balance": "1"},
"0000000000000000000000000000000000000004": {"balance": "1"},
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
}
}`, 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))
}

@ -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
Mining bool
MinerThreads int
NatSpec bool
AutoDAG bool
PowTest bool
autodagquit chan bool
etherbase common.Address
netVersionId 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.OpenDatabase("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.OpenDatabase("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)
if err != nil {
return nil, err
}
block, err := core.WriteGenesisBlock(chainDb, fr)
// 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
}
glog.V(logger.Info).Infof("Successfully wrote genesis block. New genesis hash = %x\n", block.Hash())
glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash())
}
// 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
}
// Load up a test setup if directly injected
if config.TestGenesisState != nil {
chainDb = config.TestGenesisState
}
// 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

@ -0,0 +1,171 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package node
import (
"crypto/ecdsa"
"encoding/json"
"io/ioutil"
"net"
"os"
"path/filepath"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/nat"
)
var (
datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key
datadirStaticNodes = "static-nodes.json" // Path within the datadir to the static node list
datadirTrustedNodes = "trusted-nodes.json" // Path within the datadir to the trusted node list
datadirNodeDatabase = "nodes" // Path within the datadir to store the node infos
)
// Config represents a small collection of configuration values to fine tune the
// P2P network layer of a protocol stack. These values can be further extended by
// all registered services.
type Config struct {
// DataDir is the file system folder the node should use for any data storage
// requirements. The configured data directory will not be directly shared with
// registered services, instead those can use utility methods to create/access
// databases or flat files. This enables ephemeral nodes which can fully reside
// in memory.
DataDir string
// This field should be a valid secp256k1 private key that will be used for both
// remote peer identification as well as network traffic encryption. If no key
// is configured, the preset one is loaded from the data dir, generating it if
// needed.
PrivateKey *ecdsa.PrivateKey
// Name sets the node name of this server. Use common.MakeName to create a name
// that follows existing conventions.
Name string
// NoDiscovery specifies whether the peer discovery mechanism should be started
// or not. Disabling is usually useful for protocol debugging (manual topology).
NoDiscovery bool
// Bootstrap nodes used to establish connectivity with the rest of the network.
BootstrapNodes []*discover.Node
// Network interface address on which the node should listen for inbound peers.
ListenAddr string
// If set to a non-nil value, the given NAT port mapper is used to make the
// listening port available to the Internet.
NAT nat.Interface
// If Dialer is set to a non-nil value, the given Dialer is used to dial outbound
// peer connections.
Dialer *net.Dialer
// If NoDial is true, the node will not dial any peers.
NoDial bool
// MaxPeers is the maximum number of peers that can be connected. If this is
// set to zero, then only the configured static and trusted peers can connect.
MaxPeers int
// MaxPendingPeers is the maximum number of peers that can be pending in the
// handshake phase, counted separately for inbound and outbound connections.
// Zero defaults to preset values.
MaxPendingPeers int
}
// NodeKey retrieves the currently configured private key of the node, checking
// first any manually set key, falling back to the one found in the configured
// data folder. If no key can be found, a new one is generated.
func (c *Config) NodeKey() *ecdsa.PrivateKey {
// Use any specifically configured key
if c.PrivateKey != nil {
return c.PrivateKey
}
// Generate ephemeral key if no datadir is being used
if c.DataDir == "" {
key, err := crypto.GenerateKey()
if err != nil {
glog.Fatalf("Failed to generate ephemeral node key: %v", err)
}
return key
}
// Fall back to persistent key from the data directory
keyfile := filepath.Join(c.DataDir, datadirPrivateKey)
if key, err := crypto.LoadECDSA(keyfile); err == nil {
return key
}
// No persistent key found, generate and store a new one
key, err := crypto.GenerateKey()
if err != nil {
glog.Fatalf("Failed to generate node key: %v", err)
}
if err := crypto.SaveECDSA(keyfile, key); err != nil {
glog.V(logger.Error).Infof("Failed to persist node key: %v", err)
}
return key
}
// StaticNodes returns a list of node enode URLs configured as static nodes.
func (c *Config) StaticNodes() []*discover.Node {
return c.parsePersistentNodes(datadirStaticNodes)
}
// TrusterNodes returns a list of node enode URLs configured as trusted nodes.
func (c *Config) TrusterNodes() []*discover.Node {
return c.parsePersistentNodes(datadirTrustedNodes)
}
// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
// file from within the data directory.
func (c *Config) parsePersistentNodes(file string) []*discover.Node {
// Short circuit if no node config is present
if c.DataDir == "" {
return nil
}
path := filepath.Join(c.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
}

@ -0,0 +1,120 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package node
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/ethereum/go-ethereum/crypto"
)
// Tests that datadirs can be successfully created, be them manually configured
// ones or automatically generated temporary ones.
func TestDatadirCreation(t *testing.T) {
// Create a temporary data dir and check that it can be used by a node
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("failed to create manual data dir: %v", err)
}
defer os.RemoveAll(dir)
if _, err := New(&Config{DataDir: dir}); err != nil {
t.Fatalf("failed to create stack with existing datadir: %v", err)
}
// Generate a long non-existing datadir path and check that it gets created by a node
dir = filepath.Join(dir, "a", "b", "c", "d", "e", "f")
if _, err := New(&Config{DataDir: dir}); err != nil {
t.Fatalf("failed to create stack with creatable datadir: %v", err)
}
if _, err := os.Stat(dir); err != nil {
t.Fatalf("freshly created datadir not accessible: %v", err)
}
// Verify that an impossible datadir fails creation
file, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("failed to create temporary file: %v", err)
}
defer os.Remove(file.Name())
dir = filepath.Join(file.Name(), "invalid/path")
if _, err := New(&Config{DataDir: dir}); err == nil {
t.Fatalf("protocol stack created with an invalid datadir")
}
}
// Tests that node keys can be correctly created, persisted, loaded and/or made
// ephemeral.
func TestNodeKeyPersistency(t *testing.T) {
// Create a temporary folder and make sure no key is present
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("failed to create temporary data directory: %v", err)
}
defer os.RemoveAll(dir)
if _, err := os.Stat(filepath.Join(dir, datadirPrivateKey)); err == nil {
t.Fatalf("non-created node key already exists")
}
// Configure a node with a preset key and ensure it's not persisted
key, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("failed to generate one-shot node key: %v", err)
}
if _, err := New(&Config{DataDir: dir, PrivateKey: key}); err != nil {
t.Fatalf("failed to create empty stack: %v", err)
}
if _, err := os.Stat(filepath.Join(dir, datadirPrivateKey)); err == nil {
t.Fatalf("one-shot node key persisted to data directory")
}
// Configure a node with no preset key and ensure it is persisted this time
if _, err := New(&Config{DataDir: dir}); err != nil {
t.Fatalf("failed to create newly keyed stack: %v", err)
}
if _, err := os.Stat(filepath.Join(dir, datadirPrivateKey)); err != nil {
t.Fatalf("node key not persisted to data directory: %v", err)
}
key, err = crypto.LoadECDSA(filepath.Join(dir, datadirPrivateKey))
if err != nil {
t.Fatalf("failed to load freshly persisted node key: %v", err)
}
blob1, err := ioutil.ReadFile(filepath.Join(dir, datadirPrivateKey))
if err != nil {
t.Fatalf("failed to read freshly persisted node key: %v", err)
}
// Configure a new node and ensure the previously persisted key is loaded
if _, err := New(&Config{DataDir: dir}); err != nil {
t.Fatalf("failed to create previously keyed stack: %v", err)
}
blob2, err := ioutil.ReadFile(filepath.Join(dir, datadirPrivateKey))
if err != nil {
t.Fatalf("failed to read previously persisted node key: %v", err)
}
if bytes.Compare(blob1, blob2) != 0 {
t.Fatalf("persisted node key mismatch: have %x, want %x", blob2, blob1)
}
// Configure ephemeral node and ensure no key is dumped locally
if _, err := New(&Config{DataDir: ""}); err != nil {
t.Fatalf("failed to create ephemeral stack: %v", err)
}
if _, err := os.Stat(filepath.Join(".", datadirPrivateKey)); err == nil {
t.Fatalf("ephemeral node key persisted to disk")
}
}

@ -0,0 +1,45 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package node
import (
"fmt"
"reflect"
)
// DuplicateServiceError is returned during Node startup if a registered service
// constructor returns a service of the same type that was already started.
type DuplicateServiceError struct {
Kind reflect.Type
}
// Error generates a textual representation of the duplicate service error.
func (e *DuplicateServiceError) Error() string {
return fmt.Sprintf("duplicate service: %v", e.Kind)
}
// StopError is returned if a Node fails to stop either any of its registered
// services or itself.
type StopError struct {
Server error
Services map[reflect.Type]error
}
// Error generates a textual representation of the stop error.
func (e *StopError) Error() string {
return fmt.Sprintf("server: %v, services: %v", e.Server, e.Services)
}

@ -0,0 +1,266 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package node represents the Ethereum protocol stack container.
package node
import (
"errors"
"os"
"path/filepath"
"reflect"
"sync"
"syscall"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
)
var (
ErrDatadirUsed = errors.New("datadir already used")
ErrNodeStopped = errors.New("node not started")
ErrNodeRunning = errors.New("node already running")
ErrServiceUnknown = errors.New("unknown service")
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
)
// Node represents a P2P node into which arbitrary (uniquely typed) services might
// be registered.
type Node struct {
datadir string // Path to the currently used data directory
eventmux *event.TypeMux // Event multiplexer used between the services of a stack
serverConfig *p2p.Server // Configuration of the underlying P2P networking layer
server *p2p.Server // Currently running P2P networking layer
serviceFuncs []ServiceConstructor // Service constructors (in dependency order)
services map[reflect.Type]Service // Currently running services
stop chan struct{} // Channel to wait for termination notifications
lock sync.RWMutex
}
// New creates a new P2P node, ready for protocol registration.
func New(conf *Config) (*Node, error) {
// Ensure the data directory exists, failing if it cannot be created
if conf.DataDir != "" {
if err := os.MkdirAll(conf.DataDir, 0700); err != nil {
return nil, err
}
}
// Assemble the networking layer and the node itself
nodeDbPath := ""
if conf.DataDir != "" {
nodeDbPath = filepath.Join(conf.DataDir, datadirNodeDatabase)
}
return &Node{
datadir: conf.DataDir,
serverConfig: &p2p.Server{
PrivateKey: conf.NodeKey(),
Name: conf.Name,
Discovery: !conf.NoDiscovery,
BootstrapNodes: conf.BootstrapNodes,
StaticNodes: conf.StaticNodes(),
TrustedNodes: conf.TrusterNodes(),
NodeDatabase: nodeDbPath,
ListenAddr: conf.ListenAddr,
NAT: conf.NAT,
Dialer: conf.Dialer,
NoDial: conf.NoDial,
MaxPeers: conf.MaxPeers,
MaxPendingPeers: conf.MaxPendingPeers,
},
serviceFuncs: []ServiceConstructor{},
eventmux: new(event.TypeMux),
}, nil
}
// Register injects a new service into the node's stack. The service created by
// the passed constructor must be unique in its type with regard to sibling ones.
func (n *Node) Register(constructor ServiceConstructor) error {
n.lock.Lock()
defer n.lock.Unlock()
if n.server != nil {
return ErrNodeRunning
}
n.serviceFuncs = append(n.serviceFuncs, constructor)
return nil
}
// Start create a live P2P node and starts running it.
func (n *Node) Start() error {
n.lock.Lock()
defer n.lock.Unlock()
// Short circuit if the node's already running
if n.server != nil {
return ErrNodeRunning
}
// Otherwise copy and specialize the P2P configuration
running := new(p2p.Server)
*running = *n.serverConfig
services := make(map[reflect.Type]Service)
for _, constructor := range n.serviceFuncs {
// Create a new context for the particular service
ctx := &ServiceContext{
datadir: n.datadir,
services: make(map[reflect.Type]Service),
EventMux: n.eventmux,
}
for kind, s := range services { // copy needed for threaded access
ctx.services[kind] = s
}
// Construct and save the service
service, err := constructor(ctx)
if err != nil {
return err
}
kind := reflect.TypeOf(service)
if _, exists := services[kind]; exists {
return &DuplicateServiceError{Kind: kind}
}
services[kind] = service
}
// Gather the protocols and start the freshly assembled P2P server
for _, service := range services {
running.Protocols = append(running.Protocols, service.Protocols()...)
}
if err := running.Start(); err != nil {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
return ErrDatadirUsed
}
return err
}
// Start each of the services
started := []reflect.Type{}
for kind, service := range services {
// Start the next service, stopping all previous upon failure
if err := service.Start(running); err != nil {
for _, kind := range started {
services[kind].Stop()
}
running.Stop()
return err
}
// Mark the service started for potential cleanup
started = append(started, kind)
}
// Finish initializing the startup
n.services = services
n.server = running
n.stop = make(chan struct{})
return nil
}
// Stop terminates a running node along with all it's services. In the node was
// not started, an error is returned.
func (n *Node) Stop() error {
n.lock.Lock()
defer n.lock.Unlock()
// Short circuit if the node's not running
if n.server == nil {
return ErrNodeStopped
}
// Otherwise terminate all the services and the P2P server too
failure := &StopError{
Services: make(map[reflect.Type]error),
}
for kind, service := range n.services {
if err := service.Stop(); err != nil {
failure.Services[kind] = err
}
}
n.server.Stop()
n.services = nil
n.server = nil
close(n.stop)
if len(failure.Services) > 0 {
return failure
}
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 {
if err := n.Stop(); err != nil {
return err
}
if err := n.Start(); err != nil {
return err
}
return nil
}
// Server retrieves the currently running P2P network layer. This method is meant
// only to inspect fields of the currently running server, life cycle management
// should be left to this Node entity.
func (n *Node) Server() *p2p.Server {
n.lock.RLock()
defer n.lock.RUnlock()
return n.server
}
// Service retrieves a currently running service registered of a specific type.
func (n *Node) Service(service interface{}) error {
n.lock.RLock()
defer n.lock.RUnlock()
// Short circuit if the node's not running
if n.server == nil {
return ErrNodeStopped
}
// Otherwise try to find the service to return
element := reflect.ValueOf(service).Elem()
if running, ok := n.services[element.Type()]; ok {
element.Set(reflect.ValueOf(running))
return nil
}
return ErrServiceUnknown
}
// DataDir retrieves the current datadir used by the protocol stack.
func (n *Node) DataDir() string {
return n.datadir
}
// EventMux retrieves the event multiplexer used by all the network services in
// the current protocol stack.
func (n *Node) EventMux() *event.TypeMux {
return n.eventmux
}

@ -0,0 +1,87 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package node_test
import (
"fmt"
"log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
)
// SampleService is a trivial network service that can be attached to a node for
// life cycle management.
//
// The following methods are needed to implement a node.Service:
// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on
// - Start() error - method invoked when the node is ready to start the service
// - Stop() error - method invoked when the node terminates the service
type SampleService struct{}
func (s *SampleService) Protocols() []p2p.Protocol { 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
// is only used to display each of the configuration options. All of these could
// have been ommited if the default behavior is desired.
nodeConfig := &node.Config{
DataDir: "", // Empty uses ephemeral storage
PrivateKey: nil, // Nil generates a node key on the fly
Name: "", // Any textual node name is allowed
NoDiscovery: false, // Can disable discovering remote nodes
BootstrapNodes: []*discover.Node{}, // List of bootstrap nodes to use
ListenAddr: ":0", // Network interface to listen on
NAT: nil, // UPnP port mapper to use for crossing firewalls
Dialer: nil, // Custom dialer to use for establishing peer connections
NoDial: false, // Can prevent this node from dialing out
MaxPeers: 0, // Number of peers to allow
MaxPendingPeers: 0, // Number of peers allowed to handshake concurrently
}
stack, err := node.New(nodeConfig)
if err != nil {
log.Fatalf("Failed to create network node: %v", err)
}
// Create and register a simple network service. This is done through the definition
// of a node.ServiceConstructor that will instantiate a node.Service. The reason for
// the factory method approach is to support service restarts without relying on the
// individual implementations' support for such operations.
constructor := func(context *node.ServiceContext) (node.Service, error) {
return new(SampleService), nil
}
if err := stack.Register(constructor); err != nil {
log.Fatalf("Failed to register service: %v", err)
}
// Boot up the entire protocol stack, do a restart and terminate
if err := stack.Start(); err != nil {
log.Fatalf("Failed to start the protocol stack: %v", err)
}
if err := stack.Restart(); err != nil {
log.Fatalf("Failed to restart the protocol stack: %v", err)
}
if err := stack.Stop(); err != nil {
log.Fatalf("Failed to stop the protocol stack: %v", err)
}
// Output:
// Service starting...
// Service stopping...
// Service starting...
// Service stopping...
}

@ -0,0 +1,496 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package node
import (
"errors"
"io/ioutil"
"os"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
)
var (
testNodeKey, _ = crypto.GenerateKey()
testNodeConfig = &Config{
PrivateKey: testNodeKey,
Name: "test node",
}
)
// Tests that an empty protocol stack can be started, restarted and stopped.
func TestNodeLifeCycle(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Ensure that a stopped node can be stopped again
for i := 0; i < 3; i++ {
if err := stack.Stop(); err != ErrNodeStopped {
t.Fatalf("iter %d: stop failure mismatch: have %v, want %v", i, err, ErrNodeStopped)
}
}
// Ensure that a node can be successfully started, but only once
if err := stack.Start(); err != nil {
t.Fatalf("failed to start node: %v", err)
}
if err := stack.Start(); err != ErrNodeRunning {
t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning)
}
// Ensure that a node can be restarted arbitrarily many times
for i := 0; i < 3; i++ {
if err := stack.Restart(); err != nil {
t.Fatalf("iter %d: failed to restart node: %v", i, err)
}
}
// Ensure that a node can be stopped, but only once
if err := stack.Stop(); err != nil {
t.Fatalf("failed to stop node: %v", err)
}
if err := stack.Stop(); err != ErrNodeStopped {
t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped)
}
}
// Tests that if the data dir is already in use, an appropriate error is returned.
func TestNodeUsedDataDir(t *testing.T) {
// Create a temporary folder to use as the data directory
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("failed to create temporary data directory: %v", err)
}
defer os.RemoveAll(dir)
// Create a new node based on the data directory
original, err := New(&Config{DataDir: dir})
if err != nil {
t.Fatalf("failed to create original protocol stack: %v", err)
}
if err := original.Start(); err != nil {
t.Fatalf("failed to start original protocol stack: %v", err)
}
defer original.Stop()
// Create a second node based on the same data directory and ensure failure
duplicate, err := New(&Config{DataDir: dir})
if err != nil {
t.Fatalf("failed to create duplicate protocol stack: %v", err)
}
if err := duplicate.Start(); err != ErrDatadirUsed {
t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed)
}
}
// Tests whether services can be registered and duplicates caught.
func TestServiceRegistry(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Register a batch of unique services and ensure they start successfully
services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC}
for i, constructor := range services {
if err := stack.Register(constructor); err != nil {
t.Fatalf("service #%d: registration failed: %v", i, err)
}
}
if err := stack.Start(); err != nil {
t.Fatalf("failed to start original service stack: %v", err)
}
if err := stack.Stop(); err != nil {
t.Fatalf("failed to stop original service stack: %v", err)
}
// Duplicate one of the services and retry starting the node
if err := stack.Register(NewNoopServiceB); err != nil {
t.Fatalf("duplicate registration failed: %v", err)
}
if err := stack.Start(); err == nil {
t.Fatalf("duplicate service started")
} else {
if _, ok := err.(*DuplicateServiceError); !ok {
t.Fatalf("duplicate error mismatch: have %v, want %v", err, DuplicateServiceError{})
}
}
}
// Tests that registered services get started and stopped correctly.
func TestServiceLifeCycle(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Register a batch of life-cycle instrumented services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
"B": InstrumentedServiceMakerB,
"C": InstrumentedServiceMakerC,
}
started := make(map[string]bool)
stopped := make(map[string]bool)
for id, maker := range services {
id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{
startHook: func(*p2p.Server) { started[id] = true },
stopHook: func() { stopped[id] = true },
}, nil
}
if err := stack.Register(maker(constructor)); err != nil {
t.Fatalf("service %s: registration failed: %v", id, err)
}
}
// Start the node and check that all services are running
if err := stack.Start(); err != nil {
t.Fatalf("failed to start protocol stack: %v", err)
}
for id, _ := range services {
if !started[id] {
t.Fatalf("service %s: freshly started service not running", id)
}
if stopped[id] {
t.Fatalf("service %s: freshly started service already stopped", id)
}
}
// Stop the node and check that all services have been stopped
if err := stack.Stop(); err != nil {
t.Fatalf("failed to stop protocol stack: %v", err)
}
for id, _ := range services {
if !stopped[id] {
t.Fatalf("service %s: freshly terminated service still running", id)
}
}
}
// Tests that services are restarted cleanly as new instances.
func TestServiceRestarts(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Define a service that does not support restarts
var (
running bool
started int
)
constructor := func(*ServiceContext) (Service, error) {
running = false
return &InstrumentedService{
startHook: func(*p2p.Server) {
if running {
panic("already running")
}
running = true
started++
},
}, nil
}
// Register the service and start the protocol stack
if err := stack.Register(constructor); err != nil {
t.Fatalf("failed to register the service: %v", err)
}
if err := stack.Start(); err != nil {
t.Fatalf("failed to start protocol stack: %v", err)
}
defer stack.Stop()
if running != true || started != 1 {
t.Fatalf("running/started mismatch: have %v/%d, want true/1", running, started)
}
// Restart the stack a few times and check successful service restarts
for i := 0; i < 3; i++ {
if err := stack.Restart(); err != nil {
t.Fatalf("iter %d: failed to restart stack: %v", i, err)
}
}
if running != true || started != 4 {
t.Fatalf("running/started mismatch: have %v/%d, want true/4", running, started)
}
}
// Tests that if a service fails to initialize itself, none of the other services
// will be allowed to even start.
func TestServiceConstructionAbortion(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Define a batch of good services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
"B": InstrumentedServiceMakerB,
"C": InstrumentedServiceMakerC,
}
started := make(map[string]bool)
for id, maker := range services {
id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{
startHook: func(*p2p.Server) { started[id] = true },
}, nil
}
if err := stack.Register(maker(constructor)); err != nil {
t.Fatalf("service %s: registration failed: %v", id, err)
}
}
// Register a service that fails to construct itself
failure := errors.New("fail")
failer := func(*ServiceContext) (Service, error) {
return nil, failure
}
if err := stack.Register(failer); err != nil {
t.Fatalf("failer registration failed: %v", err)
}
// Start the protocol stack and ensure none of the services get started
for i := 0; i < 100; i++ {
if err := stack.Start(); err != failure {
t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure)
}
for id, _ := range services {
if started[id] {
t.Fatalf("service %s: started should not have", id)
}
delete(started, id)
}
}
}
// Tests that if a service fails to start, all others started before it will be
// shut down.
func TestServiceStartupAbortion(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Register a batch of good services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
"B": InstrumentedServiceMakerB,
"C": InstrumentedServiceMakerC,
}
started := make(map[string]bool)
stopped := make(map[string]bool)
for id, maker := range services {
id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{
startHook: func(*p2p.Server) { started[id] = true },
stopHook: func() { stopped[id] = true },
}, nil
}
if err := stack.Register(maker(constructor)); err != nil {
t.Fatalf("service %s: registration failed: %v", id, err)
}
}
// Register a service that fails to start
failure := errors.New("fail")
failer := func(*ServiceContext) (Service, error) {
return &InstrumentedService{
start: failure,
}, nil
}
if err := stack.Register(failer); err != nil {
t.Fatalf("failer registration failed: %v", err)
}
// Start the protocol stack and ensure all started services stop
for i := 0; i < 100; i++ {
if err := stack.Start(); err != failure {
t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure)
}
for id, _ := range services {
if started[id] && !stopped[id] {
t.Fatalf("service %s: started but not stopped", id)
}
delete(started, id)
delete(stopped, id)
}
}
}
// Tests that even if a registered service fails to shut down cleanly, it does
// not influece the rest of the shutdown invocations.
func TestServiceTerminationGuarantee(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Register a batch of good services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
"B": InstrumentedServiceMakerB,
"C": InstrumentedServiceMakerC,
}
started := make(map[string]bool)
stopped := make(map[string]bool)
for id, maker := range services {
id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{
startHook: func(*p2p.Server) { started[id] = true },
stopHook: func() { stopped[id] = true },
}, nil
}
if err := stack.Register(maker(constructor)); err != nil {
t.Fatalf("service %s: registration failed: %v", id, err)
}
}
// Register a service that fails to shot down cleanly
failure := errors.New("fail")
failer := func(*ServiceContext) (Service, error) {
return &InstrumentedService{
stop: failure,
}, nil
}
if err := stack.Register(failer); err != nil {
t.Fatalf("failer registration failed: %v", err)
}
// Start the protocol stack, and ensure that a failing shut down terminates all
for i := 0; i < 100; i++ {
// Start the stack and make sure all is online
if err := stack.Start(); err != nil {
t.Fatalf("iter %d: failed to start protocol stack: %v", i, err)
}
for id, _ := range services {
if !started[id] {
t.Fatalf("iter %d, service %s: service not running", i, id)
}
if stopped[id] {
t.Fatalf("iter %d, service %s: service already stopped", i, id)
}
}
// Stop the stack, verify failure and check all terminations
err := stack.Stop()
if err, ok := err.(*StopError); !ok {
t.Fatalf("iter %d: termination failure mismatch: have %v, want StopError", i, err)
} else {
failer := reflect.TypeOf(&InstrumentedService{})
if err.Services[failer] != failure {
t.Fatalf("iter %d: failer termination failure mismatch: have %v, want %v", i, err.Services[failer], failure)
}
if len(err.Services) != 1 {
t.Fatalf("iter %d: failure count mismatch: have %d, want %d", i, len(err.Services), 1)
}
}
for id, _ := range services {
if !stopped[id] {
t.Fatalf("iter %d, service %s: service not terminated", i, id)
}
delete(started, id)
delete(stopped, id)
}
}
}
// TestServiceRetrieval tests that individual services can be retrieved.
func TestServiceRetrieval(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(NewNoopService); err != nil {
t.Fatalf("noop service registration failed: %v", err)
}
if err := stack.Register(NewInstrumentedService); 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 err := stack.Service(&noopServ); err != ErrNodeStopped {
t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, ErrNodeStopped)
}
var instServ *InstrumentedService
if err := stack.Service(&instServ); err != ErrNodeStopped {
t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, ErrNodeStopped)
}
// 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 err := stack.Service(&noopServ); err != nil {
t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, nil)
}
if err := stack.Service(&instServ); err != nil {
t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, nil)
}
}
// Tests that all protocols defined by individual services get launched.
func TestProtocolGather(t *testing.T) {
stack, err := New(testNodeConfig)
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
// Register a batch of services with some configured number of protocols
services := map[string]struct {
Count int
Maker InstrumentingWrapper
}{
"Zero Protocols": {0, InstrumentedServiceMakerA},
"Single Protocol": {1, InstrumentedServiceMakerB},
"Many Protocols": {25, InstrumentedServiceMakerC},
}
for id, config := range services {
protocols := make([]p2p.Protocol, config.Count)
for i := 0; i < len(protocols); i++ {
protocols[i].Name = id
protocols[i].Version = uint(i)
}
constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{
protocols: protocols,
}, nil
}
if err := stack.Register(config.Maker(constructor)); err != nil {
t.Fatalf("service %s: registration failed: %v", id, err)
}
}
// Start the services and ensure all protocols start successfully
if err := stack.Start(); err != nil {
t.Fatalf("failed to start protocol stack: %v", err)
}
defer stack.Stop()
protocols := stack.Server().Protocols
if len(protocols) != 26 {
t.Fatalf("mismatching number of protocols launched: have %d, want %d", len(protocols), 26)
}
for id, config := range services {
for ver := 0; ver < config.Count; ver++ {
launched := false
for i := 0; i < len(protocols); i++ {
if protocols[i].Name == id && protocols[i].Version == uint(ver) {
launched = true
break
}
}
if !launched {
t.Errorf("configured protocol not launched: %s v%d", id, ver)
}
}
}
}

@ -0,0 +1,80 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package node
import (
"path/filepath"
"reflect"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
)
// ServiceContext is a collection of service independent options inherited from
// 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
services map[reflect.Type]Service // Index of the already constructed services
EventMux *event.TypeMux // Event multiplexer used for decoupled notifications
}
// OpenDatabase opens an existing database with the given name (or creates one
// if no 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) OpenDatabase(name string, cache int) (ethdb.Database, error) {
if ctx.datadir == "" {
return ethdb.NewMemDatabase()
}
return ethdb.NewLDBDatabase(filepath.Join(ctx.datadir, name), cache)
}
// Service retrieves a currently running service registered of a specific type.
func (ctx *ServiceContext) Service(service interface{}) error {
element := reflect.ValueOf(service).Elem()
if running, ok := ctx.services[element.Type()]; ok {
element.Set(reflect.ValueOf(running))
return nil
}
return ErrServiceUnknown
}
// ServiceConstructor is the function signature of the constructors needed to be
// registered for service instantiation.
type ServiceConstructor func(ctx *ServiceContext) (Service, error)
// Service is an individual protocol that can be registered into a node.
//
// Notes:
// - Service life-cycle management is delegated to the node. The service is
// allowed to initialize itself upon creation, but no goroutines should be
// spun up outside of the Start method.
// - Restart logic is not required as the node will create a fresh instance
// every time a service is started.
type Service interface {
// Protocol retrieves the P2P protocols the service wishes to start.
Protocols() []p2p.Protocol
// 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.
Stop() error
}

@ -0,0 +1,97 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package node
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
// 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 {
t.Fatalf("failed to create temporary data directory: %v", err)
}
defer os.RemoveAll(dir)
if _, err := os.Stat(filepath.Join(dir, "database")); err == nil {
t.Fatalf("non-created database already exists")
}
// Request the opening/creation of a database and ensure it persists to disk
ctx := &ServiceContext{datadir: dir}
db, err := ctx.OpenDatabase("persistent", 0)
if err != nil {
t.Fatalf("failed to open persistent database: %v", err)
}
db.Close()
if _, err := os.Stat(filepath.Join(dir, "persistent")); err != nil {
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: ""}
db, err = ctx.OpenDatabase("ephemeral", 0)
if err != nil {
t.Fatalf("failed to open ephemeral database: %v", err)
}
db.Close()
if _, err := os.Stat(filepath.Join(dir, "ephemeral")); err == nil {
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 verifier that ensures a NoopA is before it and NoopB after
verifier := func(ctx *ServiceContext) (Service, error) {
var objA *NoopServiceA
if ctx.Service(&objA) != nil {
return nil, fmt.Errorf("former service not found")
}
var objB *NoopServiceB
if err := ctx.Service(&objB); err != ErrServiceUnknown {
return nil, fmt.Errorf("latters lookup error mismatch: have %v, want %v", err, ErrServiceUnknown)
}
return new(NoopService), nil
}
// Register the collection of services
if err := stack.Register(NewNoopServiceA); err != nil {
t.Fatalf("former failed to register service: %v", err)
}
if err := stack.Register(verifier); err != nil {
t.Fatalf("failed to register service verifier: %v", err)
}
if err := stack.Register(NewNoopServiceB); err != nil {
t.Fatalf("latter failed to register service: %v", 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()
}

@ -0,0 +1,117 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains a batch of utility type declarations used by the tests. As the node
// operates on unique types, a lot of them are needed to check various features.
package node
import (
"reflect"
"github.com/ethereum/go-ethereum/p2p"
)
// NoopService is a trivial implementation of the Service interface.
type NoopService struct{}
func (s *NoopService) Protocols() []p2p.Protocol { 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 }
// Set of services all wrapping the base NoopService resulting in the same method
// signatures but different outer types.
type NoopServiceA struct{ NoopService }
type NoopServiceB struct{ NoopService }
type NoopServiceC struct{ NoopService }
type NoopServiceD struct{ NoopService }
func NewNoopServiceA(*ServiceContext) (Service, error) { return new(NoopServiceA), nil }
func NewNoopServiceB(*ServiceContext) (Service, error) { return new(NoopServiceB), nil }
func NewNoopServiceC(*ServiceContext) (Service, error) { return new(NoopServiceC), nil }
func NewNoopServiceD(*ServiceContext) (Service, error) { return new(NoopServiceD), nil }
// InstrumentedService is an implementation of Service for which all interface
// methods can be instrumented both return value as well as event hook wise.
type InstrumentedService struct {
protocols []p2p.Protocol
start error
stop error
protocolsHook func()
startHook func(*p2p.Server)
stopHook func()
}
func NewInstrumentedService(*ServiceContext) (Service, error) { return new(InstrumentedService), nil }
func (s *InstrumentedService) Protocols() []p2p.Protocol {
if s.protocolsHook != nil {
s.protocolsHook()
}
return s.protocols
}
func (s *InstrumentedService) Start(server *p2p.Server) error {
if s.startHook != nil {
s.startHook(server)
}
return s.start
}
func (s *InstrumentedService) Stop() error {
if s.stopHook != nil {
s.stopHook()
}
return s.stop
}
// InstrumentingWrapper is a method to specialize a service constructor returning
// a generic InstrumentedService into one returning a wrapping specific one.
type InstrumentingWrapper func(base ServiceConstructor) ServiceConstructor
func InstrumentingWrapperMaker(base ServiceConstructor, kind reflect.Type) ServiceConstructor {
return func(ctx *ServiceContext) (Service, error) {
obj, err := base(ctx)
if err != nil {
return nil, err
}
wrapper := reflect.New(kind)
wrapper.Elem().Field(0).Set(reflect.ValueOf(obj).Elem())
return wrapper.Interface().(Service), nil
}
}
// Set of services all wrapping the base InstrumentedService resulting in the
// same method signatures but different outer types.
type InstrumentedServiceA struct{ InstrumentedService }
type InstrumentedServiceB struct{ InstrumentedService }
type InstrumentedServiceC struct{ InstrumentedService }
func InstrumentedServiceMakerA(base ServiceConstructor) ServiceConstructor {
return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceA{}))
}
func InstrumentedServiceMakerB(base ServiceConstructor) ServiceConstructor {
return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceB{}))
}
func InstrumentedServiceMakerC(base ServiceConstructor) ServiceConstructor {
return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceC{}))
}

@ -90,12 +90,11 @@ type transport interface {
// that was most recently active is the first element in entries.
type bucket struct{ entries []*Node }
func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string) *Table {
func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string) (*Table, error) {
// If no node database was given, use an in-memory one
db, err := newNodeDB(nodeDBPath, Version, ourID)
if err != nil {
glog.V(logger.Warn).Infoln("Failed to open node database:", err)
db, _ = newNodeDB("", Version, ourID)
return nil, err
}
tab := &Table{
net: t,
@ -114,7 +113,7 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
tab.buckets[i] = new(bucket)
}
go tab.refreshLoop()
return tab
return tab, nil
}
// Self returns the local node.

@ -34,7 +34,7 @@ import (
func TestTable_pingReplace(t *testing.T) {
doit := func(newNodeIsResponding, lastInBucketIsResponding bool) {
transport := newPingRecorder()
tab := newTable(transport, NodeID{}, &net.UDPAddr{}, "")
tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "")
defer tab.Close()
pingSender := newNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99)
@ -177,7 +177,7 @@ func TestTable_closest(t *testing.T) {
test := func(test *closeTest) bool {
// for any node table, Target and N
tab := newTable(nil, test.Self, &net.UDPAddr{}, "")
tab, _ := newTable(nil, test.Self, &net.UDPAddr{}, "")
defer tab.Close()
tab.stuff(test.All)
@ -236,7 +236,7 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
},
}
test := func(buf []*Node) bool {
tab := newTable(nil, NodeID{}, &net.UDPAddr{}, "")
tab, _ := newTable(nil, NodeID{}, &net.UDPAddr{}, "")
defer tab.Close()
for i := 0; i < len(buf); i++ {
ld := cfg.Rand.Intn(len(tab.buckets))
@ -279,7 +279,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
func TestTable_Lookup(t *testing.T) {
self := nodeAtDistance(common.Hash{}, 0)
tab := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "")
tab, _ := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "")
defer tab.Close()
// lookup on empty table returns no nodes

@ -200,12 +200,15 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP
if err != nil {
return nil, err
}
tab, _ := newUDP(priv, conn, natm, nodeDBPath)
tab, _, err := newUDP(priv, conn, natm, nodeDBPath)
if err != nil {
return nil, err
}
glog.V(logger.Info).Infoln("Listening,", tab.self)
return tab, nil
}
func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string) (*Table, *udp) {
func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string) (*Table, *udp, error) {
udp := &udp{
conn: c,
priv: priv,
@ -225,10 +228,15 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
}
// TODO: separate TCP port
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
udp.Table = newTable(udp, PubkeyID(&priv.PublicKey), realaddr, nodeDBPath)
tab, err := newTable(udp, PubkeyID(&priv.PublicKey), realaddr, nodeDBPath)
if err != nil {
return nil, nil, err
}
udp.Table = tab
go udp.loop()
go udp.readLoop()
return udp.Table, udp
return udp.Table, udp, nil
}
func (t *udp) close() {

@ -69,7 +69,7 @@ func newUDPTest(t *testing.T) *udpTest {
remotekey: newkey(),
remoteaddr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 30303},
}
test.table, test.udp = newUDP(test.localkey, test.pipe, nil, "")
test.table, test.udp, _ = newUDP(test.localkey, test.pipe, nil, "")
return test
}

@ -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{
xeth: xeth,
ethereum: ethereum,
codec: codec,
coder: codec.New(nil),
func NewAdminApi(xeth *xeth.XEth, stack *node.Node, codec codec.Codec) *adminApi {
api := &adminApi{
xeth: xeth,
stack: stack,
codec: codec,
coder: codec.New(nil),
}
if stack != nil {
stack.Service(&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 := &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.Service(&eth); 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 := &eth.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)
if err != nil {
return err
cfg := &eth.Config{
TestGenesisState: db,
TestGenesisBlock: test.Genesis,
Etherbase: common.Address{},
AccountManager: am,
}
err = ethereum.Start()
ethereum, err := eth.New(&node.ServiceContext{EventMux: new(event.TypeMux)}, cfg)
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++ {

@ -45,8 +45,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.EthereumService().ChainDb())
}
return object
}

@ -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.Service(&ethereum)
stack.Service(&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.Service(&ethereum); err != nil {
return nil
}
return ethereum
}
func (self *XEth) WhisperService() *whisper.Whisper {
var whisper *whisper.Whisper
if err := self.backend.Service(&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))
@ -777,7 +802,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
}
@ -801,7 +826,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 {
@ -834,7 +859,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
@ -845,7 +870,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")
@ -917,7 +942,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
@ -1005,7 +1030,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
@ -1019,7 +1044,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…
Cancel
Save