integrate blockpool into eth
- remove blockpool code - remove blockpool integration test (kinda embarrassing) - remove errors.go
This commit is contained in:
		@@ -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,7 +123,11 @@ func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPoo
 | 
				
			|||||||
		blockPool:    blockPool,
 | 
							blockPool:    blockPool,
 | 
				
			||||||
		rw:           rw,
 | 
							rw:           rw,
 | 
				
			||||||
		peer:         peer,
 | 
							peer:         peer,
 | 
				
			||||||
		id:           fmt.Sprintf("%x", id[:8]),
 | 
							errors: &errs.Errors{
 | 
				
			||||||
 | 
								Package: "ETH",
 | 
				
			||||||
 | 
								Errors:  errorToString,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							id: fmt.Sprintf("%x", id[:8]),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = self.handleStatus()
 | 
						err = self.handleStatus()
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
@@ -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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user