integrate blockpool into eth
- remove blockpool code - remove blockpool integration test (kinda embarrassing) - remove errors.go
This commit is contained in:
parent
cdc86965cb
commit
16ecda951b
@ -7,6 +7,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/blockpool"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
@ -117,7 +118,7 @@ type Ethereum struct {
|
|||||||
blockProcessor *core.BlockProcessor
|
blockProcessor *core.BlockProcessor
|
||||||
txPool *core.TxPool
|
txPool *core.TxPool
|
||||||
chainManager *core.ChainManager
|
chainManager *core.ChainManager
|
||||||
blockPool *BlockPool
|
blockPool *blockpool.BlockPool
|
||||||
whisper *whisper.Whisper
|
whisper *whisper.Whisper
|
||||||
|
|
||||||
net *p2p.Server
|
net *p2p.Server
|
||||||
@ -185,7 +186,7 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
|
|
||||||
hasBlock := eth.chainManager.HasBlock
|
hasBlock := eth.chainManager.HasBlock
|
||||||
insertChain := eth.chainManager.InsertChain
|
insertChain := eth.chainManager.InsertChain
|
||||||
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
|
eth.blockPool = blockpool.New(hasBlock, insertChain, ezp.Verify)
|
||||||
|
|
||||||
netprv, err := config.nodeKey()
|
netprv, err := config.nodeKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -220,7 +221,7 @@ func (s *Ethereum) Name() string { return s.net.Name }
|
|||||||
func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager }
|
func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager }
|
||||||
func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
|
func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
|
||||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||||
func (s *Ethereum) BlockPool() *BlockPool { return s.blockPool }
|
func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool }
|
||||||
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
||||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||||
func (s *Ethereum) Db() ethutil.Database { return s.db }
|
func (s *Ethereum) Db() ethutil.Database { return s.db }
|
||||||
|
1239
eth/block_pool.go
1239
eth/block_pool.go
File diff suppressed because it is too large
Load Diff
@ -1,983 +0,0 @@
|
|||||||
package eth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"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/pow"
|
|
||||||
)
|
|
||||||
|
|
||||||
const waitTimeout = 60 // seconds
|
|
||||||
|
|
||||||
var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
|
|
||||||
|
|
||||||
var ini = false
|
|
||||||
|
|
||||||
func logInit() {
|
|
||||||
if !ini {
|
|
||||||
ethlogger.AddLogSystem(logsys)
|
|
||||||
ini = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test helpers
|
|
||||||
func arrayEq(a, b []int) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range a {
|
|
||||||
if a[i] != b[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type intToHash map[int][]byte
|
|
||||||
|
|
||||||
type hashToInt map[string]int
|
|
||||||
|
|
||||||
// hashPool is a test helper, that allows random hashes to be referred to by integers
|
|
||||||
type testHashPool struct {
|
|
||||||
intToHash
|
|
||||||
hashToInt
|
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHash(i int) []byte {
|
|
||||||
return crypto.Sha3([]byte(string(i)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testHashPool) indexesToHashes(indexes []int) (hashes [][]byte) {
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
for _, i := range indexes {
|
|
||||||
hash, found := self.intToHash[i]
|
|
||||||
if !found {
|
|
||||||
hash = newHash(i)
|
|
||||||
self.intToHash[i] = hash
|
|
||||||
self.hashToInt[string(hash)] = i
|
|
||||||
}
|
|
||||||
hashes = append(hashes, hash)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testHashPool) hashesToIndexes(hashes [][]byte) (indexes []int) {
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
for _, hash := range hashes {
|
|
||||||
i, found := self.hashToInt[string(hash)]
|
|
||||||
if !found {
|
|
||||||
i = -1
|
|
||||||
}
|
|
||||||
indexes = append(indexes, i)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// test blockChain is an integer trie
|
|
||||||
type blockChain map[int][]int
|
|
||||||
|
|
||||||
// blockPoolTester provides the interface between tests and a blockPool
|
|
||||||
//
|
|
||||||
// refBlockChain is used to guide which blocks will be accepted as valid
|
|
||||||
// blockChain gives the current state of the blockchain and
|
|
||||||
// accumulates inserts so that we can check the resulting chain
|
|
||||||
type blockPoolTester struct {
|
|
||||||
hashPool *testHashPool
|
|
||||||
lock sync.RWMutex
|
|
||||||
refBlockChain blockChain
|
|
||||||
blockChain blockChain
|
|
||||||
blockPool *BlockPool
|
|
||||||
t *testing.T
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestBlockPool(t *testing.T) (hashPool *testHashPool, blockPool *BlockPool, b *blockPoolTester) {
|
|
||||||
hashPool = &testHashPool{intToHash: make(intToHash), hashToInt: make(hashToInt)}
|
|
||||||
b = &blockPoolTester{
|
|
||||||
t: t,
|
|
||||||
hashPool: hashPool,
|
|
||||||
blockChain: make(blockChain),
|
|
||||||
refBlockChain: make(blockChain),
|
|
||||||
}
|
|
||||||
b.blockPool = NewBlockPool(b.hasBlock, b.insertChain, b.verifyPoW)
|
|
||||||
blockPool = b.blockPool
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *blockPoolTester) Errorf(format string, params ...interface{}) {
|
|
||||||
fmt.Printf(format+"\n", params...)
|
|
||||||
self.t.Errorf(format, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockPoolTester implements the 3 callbacks needed by the blockPool:
|
|
||||||
// hasBlock, insetChain, verifyPoW
|
|
||||||
func (self *blockPoolTester) hasBlock(block []byte) (ok bool) {
|
|
||||||
self.lock.RLock()
|
|
||||||
defer self.lock.RUnlock()
|
|
||||||
indexes := self.hashPool.hashesToIndexes([][]byte{block})
|
|
||||||
i := indexes[0]
|
|
||||||
_, ok = self.blockChain[i]
|
|
||||||
fmt.Printf("has block %v (%x...): %v\n", i, block[0:4], ok)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *blockPoolTester) insertChain(blocks types.Blocks) error {
|
|
||||||
self.lock.RLock()
|
|
||||||
defer self.lock.RUnlock()
|
|
||||||
var parent, child int
|
|
||||||
var children, refChildren []int
|
|
||||||
var ok bool
|
|
||||||
for _, block := range blocks {
|
|
||||||
child = self.hashPool.hashesToIndexes([][]byte{block.Hash()})[0]
|
|
||||||
_, ok = self.blockChain[child]
|
|
||||||
if ok {
|
|
||||||
fmt.Printf("block %v already in blockchain\n", child)
|
|
||||||
continue // already in chain
|
|
||||||
}
|
|
||||||
parent = self.hashPool.hashesToIndexes([][]byte{block.ParentHeaderHash})[0]
|
|
||||||
children, ok = self.blockChain[parent]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("parent %v not in blockchain ", parent)
|
|
||||||
}
|
|
||||||
ok = false
|
|
||||||
var found bool
|
|
||||||
refChildren, found = self.refBlockChain[parent]
|
|
||||||
if found {
|
|
||||||
for _, c := range refChildren {
|
|
||||||
if c == child {
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid block %v", child)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
// accept any blocks if parent not in refBlockChain
|
|
||||||
fmt.Errorf("blockchain insert %v -> %v\n", parent, child)
|
|
||||||
self.blockChain[parent] = append(children, child)
|
|
||||||
self.blockChain[child] = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *blockPoolTester) verifyPoW(pblock pow.Block) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// test helper that compares the resulting blockChain to the desired blockChain
|
|
||||||
func (self *blockPoolTester) checkBlockChain(blockChain map[int][]int) {
|
|
||||||
for k, v := range self.blockChain {
|
|
||||||
fmt.Printf("got: %v -> %v\n", k, v)
|
|
||||||
}
|
|
||||||
for k, v := range blockChain {
|
|
||||||
fmt.Printf("expected: %v -> %v\n", k, v)
|
|
||||||
}
|
|
||||||
if len(blockChain) != len(self.blockChain) {
|
|
||||||
self.Errorf("blockchain incorrect (zlength differ)")
|
|
||||||
}
|
|
||||||
for k, v := range blockChain {
|
|
||||||
vv, ok := self.blockChain[k]
|
|
||||||
if !ok || !arrayEq(v, vv) {
|
|
||||||
self.Errorf("blockchain incorrect on %v -> %v (!= %v)", k, vv, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
// peerTester provides the peer callbacks for the blockPool
|
|
||||||
// it registers actual callbacks so that result can be compared to desired behaviour
|
|
||||||
// provides helper functions to mock the protocol calls to the blockPool
|
|
||||||
type peerTester struct {
|
|
||||||
blockHashesRequests []int
|
|
||||||
blocksRequests [][]int
|
|
||||||
blocksRequestsMap map[int]bool
|
|
||||||
peerErrors []int
|
|
||||||
blockPool *BlockPool
|
|
||||||
hashPool *testHashPool
|
|
||||||
lock sync.RWMutex
|
|
||||||
id string
|
|
||||||
td int
|
|
||||||
currentBlock int
|
|
||||||
t *testing.T
|
|
||||||
}
|
|
||||||
|
|
||||||
// peerTester constructor takes hashPool and blockPool from the blockPoolTester
|
|
||||||
func (self *blockPoolTester) newPeer(id string, td int, cb int) *peerTester {
|
|
||||||
return &peerTester{
|
|
||||||
id: id,
|
|
||||||
td: td,
|
|
||||||
currentBlock: cb,
|
|
||||||
hashPool: self.hashPool,
|
|
||||||
blockPool: self.blockPool,
|
|
||||||
t: self.t,
|
|
||||||
blocksRequestsMap: make(map[int]bool),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *peerTester) Errorf(format string, params ...interface{}) {
|
|
||||||
fmt.Printf(format+"\n", params...)
|
|
||||||
self.t.Errorf(format, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper to compare actual and expected block requests
|
|
||||||
func (self *peerTester) checkBlocksRequests(blocksRequests ...[]int) {
|
|
||||||
if len(blocksRequests) > len(self.blocksRequests) {
|
|
||||||
self.Errorf("blocks requests incorrect (length differ)\ngot %v\nexpected %v", self.blocksRequests, blocksRequests)
|
|
||||||
} else {
|
|
||||||
for i, rr := range blocksRequests {
|
|
||||||
r := self.blocksRequests[i]
|
|
||||||
if !arrayEq(r, rr) {
|
|
||||||
self.Errorf("blocks requests incorrect\ngot %v\nexpected %v", self.blocksRequests, blocksRequests)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper to compare actual and expected block hash requests
|
|
||||||
func (self *peerTester) checkBlockHashesRequests(blocksHashesRequests ...int) {
|
|
||||||
rr := blocksHashesRequests
|
|
||||||
self.lock.RLock()
|
|
||||||
r := self.blockHashesRequests
|
|
||||||
self.lock.RUnlock()
|
|
||||||
if len(r) != len(rr) {
|
|
||||||
self.Errorf("block hashes requests incorrect (length differ)\ngot %v\nexpected %v", r, rr)
|
|
||||||
} else {
|
|
||||||
if !arrayEq(r, rr) {
|
|
||||||
self.Errorf("block hashes requests incorrect\ngot %v\nexpected %v", r, rr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// waiter function used by peer.AddBlocks
|
|
||||||
// blocking until requests appear
|
|
||||||
// since block requests are sent to any random peers
|
|
||||||
// block request map is shared between peers
|
|
||||||
// times out after a period
|
|
||||||
func (self *peerTester) waitBlocksRequests(blocksRequest ...int) {
|
|
||||||
timeout := time.After(waitTimeout * time.Second)
|
|
||||||
rr := blocksRequest
|
|
||||||
for {
|
|
||||||
self.lock.RLock()
|
|
||||||
r := self.blocksRequestsMap
|
|
||||||
fmt.Printf("[%s] blocks request check %v (%v)\n", self.id, rr, r)
|
|
||||||
i := 0
|
|
||||||
for i = 0; i < len(rr); i++ {
|
|
||||||
_, ok := r[rr[i]]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.lock.RUnlock()
|
|
||||||
|
|
||||||
if i == len(rr) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
select {
|
|
||||||
case <-timeout:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// waiter function used by peer.AddBlockHashes
|
|
||||||
// blocking until requests appear
|
|
||||||
// times out after a period
|
|
||||||
func (self *peerTester) waitBlockHashesRequests(blocksHashesRequest int) {
|
|
||||||
timeout := time.After(waitTimeout * time.Second)
|
|
||||||
rr := blocksHashesRequest
|
|
||||||
for i := 0; ; {
|
|
||||||
self.lock.RLock()
|
|
||||||
r := self.blockHashesRequests
|
|
||||||
self.lock.RUnlock()
|
|
||||||
fmt.Printf("[%s] block hash request check %v (%v)\n", self.id, rr, r)
|
|
||||||
for ; i < len(r); i++ {
|
|
||||||
if rr == r[i] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
select {
|
|
||||||
case <-timeout:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mocks a simple blockchain 0 (genesis) ... n (head)
|
|
||||||
func (self *blockPoolTester) initRefBlockChain(n int) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
self.refBlockChain[i] = []int{i + 1}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// peerTester functions that mimic protocol calls to the blockpool
|
|
||||||
// registers the peer with the blockPool
|
|
||||||
func (self *peerTester) AddPeer() bool {
|
|
||||||
hash := self.hashPool.indexesToHashes([]int{self.currentBlock})[0]
|
|
||||||
return self.blockPool.AddPeer(big.NewInt(int64(self.td)), hash, self.id, self.requestBlockHashes, self.requestBlocks, self.peerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
// peer sends blockhashes if and when gets a request
|
|
||||||
func (self *peerTester) AddBlockHashes(indexes ...int) {
|
|
||||||
fmt.Printf("ready to add block hashes %v\n", indexes)
|
|
||||||
|
|
||||||
self.waitBlockHashesRequests(indexes[0])
|
|
||||||
fmt.Printf("adding block hashes %v\n", indexes)
|
|
||||||
hashes := self.hashPool.indexesToHashes(indexes)
|
|
||||||
i := 1
|
|
||||||
next := func() (hash []byte, ok bool) {
|
|
||||||
if i < len(hashes) {
|
|
||||||
hash = hashes[i]
|
|
||||||
ok = true
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.blockPool.AddBlockHashes(next, self.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// peer sends blocks if and when there is a request
|
|
||||||
// (in the shared request store, not necessarily to a person)
|
|
||||||
func (self *peerTester) AddBlocks(indexes ...int) {
|
|
||||||
hashes := self.hashPool.indexesToHashes(indexes)
|
|
||||||
fmt.Printf("ready to add blocks %v\n", indexes[1:])
|
|
||||||
self.waitBlocksRequests(indexes[1:]...)
|
|
||||||
fmt.Printf("adding blocks %v \n", indexes[1:])
|
|
||||||
for i := 1; i < len(hashes); i++ {
|
|
||||||
fmt.Printf("adding block %v %x\n", indexes[i], hashes[i][:4])
|
|
||||||
self.blockPool.AddBlock(&types.Block{HeaderHash: ethutil.Bytes(hashes[i]), ParentHeaderHash: ethutil.Bytes(hashes[i-1])}, self.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// peer callbacks
|
|
||||||
// -1 is special: not found (a hash never seen)
|
|
||||||
// records block hashes requests by the blockPool
|
|
||||||
func (self *peerTester) requestBlockHashes(hash []byte) error {
|
|
||||||
indexes := self.hashPool.hashesToIndexes([][]byte{hash})
|
|
||||||
fmt.Printf("[%s] blocks hash request %v %x\n", self.id, indexes[0], hash[:4])
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
self.blockHashesRequests = append(self.blockHashesRequests, indexes[0])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// records block requests by the blockPool
|
|
||||||
func (self *peerTester) requestBlocks(hashes [][]byte) error {
|
|
||||||
indexes := self.hashPool.hashesToIndexes(hashes)
|
|
||||||
fmt.Printf("blocks request %v %x...\n", indexes, hashes[0][:4])
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
self.blocksRequests = append(self.blocksRequests, indexes)
|
|
||||||
for _, i := range indexes {
|
|
||||||
self.blocksRequestsMap[i] = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// records the error codes of all the peerErrors found the blockPool
|
|
||||||
func (self *peerTester) peerError(code int, format string, params ...interface{}) {
|
|
||||||
self.peerErrors = append(self.peerErrors, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the actual tests
|
|
||||||
func TestAddPeer(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
peer0 := blockPoolTester.newPeer("peer0", 1, 0)
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 2, 1)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 3, 2)
|
|
||||||
var peer *peerInfo
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
// pool
|
|
||||||
best := peer0.AddPeer()
|
|
||||||
if !best {
|
|
||||||
t.Errorf("peer0 (TD=1) not accepted as best")
|
|
||||||
}
|
|
||||||
if blockPool.peer.id != "peer0" {
|
|
||||||
t.Errorf("peer0 (TD=1) not set as best")
|
|
||||||
}
|
|
||||||
// peer0.checkBlockHashesRequests(0)
|
|
||||||
|
|
||||||
best = peer2.AddPeer()
|
|
||||||
if !best {
|
|
||||||
t.Errorf("peer2 (TD=3) not accepted as best")
|
|
||||||
}
|
|
||||||
if blockPool.peer.id != "peer2" {
|
|
||||||
t.Errorf("peer2 (TD=3) not set as best")
|
|
||||||
}
|
|
||||||
peer2.waitBlocksRequests(2)
|
|
||||||
|
|
||||||
best = peer1.AddPeer()
|
|
||||||
if best {
|
|
||||||
t.Errorf("peer1 (TD=2) accepted as best")
|
|
||||||
}
|
|
||||||
if blockPool.peer.id != "peer2" {
|
|
||||||
t.Errorf("peer2 (TD=3) not set any more as best")
|
|
||||||
}
|
|
||||||
if blockPool.peer.td.Cmp(big.NewInt(int64(3))) != 0 {
|
|
||||||
t.Errorf("peer1 TD not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
peer2.td = 4
|
|
||||||
peer2.currentBlock = 3
|
|
||||||
best = peer2.AddPeer()
|
|
||||||
if !best {
|
|
||||||
t.Errorf("peer2 (TD=4) not accepted as best")
|
|
||||||
}
|
|
||||||
if blockPool.peer.id != "peer2" {
|
|
||||||
t.Errorf("peer2 (TD=4) not set as best")
|
|
||||||
}
|
|
||||||
if blockPool.peer.td.Cmp(big.NewInt(int64(4))) != 0 {
|
|
||||||
t.Errorf("peer2 TD not updated")
|
|
||||||
}
|
|
||||||
peer2.waitBlocksRequests(3)
|
|
||||||
|
|
||||||
peer1.td = 3
|
|
||||||
peer1.currentBlock = 2
|
|
||||||
best = peer1.AddPeer()
|
|
||||||
if best {
|
|
||||||
t.Errorf("peer1 (TD=3) should not be set as best")
|
|
||||||
}
|
|
||||||
if blockPool.peer.id == "peer1" {
|
|
||||||
t.Errorf("peer1 (TD=3) should not be set as best")
|
|
||||||
}
|
|
||||||
peer, best = blockPool.getPeer("peer1")
|
|
||||||
if peer.td.Cmp(big.NewInt(int64(3))) != 0 {
|
|
||||||
t.Errorf("peer1 TD should be updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
blockPool.RemovePeer("peer2")
|
|
||||||
peer, best = blockPool.getPeer("peer2")
|
|
||||||
if peer != nil {
|
|
||||||
t.Errorf("peer2 not removed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockPool.peer.id != "peer1" {
|
|
||||||
t.Errorf("existing peer1 (TD=3) should be set as best peer")
|
|
||||||
}
|
|
||||||
peer1.waitBlocksRequests(2)
|
|
||||||
|
|
||||||
blockPool.RemovePeer("peer1")
|
|
||||||
peer, best = blockPool.getPeer("peer1")
|
|
||||||
if peer != nil {
|
|
||||||
t.Errorf("peer1 not removed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockPool.peer.id != "peer0" {
|
|
||||||
t.Errorf("existing peer0 (TD=1) should be set as best peer")
|
|
||||||
}
|
|
||||||
peer0.waitBlocksRequests(0)
|
|
||||||
|
|
||||||
blockPool.RemovePeer("peer0")
|
|
||||||
peer, best = blockPool.getPeer("peer0")
|
|
||||||
if peer != nil {
|
|
||||||
t.Errorf("peer1 not removed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// adding back earlier peer ok
|
|
||||||
peer0.currentBlock = 3
|
|
||||||
best = peer0.AddPeer()
|
|
||||||
if !best {
|
|
||||||
t.Errorf("peer0 (TD=1) should be set as best")
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockPool.peer.id != "peer0" {
|
|
||||||
t.Errorf("peer0 (TD=1) should be set as best")
|
|
||||||
}
|
|
||||||
peer0.waitBlocksRequests(3)
|
|
||||||
|
|
||||||
blockPool.Stop()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPeerWithKnownBlock(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.refBlockChain[0] = nil
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer0 := blockPoolTester.newPeer("0", 1, 0)
|
|
||||||
peer0.AddPeer()
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
// no request on known block
|
|
||||||
peer0.checkBlockHashesRequests()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPeerWithKnownParentBlock(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.initRefBlockChain(1)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer0 := blockPoolTester.newPeer("0", 1, 1)
|
|
||||||
peer0.AddPeer()
|
|
||||||
peer0.AddBlocks(0, 1)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
peer0.checkBlocksRequests([]int{1})
|
|
||||||
peer0.checkBlockHashesRequests()
|
|
||||||
blockPoolTester.refBlockChain[1] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleChain(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(2)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
|
||||||
peer1.AddPeer()
|
|
||||||
peer1.AddBlocks(1, 2)
|
|
||||||
go peer1.AddBlockHashes(2, 1, 0)
|
|
||||||
peer1.AddBlocks(0, 1)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[2] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainConnectingWithParentHash(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(3)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(2, 3)
|
|
||||||
go peer1.AddBlockHashes(3, 2, 1)
|
|
||||||
peer1.AddBlocks(0, 1, 2)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidBlock(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(2)
|
|
||||||
blockPoolTester.refBlockChain[2] = []int{}
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(2, 3)
|
|
||||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
|
||||||
peer1.AddBlocks(0, 1, 2)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[2] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
if len(peer1.peerErrors) == 1 {
|
|
||||||
if peer1.peerErrors[0] != ErrInvalidBlock {
|
|
||||||
t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrInvalidBlock)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("expected invalid block error, got nothing %v", peer1.peerErrors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifyPoW(t *testing.T) {
|
|
||||||
t.Skip("***FIX*** This test is broken")
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(3)
|
|
||||||
first := false
|
|
||||||
blockPoolTester.blockPool.verifyPoW = func(b pow.Block) bool {
|
|
||||||
bb, _ := b.(*types.Block)
|
|
||||||
indexes := blockPoolTester.hashPool.hashesToIndexes([][]byte{bb.Hash()})
|
|
||||||
if indexes[0] == 2 && !first {
|
|
||||||
first = true
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(2, 3)
|
|
||||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
|
||||||
peer1.AddBlocks(0, 1, 2)
|
|
||||||
|
|
||||||
// blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[1] = []int{}
|
|
||||||
delete(blockPoolTester.refBlockChain, 2)
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
if len(peer1.peerErrors) == 1 {
|
|
||||||
if peer1.peerErrors[0] != ErrInvalidPoW {
|
|
||||||
t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrInvalidPoW)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("expected invalid pow error, got nothing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultiSectionChain(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(5)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
|
||||||
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(4, 5)
|
|
||||||
go peer1.AddBlockHashes(5, 4, 3)
|
|
||||||
go peer1.AddBlocks(2, 3, 4)
|
|
||||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
|
||||||
peer1.AddBlocks(0, 1, 2)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[5] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewBlocksOnPartialChain(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(7)
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
|
||||||
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(4, 5) // partially complete section
|
|
||||||
go peer1.AddBlockHashes(5, 4, 3)
|
|
||||||
peer1.AddBlocks(3, 4) // partially complete section
|
|
||||||
// peer1 found new blocks
|
|
||||||
peer1.td = 2
|
|
||||||
peer1.currentBlock = 7
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(6, 7)
|
|
||||||
go peer1.AddBlockHashes(7, 6, 5)
|
|
||||||
go peer1.AddBlocks(2, 3)
|
|
||||||
go peer1.AddBlocks(5, 6)
|
|
||||||
go peer1.AddBlockHashes(3, 2, 1, 0) // tests that hash request from known chain root is remembered
|
|
||||||
peer1.AddBlocks(0, 1, 2)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[7] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPeerSwitchUp(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(7)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 6)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 7)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(5, 6)
|
|
||||||
go peer1.AddBlockHashes(6, 5, 4, 3) //
|
|
||||||
peer1.AddBlocks(2, 3) // section partially complete, block 3 will be preserved after peer demoted
|
|
||||||
peer2.AddPeer() // peer2 is promoted as best peer, peer1 is demoted
|
|
||||||
go peer2.AddBlocks(6, 7)
|
|
||||||
go peer2.AddBlockHashes(7, 6) //
|
|
||||||
go peer2.AddBlocks(4, 5) // tests that block request for earlier section is remembered
|
|
||||||
go peer1.AddBlocks(3, 4) // tests that connecting section by demoted peer is remembered and blocks are accepted from demoted peer
|
|
||||||
go peer2.AddBlockHashes(3, 2, 1, 0) // tests that known chain section is activated, hash requests from 3 is remembered
|
|
||||||
peer2.AddBlocks(0, 1, 2) // final blocks linking to blockchain sent
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[7] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPeerSwitchDown(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(6)
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 4)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer2.AddPeer()
|
|
||||||
peer2.AddBlocks(5, 6) // partially complete, section will be preserved
|
|
||||||
go peer2.AddBlockHashes(6, 5, 4) //
|
|
||||||
peer2.AddBlocks(4, 5) //
|
|
||||||
blockPool.RemovePeer("peer2") // peer2 disconnects
|
|
||||||
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
|
||||||
go peer1.AddBlockHashes(4, 3, 2, 1, 0) //
|
|
||||||
go peer1.AddBlocks(3, 4) // tests that section set by demoted peer is remembered and blocks are accepted , this connects the chain sections together
|
|
||||||
peer1.AddBlocks(0, 1, 2, 3)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[6] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPeerCompleteSectionSwitchDown(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(6)
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 4)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer2.AddPeer()
|
|
||||||
peer2.AddBlocks(5, 6) // partially complete, section will be preserved
|
|
||||||
go peer2.AddBlockHashes(6, 5, 4) //
|
|
||||||
peer2.AddBlocks(3, 4, 5) // complete section
|
|
||||||
blockPool.RemovePeer("peer2") // peer2 disconnects
|
|
||||||
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
|
||||||
peer1.AddBlockHashes(4, 3, 2, 1, 0) // tests that hash request are directly connecting if the head block exists
|
|
||||||
peer1.AddBlocks(0, 1, 2, 3)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[6] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPeerSwitchBack(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(8)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 2, 11)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 1, 8)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer2.AddPeer()
|
|
||||||
go peer2.AddBlocks(7, 8)
|
|
||||||
go peer2.AddBlockHashes(8, 7, 6)
|
|
||||||
go peer2.AddBlockHashes(6, 5, 4)
|
|
||||||
peer2.AddBlocks(4, 5) // section partially complete
|
|
||||||
peer1.AddPeer() // peer1 is promoted as best peer
|
|
||||||
go peer1.AddBlocks(10, 11) //
|
|
||||||
peer1.AddBlockHashes(11, 10) // only gives useless results
|
|
||||||
blockPool.RemovePeer("peer1") // peer1 disconnects
|
|
||||||
go peer2.AddBlockHashes(4, 3, 2, 1, 0) // tests that asking for hashes from 4 is remembered
|
|
||||||
go peer2.AddBlocks(3, 4, 5, 6, 7, 8) // tests that section 4, 5, 6 and 7, 8 are remembered for missing blocks
|
|
||||||
peer2.AddBlocks(0, 1, 2, 3)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[8] = []int{}
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestForkSimple(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(9)
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
||||||
delete(blockPoolTester.refBlockChain, 6)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(8, 9)
|
|
||||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
|
||||||
peer1.AddBlocks(1, 2, 3, 7, 8)
|
|
||||||
peer2.AddPeer() // peer2 is promoted as best peer
|
|
||||||
go peer2.AddBlocks(5, 6) //
|
|
||||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // fork on 3 -> 4 (earlier child: 7)
|
|
||||||
go peer2.AddBlocks(1, 2, 3, 4, 5)
|
|
||||||
go peer2.AddBlockHashes(2, 1, 0)
|
|
||||||
peer2.AddBlocks(0, 1, 2)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[6] = []int{}
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{4}
|
|
||||||
delete(blockPoolTester.refBlockChain, 7)
|
|
||||||
delete(blockPoolTester.refBlockChain, 8)
|
|
||||||
delete(blockPoolTester.refBlockChain, 9)
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestForkSwitchBackByNewBlocks(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(11)
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
||||||
delete(blockPoolTester.refBlockChain, 6)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer1.AddPeer()
|
|
||||||
peer1.AddBlocks(8, 9) //
|
|
||||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2) //
|
|
||||||
peer1.AddBlocks(7, 8) // partial section
|
|
||||||
peer2.AddPeer() //
|
|
||||||
peer2.AddBlocks(5, 6) //
|
|
||||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
|
||||||
peer2.AddBlocks(1, 2, 3, 4, 5) //
|
|
||||||
|
|
||||||
// peer1 finds new blocks
|
|
||||||
peer1.td = 3
|
|
||||||
peer1.currentBlock = 11
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(10, 11)
|
|
||||||
go peer1.AddBlockHashes(11, 10, 9)
|
|
||||||
peer1.AddBlocks(9, 10)
|
|
||||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered
|
|
||||||
go peer1.AddBlockHashes(2, 1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
|
|
||||||
peer1.AddBlocks(0, 1)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[11] = []int{}
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{7}
|
|
||||||
delete(blockPoolTester.refBlockChain, 6)
|
|
||||||
delete(blockPoolTester.refBlockChain, 5)
|
|
||||||
delete(blockPoolTester.refBlockChain, 4)
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestForkSwitchBackByPeerSwitchBack(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(9)
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
||||||
delete(blockPoolTester.refBlockChain, 6)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(8, 9)
|
|
||||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
|
||||||
peer1.AddBlocks(7, 8)
|
|
||||||
peer2.AddPeer()
|
|
||||||
go peer2.AddBlocks(5, 6) //
|
|
||||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
|
||||||
peer2.AddBlocks(2, 3, 4, 5) //
|
|
||||||
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
|
||||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered and orphan section relinks to existing parent block
|
|
||||||
go peer1.AddBlocks(1, 2) //
|
|
||||||
go peer1.AddBlockHashes(2, 1, 0) //
|
|
||||||
peer1.AddBlocks(0, 1)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[9] = []int{}
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{7}
|
|
||||||
delete(blockPoolTester.refBlockChain, 6)
|
|
||||||
delete(blockPoolTester.refBlockChain, 5)
|
|
||||||
delete(blockPoolTester.refBlockChain, 4)
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestForkCompleteSectionSwitchBackByPeerSwitchBack(t *testing.T) {
|
|
||||||
logInit()
|
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
||||||
blockPoolTester.blockChain[0] = nil
|
|
||||||
blockPoolTester.initRefBlockChain(9)
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
||||||
delete(blockPoolTester.refBlockChain, 6)
|
|
||||||
|
|
||||||
blockPool.Start()
|
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
|
||||||
|
|
||||||
peer1.AddPeer()
|
|
||||||
go peer1.AddBlocks(8, 9)
|
|
||||||
go peer1.AddBlockHashes(9, 8, 7)
|
|
||||||
peer1.AddBlocks(3, 7, 8) // make sure this section is complete
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
go peer1.AddBlockHashes(7, 3, 2) // block 3/7 is section boundary
|
|
||||||
peer1.AddBlocks(2, 3) // partially complete sections block 2 missing
|
|
||||||
peer2.AddPeer() //
|
|
||||||
go peer2.AddBlocks(5, 6) //
|
|
||||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
|
||||||
peer2.AddBlocks(2, 3, 4, 5) // block 2 still missing.
|
|
||||||
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
|
||||||
// peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered even though section process completed
|
|
||||||
go peer1.AddBlockHashes(2, 1, 0) //
|
|
||||||
peer1.AddBlocks(0, 1, 2)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
|
||||||
blockPool.Stop()
|
|
||||||
blockPoolTester.refBlockChain[9] = []int{}
|
|
||||||
blockPoolTester.refBlockChain[3] = []int{7}
|
|
||||||
delete(blockPoolTester.refBlockChain, 6)
|
|
||||||
delete(blockPoolTester.refBlockChain, 5)
|
|
||||||
delete(blockPoolTester.refBlockChain, 4)
|
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
||||||
|
|
||||||
}
|
|
72
eth/error.go
72
eth/error.go
@ -1,72 +0,0 @@
|
|||||||
package eth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrMsgTooLarge = iota
|
|
||||||
ErrDecode
|
|
||||||
ErrInvalidMsgCode
|
|
||||||
ErrProtocolVersionMismatch
|
|
||||||
ErrNetworkIdMismatch
|
|
||||||
ErrGenesisBlockMismatch
|
|
||||||
ErrNoStatusMsg
|
|
||||||
ErrExtraStatusMsg
|
|
||||||
ErrInvalidBlock
|
|
||||||
ErrInvalidPoW
|
|
||||||
ErrUnrequestedBlock
|
|
||||||
ErrInsufficientChainInfo
|
|
||||||
)
|
|
||||||
|
|
||||||
var errorToString = map[int]string{
|
|
||||||
ErrMsgTooLarge: "Message too long",
|
|
||||||
ErrDecode: "Invalid message",
|
|
||||||
ErrInvalidMsgCode: "Invalid message code",
|
|
||||||
ErrProtocolVersionMismatch: "Protocol version mismatch",
|
|
||||||
ErrNetworkIdMismatch: "NetworkId mismatch",
|
|
||||||
ErrGenesisBlockMismatch: "Genesis block mismatch",
|
|
||||||
ErrNoStatusMsg: "No status message",
|
|
||||||
ErrExtraStatusMsg: "Extra status message",
|
|
||||||
ErrInvalidBlock: "Invalid block",
|
|
||||||
ErrInvalidPoW: "Invalid PoW",
|
|
||||||
ErrUnrequestedBlock: "Unrequested block",
|
|
||||||
ErrInsufficientChainInfo: "Insufficient chain info",
|
|
||||||
}
|
|
||||||
|
|
||||||
type protocolError struct {
|
|
||||||
Code int
|
|
||||||
fatal bool
|
|
||||||
message string
|
|
||||||
format string
|
|
||||||
params []interface{}
|
|
||||||
// size int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newProtocolError(code int, format string, params ...interface{}) *protocolError {
|
|
||||||
return &protocolError{Code: code, format: format, params: params}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProtocolError(code int, format string, params ...interface{}) (err *protocolError) {
|
|
||||||
err = newProtocolError(code, format, params...)
|
|
||||||
// report(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self protocolError) Error() (message string) {
|
|
||||||
if len(message) == 0 {
|
|
||||||
var ok bool
|
|
||||||
self.message, ok = errorToString[self.Code]
|
|
||||||
if !ok {
|
|
||||||
panic("invalid error code")
|
|
||||||
}
|
|
||||||
if self.format != "" {
|
|
||||||
self.message += ": " + fmt.Sprintf(self.format, self.params...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return self.message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *protocolError) Fatal() bool {
|
|
||||||
return self.fatal
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/errs"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
@ -17,6 +18,8 @@ const (
|
|||||||
NetworkId = 0
|
NetworkId = 0
|
||||||
ProtocolLength = uint64(8)
|
ProtocolLength = uint64(8)
|
||||||
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
||||||
|
maxHashes = 256
|
||||||
|
maxBlocks = 64
|
||||||
)
|
)
|
||||||
|
|
||||||
// eth protocol message codes
|
// eth protocol message codes
|
||||||
@ -31,6 +34,28 @@ const (
|
|||||||
NewBlockMsg
|
NewBlockMsg
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrMsgTooLarge = iota
|
||||||
|
ErrDecode
|
||||||
|
ErrInvalidMsgCode
|
||||||
|
ErrProtocolVersionMismatch
|
||||||
|
ErrNetworkIdMismatch
|
||||||
|
ErrGenesisBlockMismatch
|
||||||
|
ErrNoStatusMsg
|
||||||
|
ErrExtraStatusMsg
|
||||||
|
)
|
||||||
|
|
||||||
|
var errorToString = map[int]string{
|
||||||
|
ErrMsgTooLarge: "Message too long",
|
||||||
|
ErrDecode: "Invalid message",
|
||||||
|
ErrInvalidMsgCode: "Invalid message code",
|
||||||
|
ErrProtocolVersionMismatch: "Protocol version mismatch",
|
||||||
|
ErrNetworkIdMismatch: "NetworkId mismatch",
|
||||||
|
ErrGenesisBlockMismatch: "Genesis block mismatch",
|
||||||
|
ErrNoStatusMsg: "No status message",
|
||||||
|
ErrExtraStatusMsg: "Extra status message",
|
||||||
|
}
|
||||||
|
|
||||||
// ethProtocol represents the ethereum wire protocol
|
// ethProtocol represents the ethereum wire protocol
|
||||||
// instance is running on each peer
|
// instance is running on each peer
|
||||||
type ethProtocol struct {
|
type ethProtocol struct {
|
||||||
@ -40,6 +65,7 @@ type ethProtocol struct {
|
|||||||
peer *p2p.Peer
|
peer *p2p.Peer
|
||||||
id string
|
id string
|
||||||
rw p2p.MsgReadWriter
|
rw p2p.MsgReadWriter
|
||||||
|
errors *errs.Errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// backend is the interface the ethereum protocol backend should implement
|
// backend is the interface the ethereum protocol backend should implement
|
||||||
@ -58,7 +84,7 @@ type chainManager interface {
|
|||||||
type blockPool interface {
|
type blockPool interface {
|
||||||
AddBlockHashes(next func() ([]byte, bool), peerId string)
|
AddBlockHashes(next func() ([]byte, bool), peerId string)
|
||||||
AddBlock(block *types.Block, peerId string)
|
AddBlock(block *types.Block, peerId string)
|
||||||
AddPeer(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool)
|
AddPeer(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(*errs.Error)) (best bool)
|
||||||
RemovePeer(peerId string)
|
RemovePeer(peerId string)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +94,6 @@ type newBlockMsgData struct {
|
|||||||
TD *big.Int
|
TD *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxHashes = 255
|
|
||||||
|
|
||||||
type getBlockHashesMsgData struct {
|
type getBlockHashesMsgData struct {
|
||||||
Hash []byte
|
Hash []byte
|
||||||
Amount uint64
|
Amount uint64
|
||||||
@ -99,6 +123,10 @@ func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPoo
|
|||||||
blockPool: blockPool,
|
blockPool: blockPool,
|
||||||
rw: rw,
|
rw: rw,
|
||||||
peer: peer,
|
peer: peer,
|
||||||
|
errors: &errs.Errors{
|
||||||
|
Package: "ETH",
|
||||||
|
Errors: errorToString,
|
||||||
|
},
|
||||||
id: fmt.Sprintf("%x", id[:8]),
|
id: fmt.Sprintf("%x", id[:8]),
|
||||||
}
|
}
|
||||||
err = self.handleStatus()
|
err = self.handleStatus()
|
||||||
@ -145,7 +173,6 @@ func (self *ethProtocol) handle() error {
|
|||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//request.Amount = uint64(math.Min(float64(maxHashes), float64(request.Amount)))
|
|
||||||
if request.Amount > maxHashes {
|
if request.Amount > maxHashes {
|
||||||
request.Amount = maxHashes
|
request.Amount = maxHashes
|
||||||
}
|
}
|
||||||
@ -153,7 +180,6 @@ func (self *ethProtocol) handle() error {
|
|||||||
return p2p.EncodeMsg(self.rw, BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
|
return p2p.EncodeMsg(self.rw, BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||||
|
|
||||||
case BlockHashesMsg:
|
case BlockHashesMsg:
|
||||||
// TODO: redo using lazy decode , this way very inefficient on known chains
|
|
||||||
msgStream := rlp.NewStream(msg.Payload)
|
msgStream := rlp.NewStream(msg.Payload)
|
||||||
var err error
|
var err error
|
||||||
var i int
|
var i int
|
||||||
@ -191,7 +217,7 @@ func (self *ethProtocol) handle() error {
|
|||||||
if block != nil {
|
if block != nil {
|
||||||
blocks = append(blocks, block)
|
blocks = append(blocks, block)
|
||||||
}
|
}
|
||||||
if i == blockHashesBatchSize {
|
if i == maxBlocks {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +244,7 @@ func (self *ethProtocol) handle() error {
|
|||||||
}
|
}
|
||||||
hash := request.Block.Hash()
|
hash := request.Block.Hash()
|
||||||
// to simplify backend interface adding a new block
|
// to simplify backend interface adding a new block
|
||||||
// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
|
// uses AddPeer followed by AddBlock only if peer is the best peer
|
||||||
// (or selected as new best peer)
|
// (or selected as new best peer)
|
||||||
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
|
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
|
||||||
self.blockPool.AddBlock(request.Block, self.id)
|
self.blockPool.AddBlock(request.Block, self.id)
|
||||||
@ -277,7 +303,7 @@ func (self *ethProtocol) handleStatus() error {
|
|||||||
|
|
||||||
_, _, genesisBlock := self.chainManager.Status()
|
_, _, genesisBlock := self.chainManager.Status()
|
||||||
|
|
||||||
if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 {
|
if !bytes.Equal(status.GenesisBlock, genesisBlock) {
|
||||||
return self.protoError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
|
return self.protoError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,8 +323,8 @@ func (self *ethProtocol) handleStatus() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *ethProtocol) requestBlockHashes(from []byte) error {
|
func (self *ethProtocol) requestBlockHashes(from []byte) error {
|
||||||
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
|
self.peer.Debugf("fetching hashes (%d) %x...\n", maxHashes, from[0:4])
|
||||||
return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
|
return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(maxHashes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
|
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
|
||||||
@ -306,26 +332,17 @@ func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
|
|||||||
return p2p.EncodeMsg(self.rw, GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
|
return p2p.EncodeMsg(self.rw, GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
|
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *errs.Error) {
|
||||||
err = ProtocolError(code, format, params...)
|
err = self.errors.New(code, format, params...)
|
||||||
if err.Fatal() {
|
err.Log(self.peer.Logger)
|
||||||
self.peer.Errorln("err %v", err)
|
|
||||||
// disconnect
|
|
||||||
} else {
|
|
||||||
self.peer.Debugf("fyi %v", err)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) {
|
func (self *ethProtocol) protoErrorDisconnect(err *errs.Error) {
|
||||||
err := ProtocolError(code, format, params...)
|
err.Log(self.peer.Logger)
|
||||||
if err.Fatal() {
|
if err.Fatal() {
|
||||||
self.peer.Errorln("err %v", err)
|
self.peer.Disconnect(p2p.DiscSubprotocolError)
|
||||||
// disconnect
|
|
||||||
} else {
|
|
||||||
self.peer.Debugf("fyi %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ethProtocol) propagateTxs() {
|
func (self *ethProtocol) propagateTxs() {
|
||||||
|
@ -11,13 +11,23 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/errs"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
ethlogger "github.com/ethereum/go-ethereum/logger"
|
ethlogger "github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
|
var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
|
||||||
|
|
||||||
|
var ini = false
|
||||||
|
|
||||||
|
func logInit() {
|
||||||
|
if !ini {
|
||||||
|
ethlogger.AddLogSystem(logsys)
|
||||||
|
ini = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type testMsgReadWriter struct {
|
type testMsgReadWriter struct {
|
||||||
in chan p2p.Msg
|
in chan p2p.Msg
|
||||||
@ -64,7 +74,7 @@ type testChainManager struct {
|
|||||||
type testBlockPool struct {
|
type testBlockPool struct {
|
||||||
addBlockHashes func(next func() ([]byte, bool), peerId string)
|
addBlockHashes func(next func() ([]byte, bool), peerId string)
|
||||||
addBlock func(block *types.Block, peerId string) (err error)
|
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)
|
addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(*errs.Error)) (best bool)
|
||||||
removePeer func(peerId string)
|
removePeer func(peerId string)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +126,7 @@ func (self *testBlockPool) AddBlock(block *types.Block, peerId string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
func (self *testBlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(*errs.Error)) (best bool) {
|
||||||
if self.addPeer != nil {
|
if self.addPeer != nil {
|
||||||
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, peerError)
|
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, peerError)
|
||||||
}
|
}
|
||||||
@ -169,7 +179,7 @@ func (self *ethProtocolTester) checkError(expCode int, delay time.Duration) (err
|
|||||||
self.t.Errorf("no error after %v, expected %v", delay, expCode)
|
self.t.Errorf("no error after %v, expected %v", delay, expCode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
perr, ok := err.(*protocolError)
|
perr, ok := err.(*errs.Error)
|
||||||
if ok && perr != nil {
|
if ok && perr != nil {
|
||||||
if code := perr.Code; code != expCode {
|
if code := perr.Code; code != expCode {
|
||||||
self.t.Errorf("expected protocol error (code %v), got %v (%v)", expCode, code, err)
|
self.t.Errorf("expected protocol error (code %v), got %v (%v)", expCode, code, err)
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
= 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
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
#!/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
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,20 +0,0 @@
|
|||||||
#!/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'
|
|
@ -1,53 +0,0 @@
|
|||||||
#!/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 +0,0 @@
|
|||||||
../chains/01.chain
|
|
@ -1,13 +0,0 @@
|
|||||||
#!/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 +0,0 @@
|
|||||||
../chains/02.chain
|
|
@ -1,18 +0,0 @@
|
|||||||
#!/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 +0,0 @@
|
|||||||
../chains/01.chain
|
|
@ -1,19 +0,0 @@
|
|||||||
#!/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
|
|
||||||
P12ID=$PID
|
|
||||||
test_node $NAME "" -loglevel 5 $JSFILE
|
|
||||||
sleep 0.3
|
|
||||||
kill $P12ID
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
../chains/12k.chain
|
|
@ -1,14 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
TIMEOUT=12
|
|
||||||
|
|
||||||
cat >> $JSFILE <<EOF
|
|
||||||
eth.addPeer("localhost:30311");
|
|
||||||
sleep(10000);
|
|
||||||
eth.export("$CHAIN_TEST");
|
|
||||||
EOF
|
|
||||||
|
|
||||||
peer 11 12k
|
|
||||||
sleep 2
|
|
||||||
test_node $NAME "" -loglevel 5 $JSFILE
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
#!/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'
|
|
@ -1,20 +0,0 @@
|
|||||||
#!/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
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
function log(text) {
|
|
||||||
console.log("[JS TEST SCRIPT] " + text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sleep(seconds) {
|
|
||||||
var now = new Date().getTime();
|
|
||||||
while(new Date().getTime() < now + seconds){}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
#!/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 -port 303$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 args $ARGS ${@:3}"
|
|
||||||
$ETH $ARGS ${@:3} &
|
|
||||||
PID=$!
|
|
||||||
PIDS="$PIDS $PID"
|
|
||||||
}
|
|
||||||
|
|
||||||
function peer {
|
|
||||||
test_node $@ -loglevel 5 -logfile debug.log -maxpeer 1 -dial=false
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user