Commit 7dde2b90 authored by Jeffrey Wilcke's avatar Jeffrey Wilcke

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

Customizable protocol stacks
parents ffe58bf5 3e1000fd
// 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 ( ...@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
re "github.com/ethereum/go-ethereum/jsre" 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"
"github.com/ethereum/go-ethereum/rpc/api" "github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
...@@ -77,7 +78,7 @@ func (r dumbterm) AppendHistory(string) {} ...@@ -77,7 +78,7 @@ func (r dumbterm) AppendHistory(string) {}
type jsre struct { type jsre struct {
re *re.JSRE re *re.JSRE
ethereum *eth.Ethereum stack *node.Node
xeth *xeth.XEth xeth *xeth.XEth
wait chan *big.Int wait chan *big.Int
ps1 string ps1 string
...@@ -176,18 +177,18 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str ...@@ -176,18 +177,18 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str
return js return js
} }
func newJSRE(ethereum *eth.Ethereum, docRoot, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { func newJSRE(stack *node.Node, docRoot, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
js := &jsre{ethereum: ethereum, ps1: "> "} js := &jsre{stack: stack, ps1: "> "}
// set default cors domain used by startRpc from CLI flag // set default cors domain used by startRpc from CLI flag
js.corsDomain = corsDomain js.corsDomain = corsDomain
if f == nil { if f == nil {
f = js f = js
} }
js.xeth = xeth.New(ethereum, f) js.xeth = xeth.New(stack, f)
js.wait = js.xeth.UpdateState() js.wait = js.xeth.UpdateState()
js.client = client js.client = client
if clt, ok := js.client.(*comms.InProcClient); ok { 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...)) clt.Initialize(api.Merge(offeredApis...))
} }
} }
...@@ -202,14 +203,14 @@ func newJSRE(ethereum *eth.Ethereum, docRoot, corsDomain string, client comms.Et ...@@ -202,14 +203,14 @@ func newJSRE(ethereum *eth.Ethereum, docRoot, corsDomain string, client comms.Et
js.prompter = dumbterm{bufio.NewReader(os.Stdin)} js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
} else { } else {
lr := liner.NewLiner() 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) lr.SetCtrlCAborts(true)
js.loadAutoCompletion() js.loadAutoCompletion()
lr.SetWordCompleter(apiWordCompleter) lr.SetWordCompleter(apiWordCompleter)
lr.SetTabCompletionStyle(liner.TabPrints) lr.SetTabCompletionStyle(liner.TabPrints)
js.prompter = lr js.prompter = lr
js.atexit = func() { 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() lr.Close()
close(js.wait) close(js.wait)
} }
...@@ -276,7 +277,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { ...@@ -276,7 +277,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
apiNames = append(apiNames, a) 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 { if err != nil {
utils.Fatalf("Unable to determine supported api's: %v", err) utils.Fatalf("Unable to determine supported api's: %v", err)
} }
...@@ -342,8 +343,14 @@ func (self *jsre) AskPassword() (string, bool) { ...@@ -342,8 +343,14 @@ func (self *jsre) AskPassword() (string, bool) {
} }
func (self *jsre) ConfirmTransaction(tx string) bool { func (self *jsre) ConfirmTransaction(tx string) bool {
if self.ethereum.NatSpec { // Retrieve the Ethereum instance from the node
notice := natspec.GetNotice(self.xeth, tx, self.ethereum.HTTPClient()) 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) fmt.Println(notice)
answer, _ := self.Prompt("Confirm Transaction [y/n]") answer, _ := self.Prompt("Confirm Transaction [y/n]")
return strings.HasPrefix(strings.Trim(answer, " "), "y") return strings.HasPrefix(strings.Trim(answer, " "), "y")
...@@ -359,7 +366,11 @@ func (self *jsre) UnlockAccount(addr []byte) bool { ...@@ -359,7 +366,11 @@ func (self *jsre) UnlockAccount(addr []byte) bool {
return false return false
} }
// TODO: allow retry // 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 return false
} else { } else {
fmt.Println("Account is now unlocked for this session.") fmt.Println("Account is now unlocked for this session.")
......
...@@ -38,6 +38,7 @@ import ( ...@@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "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/codec"
"github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/comms"
) )
...@@ -66,7 +67,10 @@ type testjethre struct { ...@@ -66,7 +67,10 @@ type testjethre struct {
} }
func (self *testjethre) UnlockAccount(acc []byte) bool { 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 { if err != nil {
panic("unable to unlock") panic("unable to unlock")
} }
...@@ -74,67 +78,74 @@ func (self *testjethre) UnlockAccount(acc []byte) bool { ...@@ -74,67 +78,74 @@ func (self *testjethre) UnlockAccount(acc []byte) bool {
} }
func (self *testjethre) ConfirmTransaction(tx string) 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) self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client)
} }
return true return true
} }
func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { func testJEthRE(t *testing.T) (string, *testjethre, *node.Node) {
return testREPL(t, nil) 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") tmp, err := ioutil.TempDir("", "geth-test")
if err != nil { if err != nil {
t.Fatal(err) 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() db, _ := ethdb.NewMemDatabase()
core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)}) core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)})
ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
am := accounts.NewManager(ks) ethConf := &eth.Config{
conf := &eth.Config{ TestGenesisState: db,
NodeKey: testNodeKey, AccountManager: accman,
DataDir: tmp,
AccountManager: am,
MaxPeers: 0,
Name: "test",
DocRoot: "/", DocRoot: "/",
SolcPath: testSolcPath, SolcPath: testSolcPath,
PowTest: true, PowTest: true,
NewDB: func(path string) (ethdb.Database, error) { return db, nil },
} }
if config != nil { if config != nil {
config(conf) config(ethConf)
} }
ethereum, err := eth.New(conf) if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
if err != nil { t.Fatalf("failed to register ethereum protocol: %v", err)
t.Fatal("%v", err)
} }
// Initialize all the keys for testing
keyb, err := crypto.HexToECDSA(testKey) keyb, err := crypto.HexToECDSA(testKey)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
key := crypto.NewKeyFromECDSA(keyb) key := crypto.NewKeyFromECDSA(keyb)
err = ks.StoreKey(key, "") if err := keystore.StoreKey(key, ""); err != nil {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := accman.Unlock(key.Address, ""); err != nil {
err = am.Unlock(key.Address, "")
if err != nil {
t.Fatal(err) 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") assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
client := comms.NewInProcClient(codec.JSON) client := comms.NewInProcClient(codec.JSON)
tf := &testjethre{client: ethereum.HTTPClient()} tf := &testjethre{client: ethereum.HTTPClient()}
repl := newJSRE(ethereum, assetPath, "", client, false, tf) repl := newJSRE(stack, assetPath, "", client, false, tf)
tf.jsre = repl tf.jsre = repl
return tmp, tf, ethereum return tmp, tf, stack
} }
func TestNodeInfo(t *testing.T) { func TestNodeInfo(t *testing.T) {
...@@ -151,11 +162,8 @@ func TestNodeInfo(t *testing.T) { ...@@ -151,11 +162,8 @@ func TestNodeInfo(t *testing.T) {
} }
func TestAccounts(t *testing.T) { func TestAccounts(t *testing.T) {
tmp, repl, ethereum := testJEthRE(t) tmp, repl, node := testJEthRE(t)
if err := ethereum.Start(); err != nil { defer node.Stop()
t.Fatalf("error starting ethereum: %v", err)
}
defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`) checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`)
...@@ -174,11 +182,8 @@ func TestAccounts(t *testing.T) { ...@@ -174,11 +182,8 @@ func TestAccounts(t *testing.T) {
} }
func TestBlockChain(t *testing.T) { func TestBlockChain(t *testing.T) {
tmp, repl, ethereum := testJEthRE(t) tmp, repl, node := testJEthRE(t)
if err := ethereum.Start(); err != nil { defer node.Stop()
t.Fatalf("error starting ethereum: %v", err)
}
defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
// get current block dump before export/import. // get current block dump before export/import.
val, err := repl.re.Run("JSON.stringify(debug.dumpBlock(eth.blockNumber))") val, err := repl.re.Run("JSON.stringify(debug.dumpBlock(eth.blockNumber))")
...@@ -196,6 +201,8 @@ func TestBlockChain(t *testing.T) { ...@@ -196,6 +201,8 @@ func TestBlockChain(t *testing.T) {
tmpfile := filepath.Join(extmp, "export.chain") tmpfile := filepath.Join(extmp, "export.chain")
tmpfileq := strconv.Quote(tmpfile) tmpfileq := strconv.Quote(tmpfile)
var ethereum *eth.Ethereum
node.Service(&ethereum)
ethereum.BlockChain().Reset() ethereum.BlockChain().Reset()
checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`) checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`)
...@@ -209,22 +216,15 @@ func TestBlockChain(t *testing.T) { ...@@ -209,22 +216,15 @@ func TestBlockChain(t *testing.T) {
} }
func TestMining(t *testing.T) { func TestMining(t *testing.T) {
tmp, repl, ethereum := testJEthRE(t) tmp, repl, node := testJEthRE(t)
if err := ethereum.Start(); err != nil { defer node.Stop()
t.Fatalf("error starting ethereum: %v", err)
}
defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
checkEvalJSON(t, repl, `eth.mining`, `false`) checkEvalJSON(t, repl, `eth.mining`, `false`)
} }
func TestRPC(t *testing.T) { func TestRPC(t *testing.T) {
tmp, repl, ethereum := testJEthRE(t) tmp, repl, node := testJEthRE(t)
if err := ethereum.Start(); err != nil { defer node.Stop()
t.Errorf("error starting ethereum: %v", err)
return
}
defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 5004, "*", "web3,eth,net")`, `true`) checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 5004, "*", "web3,eth,net")`, `true`)
...@@ -234,12 +234,8 @@ func TestCheckTestAccountBalance(t *testing.T) { ...@@ -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 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 // 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 // and i have no means to fix this, sorry - @obscuren
tmp, repl, ethereum := testJEthRE(t) tmp, repl, node := testJEthRE(t)
if err := ethereum.Start(); err != nil { defer node.Stop()
t.Errorf("error starting ethereum: %v", err)
return
}
defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
repl.re.Run(`primary = "` + testAddress + `"`) repl.re.Run(`primary = "` + testAddress + `"`)
...@@ -247,12 +243,8 @@ func TestCheckTestAccountBalance(t *testing.T) { ...@@ -247,12 +243,8 @@ func TestCheckTestAccountBalance(t *testing.T) {
} }
func TestSignature(t *testing.T) { func TestSignature(t *testing.T) {
tmp, repl, ethereum := testJEthRE(t) tmp, repl, node := testJEthRE(t)
if err := ethereum.Start(); err != nil { defer node.Stop()
t.Errorf("error starting ethereum: %v", err)
return
}
defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
val, err := repl.re.Run(`eth.sign("` + testAddress + `", "` + testHash + `")`) val, err := repl.re.Run(`eth.sign("` + testAddress + `", "` + testHash + `")`)
...@@ -443,7 +435,10 @@ multiply7 = Multiply7.at(contractaddress); ...@@ -443,7 +435,10 @@ multiply7 = Multiply7.at(contractaddress);
} }
func pendingTransactions(repl *testjethre, t *testing.T) (txc int64, err error) { 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 return int64(len(txs)), nil
} }
...@@ -468,12 +463,15 @@ func processTxs(repl *testjethre, t *testing.T, expTxc int) bool { ...@@ -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) t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc)
return false return false
} }
err = repl.ethereum.StartMining(runtime.NumCPU(), "") var ethereum *eth.Ethereum
repl.stack.Service(&ethereum)
err = ethereum.StartMining(runtime.NumCPU(), "")
if err != nil { if err != nil {
t.Errorf("unexpected error mining: %v", err) t.Errorf("unexpected error mining: %v", err)
return false return false
} }
defer repl.ethereum.StopMining() defer ethereum.StopMining()
timer := time.NewTimer(100 * time.Second) timer := time.NewTimer(100 * time.Second)
height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1)) height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1))
......
This diff is collapsed.
// 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...))
}
// 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 ( ...@@ -29,9 +29,9 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "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"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/peterh/liner" "github.com/peterh/liner"
) )
...@@ -110,10 +110,9 @@ func Fatalf(format string, args ...interface{}) { ...@@ -110,10 +110,9 @@ func Fatalf(format string, args ...interface{}) {
os.Exit(1) os.Exit(1)
} }
func StartEthereum(ethereum *eth.Ethereum) { func StartNode(stack *node.Node) {
glog.V(logger.Info).Infoln("Starting", ethereum.Name()) if err := stack.Start(); err != nil {
if err := ethereum.Start(); err != nil { Fatalf("Error starting protocol stack: %v", err)
Fatalf("Error starting Ethereum: %v", err)
} }
go func() { go func() {
sigc := make(chan os.Signal, 1) sigc := make(chan os.Signal, 1)
...@@ -121,7 +120,7 @@ func StartEthereum(ethereum *eth.Ethereum) { ...@@ -121,7 +120,7 @@ func StartEthereum(ethereum *eth.Ethereum) {
defer signal.Stop(sigc) defer signal.Stop(sigc)
<-sigc <-sigc
glog.V(logger.Info).Infoln("Got interrupt, shutting down...") glog.V(logger.Info).Infoln("Got interrupt, shutting down...")
go ethereum.Stop() go stack.Stop()
logger.Flush() logger.Flush()
for i := 10; i > 0; i-- { for i := 10; i > 0; i-- {
<-sigc <-sigc
......
This diff is collapsed.
...@@ -34,6 +34,8 @@ import ( ...@@ -34,6 +34,8 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "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" xe "github.com/ethereum/go-ethereum/xeth"
) )
...@@ -146,13 +148,11 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { ...@@ -146,13 +148,11 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
} }
// only use minimalistic stack with no networking // only use minimalistic stack with no networking
return eth.New(&eth.Config{ return eth.New(&node.ServiceContext{EventMux: new(event.TypeMux)}, &eth.Config{
DataDir: tmp,
AccountManager: am, AccountManager: am,
Etherbase: common.HexToAddress(testAddress), Etherbase: common.HexToAddress(testAddress),
MaxPeers: 0,
PowTest: true, PowTest: true,
NewDB: func(path string) (ethdb.Database, error) { return db, nil }, TestGenesisState: db,
GpoMinGasPrice: common.Big1, GpoMinGasPrice: common.Big1,
GpobaseCorrectionFactor: 1, GpobaseCorrectionFactor: 1,
GpoMaxGasPrice: common.Big1, GpoMaxGasPrice: common.Big1,
...@@ -166,7 +166,7 @@ func testInit(t *testing.T) (self *testFrontend) { ...@@ -166,7 +166,7 @@ func testInit(t *testing.T) (self *testFrontend) {
t.Errorf("error creating ethereum: %v", err) t.Errorf("error creating ethereum: %v", err)
return return
} }
err = ethereum.Start() err = ethereum.Start(nil)
if err != nil { if err != nil {
t.Errorf("error starting ethereum: %v", err) t.Errorf("error starting ethereum: %v", err)
return return
...@@ -174,7 +174,7 @@ func testInit(t *testing.T) (self *testFrontend) { ...@@ -174,7 +174,7 @@ func testInit(t *testing.T) (self *testFrontend) {
// mock frontend // mock frontend
self = &testFrontend{t: t, ethereum: ethereum} self = &testFrontend{t: t, ethereum: ethereum}
self.xeth = xe.New(ethereum, self) self.xeth = xe.New(nil, self)
self.wait = self.xeth.UpdateState() self.wait = self.xeth.UpdateState()
addr, _ := self.ethereum.Etherbase() addr, _ := self.ethereum.Etherbase()
......
...@@ -24,13 +24,13 @@ import ( ...@@ -24,13 +24,13 @@ import (
) )
const ( const (
hashLength = 32 HashLength = 32
addressLength = 20 AddressLength = 20
) )
type ( type (
Hash [hashLength]byte Hash [HashLength]byte
Address [addressLength]byte Address [AddressLength]byte
) )
func BytesToHash(b []byte) Hash { func BytesToHash(b []byte) Hash {
...@@ -53,10 +53,10 @@ func (h Hash) Hex() string { return "0x" + Bytes2Hex(h[:]) } ...@@ -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 // Sets the hash to the value of b. If b is larger than len(h) it will panic
func (h *Hash) SetBytes(b []byte) { func (h *Hash) SetBytes(b []byte) {
if len(b) > len(h) { 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 // 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)) } ...@@ -92,6 +92,18 @@ func StringToAddress(s string) Address { return BytesToAddress([]byte(s)) }
func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) }
func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) } 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 // Get the string representation of the underlying address
func (a Address) Str() string { return string(a[:]) } func (a Address) Str() string { return string(a[:]) }
func (a Address) Bytes() []byte { return a[:] } func (a Address) Bytes() []byte { return a[:] }
...@@ -102,9 +114,9 @@ func (a Address) Hex() string { return "0x" + Bytes2Hex(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 // Sets the address to the value of b. If b is larger than len(a) it will panic
func (a *Address) SetBytes(b []byte) { func (a *Address) SetBytes(b []byte) {
if len(b) > len(a) { 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 // Set string `s` to a. If s is larger than len(a) it will panic
......
...@@ -34,7 +34,7 @@ func proc() (Validator, *BlockChain) { ...@@ -34,7 +34,7 @@ func proc() (Validator, *BlockChain) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux var mux event.TypeMux
WriteTestNetGenesisBlock(db, 0) WriteTestNetGenesisBlock(db)
blockchain, err := NewBlockChain(db, thePow(), &mux) blockchain, err := NewBlockChain(db, thePow(), &mux)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
......
...@@ -149,11 +149,7 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl ...@@ -149,11 +149,7 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
bc.genesisBlock = bc.GetBlockByNumber(0) bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil { if bc.genesisBlock == nil {
reader, err := NewDefaultGenesisReader() bc.genesisBlock, err = WriteDefaultGenesisBlock(chainDb)
if err != nil {
return nil, err
}
bc.genesisBlock, err = WriteGenesisBlock(chainDb, reader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -51,7 +51,7 @@ func thePow() pow.PoW { ...@@ -51,7 +51,7 @@ func thePow() pow.PoW {
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
var eventMux event.TypeMux var eventMux event.TypeMux
WriteTestNetGenesisBlock(db, 0) WriteTestNetGenesisBlock(db)
blockchain, err := NewBlockChain(db, thePow(), &eventMux) blockchain, err := NewBlockChain(db, thePow(), &eventMux)
if err != nil { if err != nil {
t.Error("failed creating blockchain:", err) t.Error("failed creating blockchain:", err)
...@@ -506,7 +506,7 @@ func testReorgShort(t *testing.T, full bool) { ...@@ -506,7 +506,7 @@ func testReorgShort(t *testing.T, full bool) {
func testReorg(t *testing.T, first, second []int, td int64, full bool) { func testReorg(t *testing.T, first, second []int, td int64, full bool) {
// Create a pristine block chain // Create a pristine block chain
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis, _ := WriteTestNetGenesisBlock(db, 0) genesis, _ := WriteTestNetGenesisBlock(db)
bc := chm(genesis, db) bc := chm(genesis, db)
// Insert an easy and a difficult chain afterwards // Insert an easy and a difficult chain afterwards
...@@ -553,7 +553,7 @@ func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) } ...@@ -553,7 +553,7 @@ func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
func testBadHashes(t *testing.T, full bool) { func testBadHashes(t *testing.T, full bool) {
// Create a pristine block chain // Create a pristine block chain
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis, _ := WriteTestNetGenesisBlock(db, 0) genesis, _ := WriteTestNetGenesisBlock(db)
bc := chm(genesis, db) bc := chm(genesis, db)
// Create a chain, ban a hash and try to import // Create a chain, ban a hash and try to import
...@@ -580,7 +580,7 @@ func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) } ...@@ -580,7 +580,7 @@ func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
func testReorgBadHashes(t *testing.T, full bool) { func testReorgBadHashes(t *testing.T, full bool) {
// Create a pristine block chain // Create a pristine block chain
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis, _ := WriteTestNetGenesisBlock(db, 0) genesis, _ := WriteTestNetGenesisBlock(db)
bc := chm(genesis, db) bc := chm(genesis, db)
// Create a chain, import and ban aferwards // Create a chain, import and ban aferwards
......
...@@ -220,7 +220,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) { ...@@ -220,7 +220,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
// Initialize a fresh chain with only a genesis block // Initialize a fresh chain with only a genesis block
genesis, _ := WriteTestNetGenesisBlock(db, 0) genesis, _ := WriteTestNetGenesisBlock(db)
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
// Create and inject the requested chain // Create and inject the requested chain
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
package core package core
import ( import (
"compress/gzip"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
...@@ -158,29 +160,42 @@ func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount) ...@@ -158,29 +160,42 @@ func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount)
return block return block
} }
func WriteTestNetGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Block, error) { // WriteDefaultGenesisBlock assembles the official Ethereum genesis block and
testGenesis := fmt.Sprintf(`{ // writes it - along with all associated state - into a chain database.
"nonce": "0x%x", func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
"difficulty": "0x20000", return WriteGenesisBlock(chainDb, strings.NewReader(DefaultGenesisBlock()))
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578", }
"coinbase": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00", // WriteTestNetGenesisBlock assembles the Morden test network genesis block and
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", // writes it - along with all associated state - into a chain database.
"extraData": "0x", func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
"gasLimit": "0x2FEFD8", return WriteGenesisBlock(chainDb, strings.NewReader(TestNetGenesisBlock()))
"alloc": { }
"0000000000000000000000000000000000000001": { "balance": "1" },
"0000000000000000000000000000000000000002": { "balance": "1" }, // WriteOlympicGenesisBlock assembles the Olympic genesis block and writes it
"0000000000000000000000000000000000000003": { "balance": "1" }, // along with all associated state into a chain database.
"0000000000000000000000000000000000000004": { "balance": "1" }, func WriteOlympicGenesisBlock(db ethdb.Database) (*types.Block, error) {
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } 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)) return string(blob)
return WriteGenesisBlock(chainDb, strings.NewReader(testGenesis))
} }
func WriteOlympicGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Block, error) { // OlympicGenesisBlock assembles a JSON string representing the Olympic genesis
testGenesis := fmt.Sprintf(`{ // block.
func OlympicGenesisBlock() string {
return fmt.Sprintf(`{
"nonce":"0x%x", "nonce":"0x%x",
"gasLimit":"0x%x", "gasLimit":"0x%x",
"difficulty":"0x%x", "difficulty":"0x%x",
...@@ -198,6 +213,27 @@ func WriteOlympicGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Bloc ...@@ -198,6 +213,27 @@ func WriteOlympicGenesisBlock(chainDb ethdb.Database, nonce uint64) (*types.Bloc
"e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"} "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
} }
}`, types.EncodeNonce(nonce), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes()) }`, types.EncodeNonce(42), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
return WriteGenesisBlock(chainDb, strings.NewReader(testGenesis)) }
// 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))
} }
This diff is collapsed.
// 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
}
// 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")
}
}
// 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)
}
// 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
}
// 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...
}
This diff is collapsed.
// 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
}
// 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()
}
// 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 { ...@@ -90,12 +90,11 @@ type transport interface {
// that was most recently active is the first element in entries. // that was most recently active is the first element in entries.
type bucket struct{ entries []*Node } 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 // If no node database was given, use an in-memory one
db, err := newNodeDB(nodeDBPath, Version, ourID) db, err := newNodeDB(nodeDBPath, Version, ourID)
if err != nil { if err != nil {
glog.V(logger.Warn).Infoln("Failed to open node database:", err) return nil, err
db, _ = newNodeDB("", Version, ourID)
} }
tab := &Table{ tab := &Table{
net: t, net: t,
...@@ -114,7 +113,7 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string ...@@ -114,7 +113,7 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
tab.buckets[i] = new(bucket) tab.buckets[i] = new(bucket)
} }
go tab.refreshLoop() go tab.refreshLoop()
return tab return tab, nil
} }
// Self returns the local node. // Self returns the local node.
......
...@@ -34,7 +34,7 @@ import ( ...@@ -34,7 +34,7 @@ import (
func TestTable_pingReplace(t *testing.T) { func TestTable_pingReplace(t *testing.T) {
doit := func(newNodeIsResponding, lastInBucketIsResponding bool) { doit := func(newNodeIsResponding, lastInBucketIsResponding bool) {
transport := newPingRecorder() transport := newPingRecorder()
tab := newTable(transport, NodeID{}, &net.UDPAddr{}, "") tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "")
defer tab.Close() defer tab.Close()
pingSender := newNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99) pingSender := newNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99)
...@@ -177,7 +177,7 @@ func TestTable_closest(t *testing.T) { ...@@ -177,7 +177,7 @@ func TestTable_closest(t *testing.T) {
test := func(test *closeTest) bool { test := func(test *closeTest) bool {
// for any node table, Target and N // for any node table, Target and N
tab := newTable(nil, test.Self, &net.UDPAddr{}, "") tab, _ := newTable(nil, test.Self, &net.UDPAddr{}, "")
defer tab.Close() defer tab.Close()
tab.stuff(test.All) tab.stuff(test.All)
...@@ -236,7 +236,7 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) { ...@@ -236,7 +236,7 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
}, },
} }
test := func(buf []*Node) bool { test := func(buf []*Node) bool {
tab := newTable(nil, NodeID{}, &net.UDPAddr{}, "") tab, _ := newTable(nil, NodeID{}, &net.UDPAddr{}, "")
defer tab.Close() defer tab.Close()
for i := 0; i < len(buf); i++ { for i := 0; i < len(buf); i++ {
ld := cfg.Rand.Intn(len(tab.buckets)) ld := cfg.Rand.Intn(len(tab.buckets))
...@@ -279,7 +279,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value { ...@@ -279,7 +279,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
func TestTable_Lookup(t *testing.T) { func TestTable_Lookup(t *testing.T) {
self := nodeAtDistance(common.Hash{}, 0) self := nodeAtDistance(common.Hash{}, 0)
tab := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "") tab, _ := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "")
defer tab.Close() defer tab.Close()
// lookup on empty table returns no nodes // lookup on empty table returns no nodes
......
...@@ -200,12 +200,15 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP ...@@ -200,12 +200,15 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP
if err != nil { if err != nil {
return nil, err 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) glog.V(logger.Info).Infoln("Listening,", tab.self)
return tab, nil 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{ udp := &udp{
conn: c, conn: c,
priv: priv, priv: priv,
...@@ -225,10 +228,15 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin ...@@ -225,10 +228,15 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
} }
// TODO: separate TCP port // TODO: separate TCP port
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.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.loop()
go udp.readLoop() go udp.readLoop()
return udp.Table, udp return udp.Table, udp, nil
} }
func (t *udp) close() { func (t *udp) close() {
......
...@@ -69,7 +69,7 @@ func newUDPTest(t *testing.T) *udpTest { ...@@ -69,7 +69,7 @@ func newUDPTest(t *testing.T) *udpTest {
remotekey: newkey(), remotekey: newkey(),
remoteaddr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 30303}, 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 return test
} }
......
...@@ -32,6 +32,8 @@ import ( ...@@ -32,6 +32,8 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger/glog" "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/rlp"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/comms"
...@@ -80,19 +82,24 @@ type adminhandler func(*adminApi, *shared.Request) (interface{}, error) ...@@ -80,19 +82,24 @@ type adminhandler func(*adminApi, *shared.Request) (interface{}, error)
// admin api provider // admin api provider
type adminApi struct { type adminApi struct {
xeth *xeth.XEth xeth *xeth.XEth
stack *node.Node
ethereum *eth.Ethereum ethereum *eth.Ethereum
codec codec.Codec codec codec.Codec
coder codec.ApiCoder coder codec.ApiCoder
} }
// create a new admin api instance // create a new admin api instance
func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *adminApi { func NewAdminApi(xeth *xeth.XEth, stack *node.Node, codec codec.Codec) *adminApi {
return &adminApi{ api := &adminApi{
xeth: xeth, xeth: xeth,
ethereum: ethereum, stack: stack,
codec: codec, codec: codec,
coder: codec.New(nil), coder: codec.New(nil),
} }
if stack != nil {
stack.Service(&api.ethereum)
}
return api
} }
// collection with supported methods // collection with supported methods
...@@ -128,24 +135,24 @@ func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) { ...@@ -128,24 +135,24 @@ func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
if err := self.coder.Decode(req.Params, &args); err != nil { if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
node, err := discover.ParseNode(args.Url)
err := self.ethereum.AddPeer(args.Url) if err != nil {
if err == nil { return nil, fmt.Errorf("invalid node URL: %v", err)
return true, nil
} }
return false, err self.stack.Server().AddPeer(node)
return true, nil
} }
func (self *adminApi) Peers(req *shared.Request) (interface{}, error) { 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) { 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) { 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 { func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
...@@ -253,7 +260,7 @@ func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) { ...@@ -253,7 +260,7 @@ func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) {
CorsDomain: args.CorsDomain, 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 { if err != nil {
return false, err return false, err
} }
......
...@@ -93,7 +93,7 @@ func TestCompileSolidity(t *testing.T) { ...@@ -93,7 +93,7 @@ func TestCompileSolidity(t *testing.T) {
expSource := source expSource := source
eth := &eth.Ethereum{} eth := &eth.Ethereum{}
xeth := xeth.NewTest(eth, nil) xeth := xeth.NewTest(nil, nil)
api := NewEthApi(xeth, eth, codec.JSON) api := NewEthApi(xeth, eth, codec.JSON)
var rpcRequest shared.Request var rpcRequest shared.Request
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/eth" "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/codec"
"github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
...@@ -154,7 +155,7 @@ var ( ...@@ -154,7 +155,7 @@ var (
) )
// Parse a comma separated API string to individual api's // 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 { if len(strings.TrimSpace(apistr)) == 0 {
return nil, fmt.Errorf("Empty apistr provided") return nil, fmt.Errorf("Empty apistr provided")
} }
...@@ -162,10 +163,16 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. ...@@ -162,10 +163,16 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
names := strings.Split(apistr, ",") names := strings.Split(apistr, ",")
apis := make([]shared.EthereumApi, len(names)) 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 { for i, name := range names {
switch strings.ToLower(strings.TrimSpace(name)) { switch strings.ToLower(strings.TrimSpace(name)) {
case shared.AdminApiName: case shared.AdminApiName:
apis[i] = NewAdminApi(xeth, eth, codec) apis[i] = NewAdminApi(xeth, stack, codec)
case shared.DebugApiName: case shared.DebugApiName:
apis[i] = NewDebugApi(xeth, eth, codec) apis[i] = NewDebugApi(xeth, eth, codec)
case shared.DbApiName: case shared.DbApiName:
...@@ -188,7 +195,6 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. ...@@ -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 nil, fmt.Errorf("Unknown API '%s'", name)
} }
} }
return apis, nil return apis, nil
} }
......
...@@ -36,7 +36,9 @@ import ( ...@@ -36,7 +36,9 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
...@@ -165,15 +167,6 @@ func runBlockTest(test *BlockTest) error { ...@@ -165,15 +167,6 @@ func runBlockTest(test *BlockTest) error {
ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"), crypto.StandardScryptN, crypto.StandardScryptP) ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"), crypto.StandardScryptN, crypto.StandardScryptP)
am := accounts.NewManager(ks) am := accounts.NewManager(ks)
db, _ := ethdb.NewMemDatabase() 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 // import pre accounts & construct test genesis block & state root
_, err := test.InsertPreState(db, am) _, err := test.InsertPreState(db, am)
...@@ -181,16 +174,16 @@ func runBlockTest(test *BlockTest) error { ...@@ -181,16 +174,16 @@ func runBlockTest(test *BlockTest) error {
return fmt.Errorf("InsertPreState: %v", err) return fmt.Errorf("InsertPreState: %v", err)
} }
ethereum, err := eth.New(cfg) cfg := &eth.Config{
if err != nil { TestGenesisState: db,
return err TestGenesisBlock: test.Genesis,
Etherbase: common.Address{},
AccountManager: am,
} }
ethereum, err := eth.New(&node.ServiceContext{EventMux: new(event.TypeMux)}, cfg)
err = ethereum.Start()
if err != nil { if err != nil {
return err return err
} }
cm := ethereum.BlockChain() cm := ethereum.BlockChain()
validBlocks, err := test.TryBlocksInsert(cm) validBlocks, err := test.TryBlocksInsert(cm)
if err != nil { if err != nil {
......
...@@ -37,7 +37,7 @@ func startTestPeer() *testPeer { ...@@ -37,7 +37,7 @@ func startTestPeer() *testPeer {
// Create a whisper client and connect with it to the tester peer // Create a whisper client and connect with it to the tester peer
client := New() client := New()
client.Start() client.Start(nil)
termed := make(chan struct{}) termed := make(chan struct{})
go func() { go func() {
......
...@@ -98,9 +98,9 @@ func New() *Whisper { ...@@ -98,9 +98,9 @@ func New() *Whisper {
return whisper return whisper
} }
// Protocol returns the whisper sub-protocol handler for this particular client. // Protocols returns the whisper sub-protocols ran by this particular client.
func (self *Whisper) Protocol() p2p.Protocol { func (self *Whisper) Protocols() []p2p.Protocol {
return self.protocol return []p2p.Protocol{self.protocol}
} }
// Version returns the whisper sub-protocols version number. // Version returns the whisper sub-protocols version number.
...@@ -156,14 +156,20 @@ func (self *Whisper) Send(envelope *Envelope) error { ...@@ -156,14 +156,20 @@ func (self *Whisper) Send(envelope *Envelope) error {
return self.add(envelope) 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") glog.V(logger.Info).Infoln("Whisper started")
go self.update() 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) close(self.quit)
glog.V(logger.Info).Infoln("Whisper stopped") glog.V(logger.Info).Infoln("Whisper stopped")
return nil
} }
// Messages retrieves all the currently pooled messages matching a filter id. // Messages retrieves all the currently pooled messages matching a filter id.
......
...@@ -33,7 +33,7 @@ func startTestCluster(n int) []*Whisper { ...@@ -33,7 +33,7 @@ func startTestCluster(n int) []*Whisper {
whispers := make([]*Whisper, n) whispers := make([]*Whisper, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
whispers[i] = New() whispers[i] = New()
whispers[i].Start() whispers[i].Start(nil)
} }
// Wire all the peers to the root one // Wire all the peers to the root one
for i := 1; i < n; i++ { for i := 1; i < n; i++ {
......
...@@ -45,8 +45,7 @@ func (self *State) SafeGet(addr string) *Object { ...@@ -45,8 +45,7 @@ func (self *State) SafeGet(addr string) *Object {
func (self *State) safeGet(addr string) *state.StateObject { func (self *State) safeGet(addr string) *state.StateObject {
object := self.state.GetStateObject(common.HexToAddress(addr)) object := self.state.GetStateObject(common.HexToAddress(addr))
if object == nil { 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 return object
} }
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment