|
|
|
@ -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) |
|
|
|
|
|
|
|
|
|
} |