This commit is contained in:
obscuren
2015-01-05 17:10:42 +01:00
parent b0854fbff5
commit 6abf8ef78f
41 changed files with 2329 additions and 1203 deletions

View File

@ -36,6 +36,9 @@ type Config struct {
NATType string
PMPGateway string
Shh bool
Dial bool
KeyManager *crypto.KeyManager
}
@ -130,11 +133,13 @@ func New(config *Config) (*Ethereum, error) {
insertChain := eth.chainManager.InsertChain
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
// Start services
eth.txPool.Start()
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
protocols := []p2p.Protocol{ethProto}
if config.Shh {
eth.whisper = whisper.New()
protocols = append(protocols, eth.whisper.Protocol())
}
nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
if err != nil {
@ -142,12 +147,16 @@ func New(config *Config) (*Ethereum, error) {
}
eth.net = &p2p.Server{
Identity: clientId,
MaxPeers: config.MaxPeers,
Protocols: protocols,
ListenAddr: ":" + config.Port,
Blacklist: eth.blacklist,
NAT: nat,
Identity: clientId,
MaxPeers: config.MaxPeers,
Protocols: protocols,
Blacklist: eth.blacklist,
NAT: nat,
NoDial: !config.Dial,
}
if len(config.Port) > 0 {
eth.net.ListenAddr = ":" + config.Port
}
return eth, nil
@ -219,8 +228,14 @@ func (s *Ethereum) Start(seed bool) error {
if err != nil {
return err
}
// Start services
s.txPool.Start()
s.blockPool.Start()
s.whisper.Start()
if s.whisper != nil {
s.whisper.Start()
}
// broadcast transactions
s.txSub = s.eventMux.Subscribe(core.TxPreEvent{})
@ -268,7 +283,9 @@ func (s *Ethereum) Stop() {
s.txPool.Stop()
s.eventMux.Stop()
s.blockPool.Stop()
s.whisper.Stop()
if s.whisper != nil {
s.whisper.Stop()
}
logger.Infoln("Server stopped")
close(s.shutdownChan)
@ -285,16 +302,16 @@ func (self *Ethereum) txBroadcastLoop() {
// automatically stops if unsubscribe
for obj := range self.txSub.Chan() {
event := obj.(core.TxPreEvent)
self.net.Broadcast("eth", TxMsg, []interface{}{event.Tx.RlpData()})
self.net.Broadcast("eth", TxMsg, event.Tx.RlpData())
}
}
func (self *Ethereum) blockBroadcastLoop() {
// automatically stops if unsubscribe
for obj := range self.txSub.Chan() {
for obj := range self.blockSub.Chan() {
switch ev := obj.(type) {
case core.NewMinedBlockEvent:
self.net.Broadcast("eth", NewBlockMsg, ev.Block.RlpData())
self.net.Broadcast("eth", NewBlockMsg, ev.Block.RlpData(), ev.Block.Td)
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -52,18 +52,17 @@ func ProtocolError(code int, format string, params ...interface{}) (err *protoco
}
func (self protocolError) Error() (message string) {
message = self.message
if message == "" {
message, ok := errorToString[self.Code]
if len(message) == 0 {
var ok bool
self.message, ok = errorToString[self.Code]
if !ok {
panic("invalid error code")
}
if self.format != "" {
message += ": " + fmt.Sprintf(self.format, self.params...)
self.message += ": " + fmt.Sprintf(self.format, self.params...)
}
self.message = message
}
return
return self.message
}
func (self *protocolError) Fatal() bool {

View File

@ -3,7 +3,7 @@ package eth
import (
"bytes"
"fmt"
"math"
"io"
"math/big"
"github.com/ethereum/go-ethereum/core/types"
@ -95,14 +95,13 @@ func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPoo
blockPool: blockPool,
rw: rw,
peer: peer,
id: (string)(peer.Identity().Pubkey()),
id: fmt.Sprintf("%x", peer.Identity().Pubkey()[:8]),
}
err = self.handleStatus()
if err == nil {
for {
err = self.handle()
if err != nil {
fmt.Println(err)
self.blockPool.RemovePeer(self.id)
break
}
@ -117,7 +116,7 @@ func (self *ethProtocol) handle() error {
return err
}
if msg.Size > ProtocolMaxMsgSize {
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
}
// make sure that the payload has been fully consumed
defer msg.Discard()
@ -125,76 +124,87 @@ func (self *ethProtocol) handle() error {
switch msg.Code {
case StatusMsg:
return ProtocolError(ErrExtraStatusMsg, "")
return self.protoError(ErrExtraStatusMsg, "")
case TxMsg:
// TODO: rework using lazy RLP stream
var txs []*types.Transaction
if err := msg.Decode(&txs); err != nil {
return ProtocolError(ErrDecode, "%v", err)
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
}
self.txPool.AddTransactions(txs)
case GetBlockHashesMsg:
var request getBlockHashesMsgData
if err := msg.Decode(&request); err != nil {
return ProtocolError(ErrDecode, "%v", err)
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
}
hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
case BlockHashesMsg:
// TODO: redo using lazy decode , this way very inefficient on known chains
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
msgStream := rlp.NewStream(msg.Payload)
var err error
var i int
iter := func() (hash []byte, ok bool) {
hash, err = msgStream.Bytes()
if err == nil {
i++
ok = true
} else {
if err != io.EOF {
self.protoError(ErrDecode, "msg %v: after %v hashes : %v", msg, i, err)
}
}
return
}
self.blockPool.AddBlockHashes(iter, self.id)
if err != nil && err != rlp.EOL {
return ProtocolError(ErrDecode, "%v", err)
}
case GetBlocksMsg:
var blockHashes [][]byte
if err := msg.Decode(&blockHashes); err != nil {
return ProtocolError(ErrDecode, "%v", err)
}
max := int(math.Min(float64(len(blockHashes)), blockHashesBatchSize))
msgStream := rlp.NewStream(msg.Payload)
var blocks []interface{}
for i, hash := range blockHashes {
if i >= max {
break
var i int
for {
i++
var hash []byte
if err := msgStream.Decode(&hash); err != nil {
if err == io.EOF {
break
} else {
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
}
}
block := self.chainManager.GetBlock(hash)
if block != nil {
blocks = append(blocks, block.RlpData())
blocks = append(blocks, block)
}
if i == blockHashesBatchSize {
break
}
}
return self.rw.EncodeMsg(BlocksMsg, blocks...)
case BlocksMsg:
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
msgStream := rlp.NewStream(msg.Payload)
for {
var block *types.Block
var block types.Block
if err := msgStream.Decode(&block); err != nil {
if err == rlp.EOL {
if err == io.EOF {
break
} else {
return ProtocolError(ErrDecode, "%v", err)
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
}
}
self.blockPool.AddBlock(block, self.id)
self.blockPool.AddBlock(&block, self.id)
}
case NewBlockMsg:
var request newBlockMsgData
if err := msg.Decode(&request); err != nil {
return ProtocolError(ErrDecode, "%v", err)
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
}
hash := request.Block.Hash()
// to simplify backend interface adding a new block
@ -202,12 +212,12 @@ func (self *ethProtocol) handle() error {
// (or selected as new best peer)
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
called := true
iter := func() (hash []byte, ok bool) {
iter := func() ([]byte, bool) {
if called {
called = false
return hash, true
} else {
return
return nil, false
}
}
self.blockPool.AddBlockHashes(iter, self.id)
@ -215,14 +225,14 @@ func (self *ethProtocol) handle() error {
}
default:
return ProtocolError(ErrInvalidMsgCode, "%v", msg.Code)
return self.protoError(ErrInvalidMsgCode, "%v", msg.Code)
}
return nil
}
type statusMsgData struct {
ProtocolVersion uint
NetworkId uint
ProtocolVersion uint32
NetworkId uint32
TD *big.Int
CurrentBlock []byte
GenesisBlock []byte
@ -253,56 +263,56 @@ func (self *ethProtocol) handleStatus() error {
}
if msg.Code != StatusMsg {
return ProtocolError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
return self.protoError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
}
if msg.Size > ProtocolMaxMsgSize {
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
}
var status statusMsgData
if err := msg.Decode(&status); err != nil {
return ProtocolError(ErrDecode, "%v", err)
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
}
_, _, genesisBlock := self.chainManager.Status()
if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 {
return ProtocolError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
return self.protoError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
}
if status.NetworkId != NetworkId {
return ProtocolError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
}
if ProtocolVersion != status.ProtocolVersion {
return ProtocolError(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, ProtocolVersion)
return self.protoError(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, ProtocolVersion)
}
self.peer.Infof("Peer is [eth] capable (%d/%d). TD=%v H=%x\n", status.ProtocolVersion, status.NetworkId, status.TD, status.CurrentBlock[:4])
//self.blockPool.AddPeer(status.TD, status.CurrentBlock, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect)
self.peer.Infoln("AddPeer(IGNORED)")
self.blockPool.AddPeer(status.TD, status.CurrentBlock, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect)
return nil
}
func (self *ethProtocol) requestBlockHashes(from []byte) error {
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
return self.rw.EncodeMsg(GetBlockHashesMsg, from, blockHashesBatchSize)
return self.rw.EncodeMsg(GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
}
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
self.peer.Debugf("fetching %v blocks", len(hashes))
return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes))
return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
}
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
err = ProtocolError(code, format, params...)
if err.Fatal() {
self.peer.Errorln(err)
self.peer.Errorln("err %v", err)
// disconnect
} else {
self.peer.Debugln(err)
self.peer.Debugf("fyi %v", err)
}
return
}
@ -310,10 +320,10 @@ func (self *ethProtocol) protoError(code int, format string, params ...interface
func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) {
err := ProtocolError(code, format, params...)
if err.Fatal() {
self.peer.Errorln(err)
self.peer.Errorln("err %v", err)
// disconnect
} else {
self.peer.Debugln(err)
self.peer.Debugf("fyi %v", err)
}
}

View File

@ -1,35 +1,48 @@
package eth
import (
"bytes"
"io"
"log"
"math/big"
"os"
"testing"
"time"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
ethlogger "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
)
var sys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
type testMsgReadWriter struct {
in chan p2p.Msg
out chan p2p.Msg
out []p2p.Msg
}
func (self *testMsgReadWriter) In(msg p2p.Msg) {
self.in <- msg
}
func (self *testMsgReadWriter) Out(msg p2p.Msg) {
self.in <- msg
func (self *testMsgReadWriter) Out() (msg p2p.Msg, ok bool) {
if len(self.out) > 0 {
msg = self.out[0]
self.out = self.out[1:]
ok = true
}
return
}
func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
self.out <- msg
self.out = append(self.out, msg)
return nil
}
func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error {
return self.WriteMsg(p2p.NewMsg(code, data))
return self.WriteMsg(p2p.NewMsg(code, data...))
}
func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
@ -40,145 +53,83 @@ func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
return msg, nil
}
func errorCheck(t *testing.T, expCode int, err error) {
perr, ok := err.(*protocolError)
if ok && perr != nil {
if code := perr.Code; code != expCode {
ok = false
}
}
if !ok {
t.Errorf("expected error code %v, got %v", ErrNoStatusMsg, err)
}
}
type TestBackend struct {
type testTxPool struct {
getTransactions func() []*types.Transaction
addTransactions func(txs []*types.Transaction)
getBlockHashes func(hash []byte, amount uint32) (hashes [][]byte)
addBlockHashes func(next func() ([]byte, bool), peerId string)
getBlock func(hash []byte) *types.Block
addBlock func(block *types.Block, peerId string) (err error)
addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool)
removePeer func(peerId string)
status func() (td *big.Int, currentBlock []byte, genesisBlock []byte)
}
func (self *TestBackend) GetTransactions() (txs []*types.Transaction) {
if self.getTransactions != nil {
txs = self.getTransactions()
}
return
type testChainManager struct {
getBlockHashes func(hash []byte, amount uint64) (hashes [][]byte)
getBlock func(hash []byte) *types.Block
status func() (td *big.Int, currentBlock []byte, genesisBlock []byte)
}
func (self *TestBackend) AddTransactions(txs []*types.Transaction) {
type testBlockPool struct {
addBlockHashes func(next func() ([]byte, bool), peerId string)
addBlock func(block *types.Block, peerId string) (err error)
addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool)
removePeer func(peerId string)
}
// func (self *testTxPool) GetTransactions() (txs []*types.Transaction) {
// if self.getTransactions != nil {
// txs = self.getTransactions()
// }
// return
// }
func (self *testTxPool) AddTransactions(txs []*types.Transaction) {
if self.addTransactions != nil {
self.addTransactions(txs)
}
}
func (self *TestBackend) GetBlockHashes(hash []byte, amount uint32) (hashes [][]byte) {
func (self *testChainManager) GetBlockHashesFromHash(hash []byte, amount uint64) (hashes [][]byte) {
if self.getBlockHashes != nil {
hashes = self.getBlockHashes(hash, amount)
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
if self.addBlockHashes != nil {
self.addBlockHashes(next, peerId)
}
}
=======
func (self *TestBackend) AddHash(hash []byte, peer *p2p.Peer) (more bool) {
if self.addHash != nil {
more = self.addHash(hash, peer)
=======
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
if self.addBlockHashes != nil {
self.addBlockHashes(next, peerId)
>>>>>>> eth protocol changes
}
}
<<<<<<< HEAD
>>>>>>> initial commit for eth-p2p integration
=======
>>>>>>> eth protocol changes
func (self *TestBackend) GetBlock(hash []byte) (block *types.Block) {
if self.getBlock != nil {
block = self.getBlock(hash)
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
if self.addBlock != nil {
err = self.addBlock(block, peerId)
=======
func (self *TestBackend) AddBlock(td *big.Int, block *types.Block, peer *p2p.Peer) (fetchHashes bool, err error) {
if self.addBlock != nil {
fetchHashes, err = self.addBlock(td, block, peer)
>>>>>>> initial commit for eth-p2p integration
=======
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
if self.addBlock != nil {
err = self.addBlock(block, peerId)
>>>>>>> eth protocol changes
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
if self.addPeer != nil {
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
=======
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peer *p2p.Peer) (fetchHashes bool) {
if self.addPeer != nil {
fetchHashes = self.addPeer(td, currentBlock, peer)
>>>>>>> initial commit for eth-p2p integration
=======
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
if self.addPeer != nil {
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
>>>>>>> eth protocol changes
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> eth protocol changes
func (self *TestBackend) RemovePeer(peerId string) {
if self.removePeer != nil {
self.removePeer(peerId)
}
}
<<<<<<< HEAD
=======
>>>>>>> initial commit for eth-p2p integration
=======
>>>>>>> eth protocol changes
func (self *TestBackend) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
func (self *testChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
if self.status != nil {
td, currentBlock, genesisBlock = self.status()
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> eth protocol changes
func (self *testChainManager) GetBlock(hash []byte) (block *types.Block) {
if self.getBlock != nil {
block = self.getBlock(hash)
}
return
}
func (self *testBlockPool) AddBlockHashes(next func() ([]byte, bool), peerId string) {
if self.addBlockHashes != nil {
self.addBlockHashes(next, peerId)
}
}
func (self *testBlockPool) AddBlock(block *types.Block, peerId string) {
if self.addBlock != nil {
self.addBlock(block, peerId)
}
}
func (self *testBlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool) {
if self.addPeer != nil {
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, peerError)
}
return
}
func (self *testBlockPool) RemovePeer(peerId string) {
if self.removePeer != nil {
self.removePeer(peerId)
}
}
// TODO: refactor this into p2p/client_identity
type peerId struct {
pubkey []byte
@ -201,32 +152,119 @@ func testPeer() *p2p.Peer {
return p2p.NewPeer(&peerId{}, []p2p.Cap{})
}
func TestErrNoStatusMsg(t *testing.T) {
<<<<<<< HEAD
=======
func TestEth(t *testing.T) {
>>>>>>> initial commit for eth-p2p integration
=======
>>>>>>> eth protocol changes
quit := make(chan bool)
rw := &testMsgReadWriter{make(chan p2p.Msg, 10), make(chan p2p.Msg, 10)}
testBackend := &TestBackend{}
var err error
go func() {
<<<<<<< HEAD
<<<<<<< HEAD
err = runEthProtocol(testBackend, testPeer(), rw)
=======
err = runEthProtocol(testBackend, nil, rw)
>>>>>>> initial commit for eth-p2p integration
=======
err = runEthProtocol(testBackend, testPeer(), rw)
>>>>>>> eth protocol changes
close(quit)
}()
statusMsg := p2p.NewMsg(4)
rw.In(statusMsg)
<-quit
errorCheck(t, ErrNoStatusMsg, err)
// read(t, remote, []byte("hello, world"), nil)
type ethProtocolTester struct {
quit chan error
rw *testMsgReadWriter // p2p.MsgReadWriter
txPool *testTxPool // txPool
chainManager *testChainManager // chainManager
blockPool *testBlockPool // blockPool
t *testing.T
}
func newEth(t *testing.T) *ethProtocolTester {
return &ethProtocolTester{
quit: make(chan error),
rw: &testMsgReadWriter{in: make(chan p2p.Msg, 10)},
txPool: &testTxPool{},
chainManager: &testChainManager{},
blockPool: &testBlockPool{},
t: t,
}
}
func (self *ethProtocolTester) reset() {
self.rw = &testMsgReadWriter{in: make(chan p2p.Msg, 10)}
self.quit = make(chan error)
}
func (self *ethProtocolTester) checkError(expCode int, delay time.Duration) (err error) {
var timer = time.After(delay)
select {
case err = <-self.quit:
case <-timer:
self.t.Errorf("no error after %v, expected %v", delay, expCode)
return
}
perr, ok := err.(*protocolError)
if ok && perr != nil {
if code := perr.Code; code != expCode {
self.t.Errorf("expected protocol error (code %v), got %v (%v)", expCode, code, err)
}
} else {
self.t.Errorf("expected protocol error (code %v), got %v", expCode, err)
}
return
}
func (self *ethProtocolTester) In(msg p2p.Msg) {
self.rw.In(msg)
}
func (self *ethProtocolTester) Out() (p2p.Msg, bool) {
return self.rw.Out()
}
func (self *ethProtocolTester) checkMsg(i int, code uint64, val interface{}) (msg p2p.Msg) {
if i >= len(self.rw.out) {
self.t.Errorf("expected at least %v msgs, got %v", i, len(self.rw.out))
return
}
msg = self.rw.out[i]
if msg.Code != code {
self.t.Errorf("expected msg code %v, got %v", code, msg.Code)
}
if val != nil {
if err := msg.Decode(val); err != nil {
self.t.Errorf("rlp encoding error: %v", err)
}
}
return
}
func (self *ethProtocolTester) run() {
err := runEthProtocol(self.txPool, self.chainManager, self.blockPool, testPeer(), self.rw)
self.quit <- err
}
func TestStatusMsgErrors(t *testing.T) {
logInit()
eth := newEth(t)
td := ethutil.Big1
currentBlock := []byte{1}
genesis := []byte{2}
eth.chainManager.status = func() (*big.Int, []byte, []byte) { return td, currentBlock, genesis }
go eth.run()
statusMsg := p2p.NewMsg(4)
eth.In(statusMsg)
delay := 1 * time.Second
eth.checkError(ErrNoStatusMsg, delay)
var status statusMsgData
eth.checkMsg(0, StatusMsg, &status) // first outgoing msg should be StatusMsg
if status.TD.Cmp(td) != 0 ||
status.ProtocolVersion != ProtocolVersion ||
status.NetworkId != NetworkId ||
status.TD.Cmp(td) != 0 ||
bytes.Compare(status.CurrentBlock, currentBlock) != 0 ||
bytes.Compare(status.GenesisBlock, genesis) != 0 {
t.Errorf("incorrect outgoing status")
}
eth.reset()
go eth.run()
statusMsg = p2p.NewMsg(0, uint32(48), uint32(0), td, currentBlock, genesis)
eth.In(statusMsg)
eth.checkError(ErrProtocolVersionMismatch, delay)
eth.reset()
go eth.run()
statusMsg = p2p.NewMsg(0, uint32(49), uint32(1), td, currentBlock, genesis)
eth.In(statusMsg)
eth.checkError(ErrNetworkIdMismatch, delay)
eth.reset()
go eth.run()
statusMsg = p2p.NewMsg(0, uint32(49), uint32(0), td, currentBlock, []byte{3})
eth.In(statusMsg)
eth.checkError(ErrGenesisBlockMismatch, delay)
}

27
eth/test/README.md Normal file
View File

@ -0,0 +1,27 @@
= Integration tests for eth protocol and blockpool
This is a simple suite of tests to fire up a local test node with peers to test blockchain synchronisation and download.
The scripts call ethereum (assumed to be compiled in go-ethereum root).
To run a test:
. run.sh 00 02
Without arguments, all tests are run.
Peers are launched with preloaded imported chains. In order to prevent them from synchronizing with each other they are set with `-dial=false` and `-maxpeer 1` options. They log into `/tmp/eth.test/nodes/XX` where XX is the last two digits of their port.
Chains to import can be bootstrapped by letting nodes mine for some time. This is done with
. bootstrap.sh
Only the relative timing and forks matter so they should work if the bootstrap script is rerun.
The reference blockchain of tests are soft links to these import chains and check at the end of a test run.
Connecting to peers and exporting blockchain is scripted with JS files executed by the JSRE, see `tests/XX.sh`.
Each test is set with a timeout. This may vary on different computers so adjust sensibly.
If you kill a test before it completes, do not forget to kill all the background processes, since they will impact the result. Use:
killall ethereum

9
eth/test/bootstrap.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# bootstrap chains - used to regenerate tests/chains/*.chain
mkdir -p chains
bash ./mine.sh 00 10
bash ./mine.sh 01 5 00
bash ./mine.sh 02 10 00
bash ./mine.sh 03 5 02
bash ./mine.sh 04 10 02

BIN
eth/test/chains/00.chain Executable file

Binary file not shown.

BIN
eth/test/chains/01.chain Executable file

Binary file not shown.

BIN
eth/test/chains/02.chain Executable file

Binary file not shown.

BIN
eth/test/chains/03.chain Executable file

Binary file not shown.

BIN
eth/test/chains/04.chain Executable file

Binary file not shown.

20
eth/test/mine.sh Normal file
View File

@ -0,0 +1,20 @@
#!/bin/bash
# bash ./mine.sh node_id timeout(sec) [basechain]
ETH=../../ethereum
MINE="$ETH -datadir tmp/nodes/$1 -seed=false -port '' -shh=false -id test$1"
rm -rf tmp/nodes/$1
echo "Creating chain $1..."
if [[ "" != "$3" ]]; then
CHAIN="chains/$3.chain"
CHAINARG="-chain $CHAIN"
$MINE -mine $CHAINARG -loglevel 3 | grep 'importing'
fi
$MINE -mine -loglevel 0 &
PID=$!
sleep $2
kill $PID
$MINE -loglevel 3 <(echo "eth.export(\"chains/$1.chain\")") > /tmp/eth.test/mine.tmp &
PID=$!
sleep 1
kill $PID
cat /tmp/eth.test/mine.tmp | grep 'exporting'

53
eth/test/run.sh Normal file
View File

@ -0,0 +1,53 @@
#!/bin/bash
# bash run.sh (testid0 testid1 ...)
# runs tests tests/testid0.sh tests/testid1.sh ...
# without arguments, it runs all tests
. tests/common.sh
TESTS=
if [ "$#" -eq 0 ]; then
for NAME in tests/??.sh; do
i=`basename $NAME .sh`
TESTS="$TESTS $i"
done
else
TESTS=$@
fi
ETH=../../ethereum
DIR="/tmp/eth.test/nodes"
TIMEOUT=10
mkdir -p $DIR/js
echo "running tests $TESTS"
for NAME in $TESTS; do
PIDS=
CHAIN="tests/$NAME.chain"
JSFILE="$DIR/js/$NAME.js"
CHAIN_TEST="$DIR/$NAME/chain"
echo "RUN: test $NAME"
cat tests/common.js > $JSFILE
. tests/$NAME.sh
sleep $TIMEOUT
echo "timeout after $TIMEOUT seconds: killing $PIDS"
kill $PIDS
if [ -r "$CHAIN" ]; then
if diff $CHAIN $CHAIN_TEST >/dev/null ; then
echo "chain ok: $CHAIN=$CHAIN_TEST"
else
echo "FAIL: chains differ: expected $CHAIN ; got $CHAIN_TEST"
continue
fi
fi
ERRORS=$DIR/errors
if [ -r "$ERRORS" ]; then
echo "FAIL: "
cat $ERRORS
else
echo PASS
fi
done

1
eth/test/tests/00.chain Symbolic link
View File

@ -0,0 +1 @@
../chains/01.chain

13
eth/test/tests/00.sh Normal file
View File

@ -0,0 +1,13 @@
#!/bin/bash
TIMEOUT=4
cat >> $JSFILE <<EOF
eth.addPeer("localhost:30311");
sleep(1000)
eth.export("$CHAIN_TEST");
EOF
peer 11 01
test_node $NAME "" -loglevel 5 $JSFILE

1
eth/test/tests/01.chain Symbolic link
View File

@ -0,0 +1 @@
../chains/02.chain

18
eth/test/tests/01.sh Normal file
View File

@ -0,0 +1,18 @@
#!/bin/bash
TIMEOUT=5
cat >> $JSFILE <<EOF
eth.addPeer("localhost:30311");
log("added peer localhost:30311");
sleep(1000);
log("added peer localhost:30312");
eth.addPeer("localhost:30312");
sleep(3000);
eth.export("$CHAIN_TEST");
EOF
peer 11 01
peer 12 02
test_node $NAME "" -loglevel 5 $JSFILE

1
eth/test/tests/02.chain Symbolic link
View File

@ -0,0 +1 @@
../chains/01.chain

19
eth/test/tests/02.sh Normal file
View File

@ -0,0 +1,19 @@
#!/bin/bash
TIMEOUT=6
cat >> $JSFILE <<EOF
eth.addPeer("localhost:30311");
sleep(200);
eth.addPeer("localhost:30312");
sleep(3000);
eth.export("$CHAIN_TEST");
EOF
peer 11 01
peer 12 02
P13ID=$PID
test_node $NAME "" -loglevel 5 $JSFILE
sleep 0.5
kill $P13ID

1
eth/test/tests/03.chain Symbolic link
View File

@ -0,0 +1 @@
../chains/12k.chain

14
eth/test/tests/03.sh Normal file
View File

@ -0,0 +1,14 @@
#!/bin/bash
TIMEOUT=35
cat >> $JSFILE <<EOF
eth.addPeer("localhost:30311");
sleep(30000);
eth.export("$CHAIN_TEST");
EOF
peer 11 12k
sleep 2
test_node $NAME "" -loglevel 5 $JSFILE

17
eth/test/tests/04.sh Normal file
View File

@ -0,0 +1,17 @@
#!/bin/bash
TIMEOUT=15
cat >> $JSFILE <<EOF
eth.addPeer("localhost:30311");
sleep(200);
eth.addPeer("localhost:30312");
sleep(13000);
eth.export("$CHAIN_TEST");
EOF
peer 11 01 -mine
peer 12 02
test_node $NAME "" -loglevel 5 $JSFILE
sleep 6
cat $DIR/$NAME/debug.log | grep 'best peer'

20
eth/test/tests/05.sh Normal file
View File

@ -0,0 +1,20 @@
#!/bin/bash
TIMEOUT=60
cat >> $JSFILE <<EOF
eth.addPeer("localhost:30311");
sleep(200);
eth.addPeer("localhost:30312");
eth.addPeer("localhost:30313");
eth.addPeer("localhost:30314");
sleep(3000);
eth.export("$CHAIN_TEST");
EOF
peer 11 01 -mine
peer 12 02 -mine
peer 13 03
peer 14 04
test_node $NAME "" -loglevel 5 $JSFILE

9
eth/test/tests/common.js Normal file
View File

@ -0,0 +1,9 @@
function log(text) {
console.log("[JS TEST SCRIPT] " + text);
}
function sleep(seconds) {
var now = new Date().getTime();
while(new Date().getTime() < now + seconds){}
}

20
eth/test/tests/common.sh Normal file
View File

@ -0,0 +1,20 @@
#!/bin/bash
# launched by run.sh
function test_node {
rm -rf $DIR/$1
ARGS="-datadir $DIR/$1 -debug debug -seed=false -shh=false -id test$1"
if [ "" != "$2" ]; then
chain="chains/$2.chain"
echo "import chain $chain"
$ETH $ARGS -loglevel 3 -chain $chain | grep CLI |grep import
fi
echo "starting test node $1 with extra args ${@:3}"
$ETH $ARGS -port 303$1 ${@:3} &
PID=$!
PIDS="$PIDS $PID"
}
function peer {
test_node $@ -loglevel 5 -logfile debug.log -maxpeer 1 -dial=false
}