mirror of https://github.com/status-im/op-geth.git
check and penalise td misreporting
- add ErrIncorrectTD - checkTD called after insertChain successful - fix tests, use blockPoolTester.tds to map block index to TD
This commit is contained in:
parent
a9926a289d
commit
137a9c9365
|
@ -62,6 +62,7 @@ const (
|
|||
ErrUnrequestedBlock
|
||||
ErrInsufficientChainInfo
|
||||
ErrIdleTooLong
|
||||
ErrIncorrectTD
|
||||
)
|
||||
|
||||
var errorToString = map[int]string{
|
||||
|
@ -70,6 +71,7 @@ var errorToString = map[int]string{
|
|||
ErrUnrequestedBlock: "Unrequested block",
|
||||
ErrInsufficientChainInfo: "Insufficient chain info",
|
||||
ErrIdleTooLong: "Idle too long",
|
||||
ErrIncorrectTD: "Incorrect Total Difficulty",
|
||||
}
|
||||
|
||||
// init initialises all your laundry
|
||||
|
@ -373,6 +375,7 @@ func (self *BlockPool) AddBlockHashes(next func() (common.Hash, bool), peerId st
|
|||
block: bestpeer.currentBlock,
|
||||
hashBy: peerId,
|
||||
blockBy: peerId,
|
||||
td: bestpeer.td,
|
||||
}
|
||||
// nodes is a list of nodes in one section ordered top-bottom (old to young)
|
||||
nodes = append(nodes, node)
|
||||
|
@ -729,6 +732,17 @@ LOOP:
|
|||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) checkTD(nodes ...*node) {
|
||||
for _, n := range nodes {
|
||||
if n.td != nil {
|
||||
plog.DebugDetailf("peer td %v =?= block td %v", n.td, n.block.Td)
|
||||
if n.td.Cmp(n.block.Td) != 0 {
|
||||
self.peers.peerError(n.blockBy, ErrIncorrectTD, "on block %x", n.hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must run in separate go routine, otherwise
|
||||
// switchpeer -> activateChain -> activate deadlocks on section process select and peers.lock
|
||||
func (self *BlockPool) requestBlocks(attempts int, hashes []common.Hash) {
|
||||
|
|
|
@ -51,9 +51,11 @@ func TestPeerPromotionByOptionalTdOnBlock(t *testing.T) {
|
|||
blockPoolTester.initRefBlockChain(4)
|
||||
peer0 := blockPoolTester.newPeer("peer0", 2, 2)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 1)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 3, 4)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 4, 4)
|
||||
|
||||
blockPool.Start()
|
||||
blockPoolTester.tds = make(map[int]int)
|
||||
blockPoolTester.tds[3] = 3
|
||||
|
||||
// pool
|
||||
peer0.AddPeer()
|
||||
|
@ -94,7 +96,7 @@ func TestSimpleChain(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 2, 2)
|
||||
peer1.AddPeer()
|
||||
peer1.serveBlocks(1, 2)
|
||||
go peer1.serveBlockHashes(2, 1, 0)
|
||||
|
@ -114,7 +116,7 @@ func TestChainConnectingWithParentHash(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 3, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(2, 3)
|
||||
go peer1.serveBlockHashes(3, 2, 1)
|
||||
|
@ -134,7 +136,7 @@ func TestMultiSectionChain(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 5, 5)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(4, 5)
|
||||
|
@ -156,14 +158,16 @@ func TestNewBlocksOnPartialChain(t *testing.T) {
|
|||
blockPoolTester.initRefBlockChain(7)
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 5, 5)
|
||||
blockPoolTester.tds = make(map[int]int)
|
||||
blockPoolTester.tds[5] = 5
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(4, 5) // partially complete section
|
||||
go peer1.serveBlockHashes(5, 4, 3)
|
||||
peer1.serveBlocks(3, 4) // partially complete section
|
||||
// peer1 found new blocks
|
||||
peer1.td = 2
|
||||
peer1.td = 7
|
||||
peer1.currentBlock = 7
|
||||
peer1.AddPeer()
|
||||
peer1.sendBlocks(6, 7)
|
||||
|
@ -188,16 +192,15 @@ func TestPeerSwitchUp(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 6)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 7)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 6, 6)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 7, 7)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(5, 6)
|
||||
go peer1.serveBlockHashes(6, 5, 4, 3) //
|
||||
peer1.serveBlocks(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.serveBlocks(6, 7)
|
||||
// go peer2.serveBlockHashes(7, 6) //
|
||||
go peer2.serveBlocks(6, 7) //
|
||||
go peer2.serveBlocks(4, 5) // tests that block request for earlier section is remembered
|
||||
go peer1.serveBlocks(3, 4) // tests that connecting section by demoted peer is remembered and blocks are accepted from demoted peer
|
||||
go peer2.serveBlockHashes(3, 2, 1, 0) // tests that known chain section is activated, hash requests from 3 is remembered
|
||||
|
@ -216,8 +219,8 @@ func TestPeerSwitchDownOverlapSectionWithoutRootBlock(t *testing.T) {
|
|||
blockPoolTester.initRefBlockChain(6)
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 4)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 4, 4)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 6, 6)
|
||||
|
||||
peer2.AddPeer()
|
||||
peer2.serveBlocks(5, 6) // partially complete, section will be preserved
|
||||
|
@ -242,8 +245,8 @@ func TestPeerSwitchDownOverlapSectionWithRootBlock(t *testing.T) {
|
|||
blockPoolTester.initRefBlockChain(6)
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 4)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 4, 4)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 6, 6)
|
||||
|
||||
peer2.AddPeer()
|
||||
peer2.serveBlocks(5, 6) // partially complete, section will be preserved
|
||||
|
@ -269,8 +272,8 @@ func TestPeerSwitchDownDisjointSection(t *testing.T) {
|
|||
blockPoolTester.initRefBlockChain(3)
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 3, 3)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 6, 6)
|
||||
|
||||
peer2.AddPeer()
|
||||
peer2.serveBlocks(5, 6) // partially complete, section will be preserved
|
||||
|
@ -297,8 +300,8 @@ func TestPeerSwitchBack(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 2, 11)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 1, 8)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 11, 11)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 8, 8)
|
||||
|
||||
peer2.AddPeer()
|
||||
go peer2.serveBlocks(7, 8)
|
||||
|
@ -328,9 +331,10 @@ func TestForkSimple(t *testing.T) {
|
|||
delete(blockPoolTester.refBlockChain, 6)
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
blockPoolTester.tds = make(map[int]int)
|
||||
blockPoolTester.tds[6] = 10
|
||||
peer1 := blockPoolTester.newPeer("peer1", 9, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 10, 6)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(8, 9)
|
||||
|
@ -363,9 +367,10 @@ func TestForkSwitchBackByNewBlocks(t *testing.T) {
|
|||
delete(blockPoolTester.refBlockChain, 6)
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
blockPoolTester.tds = make(map[int]int)
|
||||
blockPoolTester.tds[6] = 10
|
||||
peer1 := blockPoolTester.newPeer("peer1", 9, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 10, 6)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(8, 9) //
|
||||
|
@ -378,7 +383,7 @@ func TestForkSwitchBackByNewBlocks(t *testing.T) {
|
|||
peer2.serveBlocks(1, 2, 3, 4, 5) //
|
||||
|
||||
// peer1 finds new blocks
|
||||
peer1.td = 3
|
||||
peer1.td = 11
|
||||
peer1.currentBlock = 11
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(10, 11)
|
||||
|
@ -410,8 +415,14 @@ func TestForkSwitchBackByPeerSwitchBack(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
blockPoolTester.tds = make(map[int]int)
|
||||
blockPoolTester.tds[6] = 10
|
||||
|
||||
blockPoolTester.tds = make(map[int]int)
|
||||
blockPoolTester.tds[6] = 10
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 9, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 10, 6)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(8, 9)
|
||||
|
@ -448,8 +459,11 @@ func TestForkCompleteSectionSwitchBackByPeerSwitchBack(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
blockPoolTester.tds = make(map[int]int)
|
||||
blockPoolTester.tds[6] = 10
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 9, 9)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 10, 6)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(8, 9)
|
||||
|
|
|
@ -40,6 +40,7 @@ type blockPoolTester struct {
|
|||
blockPool *BlockPool
|
||||
t *testing.T
|
||||
chainEvents *event.TypeMux
|
||||
tds map[int]int
|
||||
}
|
||||
|
||||
func newTestBlockPool(t *testing.T) (hashPool *test.TestHashPool, blockPool *BlockPool, b *blockPoolTester) {
|
||||
|
@ -84,6 +85,14 @@ func (self *blockPoolTester) insertChain(blocks types.Blocks) error {
|
|||
var ok bool
|
||||
for _, block := range blocks {
|
||||
child = self.hashPool.HashesToIndexes([]common.Hash{block.Hash()})[0]
|
||||
var td int
|
||||
if self.tds != nil {
|
||||
td, ok = self.tds[child]
|
||||
}
|
||||
if !ok {
|
||||
td = child
|
||||
}
|
||||
block.Td = big.NewInt(int64(td))
|
||||
_, ok = self.blockChain[child]
|
||||
if ok {
|
||||
fmt.Printf("block %v already in blockchain\n", child)
|
||||
|
|
|
@ -93,7 +93,6 @@ func TestUnrequestedBlock(t *testing.T) {
|
|||
peer1.AddPeer()
|
||||
peer1.sendBlocks(1, 2)
|
||||
|
||||
// blockPool.Wait(waitTimeout)
|
||||
blockPool.Stop()
|
||||
if len(peer1.peerErrors) == 1 {
|
||||
if peer1.peerErrors[0] != ErrUnrequestedBlock {
|
||||
|
@ -124,6 +123,33 @@ func TestErrInsufficientChainInfo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIncorrectTD(t *testing.T) {
|
||||
test.LogInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPoolTester.initRefBlockChain(3)
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.serveBlocks(2, 3)
|
||||
go peer1.serveBlockHashes(3, 2, 1, 0)
|
||||
peer1.serveBlocks(0, 1, 2)
|
||||
|
||||
blockPool.Wait(waitTimeout)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[3] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
if len(peer1.peerErrors) == 1 {
|
||||
if peer1.peerErrors[0] != ErrIncorrectTD {
|
||||
t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrIncorrectTD)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("expected %v error, got %v", ErrIncorrectTD, peer1.peerErrors)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPeerSuspension(t *testing.T) {
|
||||
test.LogInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
|
|
|
@ -452,8 +452,12 @@ func (self *peer) getBlockHashes() {
|
|||
self.addError(ErrInvalidBlock, "%v", err)
|
||||
self.bp.status.badPeers[self.id]++
|
||||
} else {
|
||||
if self.currentBlock.Td != nil {
|
||||
if self.td.Cmp(self.currentBlock.Td) != 0 {
|
||||
self.addError(ErrIncorrectTD, "on block %x", self.currentBlockHash)
|
||||
}
|
||||
}
|
||||
headKey := self.parentHash.Str()
|
||||
height := self.bp.status.chain[headKey] + 1
|
||||
self.bp.status.chain[self.currentBlockHash.Str()] = height
|
||||
if height > self.bp.status.values.LongestChain {
|
||||
self.bp.status.values.LongestChain = height
|
||||
|
@ -471,6 +475,7 @@ func (self *peer) getBlockHashes() {
|
|||
block: self.currentBlock,
|
||||
hashBy: self.id,
|
||||
blockBy: self.id,
|
||||
td: self.td,
|
||||
}
|
||||
self.bp.newSection([]*node{n}).activate(self)
|
||||
} else {
|
||||
|
|
|
@ -15,9 +15,9 @@ import (
|
|||
func TestAddPeer(t *testing.T) {
|
||||
test.LogInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
peer0 := blockPoolTester.newPeer("peer0", 1, 0)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 2, 1)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 3, 2)
|
||||
peer0 := blockPoolTester.newPeer("peer0", 1, 1)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 2, 2)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 3, 3)
|
||||
var bestpeer *peer
|
||||
|
||||
blockPool.Start()
|
||||
|
@ -38,7 +38,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peers.best.id != "peer2" {
|
||||
t.Errorf("peer2 (TD=3) not set as best")
|
||||
}
|
||||
peer2.waitBlocksRequests(2)
|
||||
peer2.waitBlocksRequests(3)
|
||||
|
||||
best = peer1.AddPeer()
|
||||
if best {
|
||||
|
@ -52,7 +52,7 @@ func TestAddPeer(t *testing.T) {
|
|||
}
|
||||
|
||||
peer2.td = 4
|
||||
peer2.currentBlock = 3
|
||||
peer2.currentBlock = 4
|
||||
best = peer2.AddPeer()
|
||||
if !best {
|
||||
t.Errorf("peer2 (TD=4) not accepted as best")
|
||||
|
@ -63,10 +63,10 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peers.best.td.Cmp(big.NewInt(int64(4))) != 0 {
|
||||
t.Errorf("peer2 TD not updated")
|
||||
}
|
||||
peer2.waitBlocksRequests(3)
|
||||
peer2.waitBlocksRequests(4)
|
||||
|
||||
peer1.td = 3
|
||||
peer1.currentBlock = 2
|
||||
peer1.currentBlock = 3
|
||||
best = peer1.AddPeer()
|
||||
if best {
|
||||
t.Errorf("peer1 (TD=3) should not be set as best")
|
||||
|
@ -88,7 +88,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peers.best.id != "peer1" {
|
||||
t.Errorf("existing peer1 (TD=3) should be set as best peer")
|
||||
}
|
||||
peer1.waitBlocksRequests(2)
|
||||
peer1.waitBlocksRequests(3)
|
||||
|
||||
blockPool.RemovePeer("peer1")
|
||||
bestpeer, best = blockPool.peers.getPeer("peer1")
|
||||
|
@ -99,7 +99,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peers.best.id != "peer0" {
|
||||
t.Errorf("existing peer0 (TD=1) should be set as best peer")
|
||||
}
|
||||
peer0.waitBlocksRequests(0)
|
||||
peer0.waitBlocksRequests(1)
|
||||
|
||||
blockPool.RemovePeer("peer0")
|
||||
bestpeer, best = blockPool.peers.getPeer("peer0")
|
||||
|
|
|
@ -83,9 +83,9 @@ func (self *BlockPool) newSection(nodes []*node) *section {
|
|||
offC: make(chan bool),
|
||||
}
|
||||
|
||||
for i, node := range nodes {
|
||||
entry := &entry{node: node, section: sec, index: &index{i}}
|
||||
self.set(node.hash, entry)
|
||||
for i, n := range nodes {
|
||||
entry := &entry{node: n, section: sec, index: &index{i}}
|
||||
self.set(n.hash, entry)
|
||||
}
|
||||
|
||||
plog.DebugDetailf("[%s] setup section process", sectionhex(sec))
|
||||
|
@ -104,20 +104,22 @@ func (self *section) addSectionToBlockChain(p *peer) {
|
|||
self.bp.wg.Done()
|
||||
}()
|
||||
|
||||
var node *node
|
||||
var nodes []*node
|
||||
var n *node
|
||||
var keys []string
|
||||
var blocks []*types.Block
|
||||
for self.poolRootIndex > 0 {
|
||||
node = self.nodes[self.poolRootIndex-1]
|
||||
node.lock.RLock()
|
||||
block := node.block
|
||||
node.lock.RUnlock()
|
||||
n = self.nodes[self.poolRootIndex-1]
|
||||
n.lock.RLock()
|
||||
block := n.block
|
||||
n.lock.RUnlock()
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
self.poolRootIndex--
|
||||
keys = append(keys, node.hash.Str())
|
||||
blocks = append(blocks, block)
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
|
||||
if len(blocks) == 0 {
|
||||
|
@ -134,13 +136,20 @@ func (self *section) addSectionToBlockChain(p *peer) {
|
|||
err := self.bp.insertChain(blocks)
|
||||
if err != nil {
|
||||
self.invalid = true
|
||||
self.bp.peers.peerError(node.blockBy, ErrInvalidBlock, "%v", err)
|
||||
plog.Warnf("invalid block %x", node.hash)
|
||||
plog.Warnf("penalise peers %v (hash), %v (block)", node.hashBy, node.blockBy)
|
||||
self.bp.peers.peerError(n.blockBy, ErrInvalidBlock, "%v", err)
|
||||
plog.Warnf("invalid block %x", n.hash)
|
||||
plog.Warnf("penalise peers %v (hash), %v (block)", n.hashBy, n.blockBy)
|
||||
|
||||
// or invalid block and the entire chain needs to be removed
|
||||
self.removeChain()
|
||||
} else {
|
||||
// check tds
|
||||
self.bp.wg.Add(1)
|
||||
go func() {
|
||||
plog.DebugDetailf("checking td")
|
||||
self.bp.checkTD(nodes...)
|
||||
self.bp.wg.Done()
|
||||
}()
|
||||
// if all blocks inserted in this section
|
||||
// then need to try to insert blocks in child section
|
||||
if self.poolRootIndex == 0 {
|
||||
|
@ -178,7 +187,7 @@ func (self *section) addSectionToBlockChain(p *peer) {
|
|||
self.bp.status.values.BlocksInChain += len(blocks)
|
||||
self.bp.status.values.BlocksInPool -= len(blocks)
|
||||
if err != nil {
|
||||
self.bp.status.badPeers[node.blockBy]++
|
||||
self.bp.status.badPeers[n.blockBy]++
|
||||
}
|
||||
self.bp.status.lock.Unlock()
|
||||
|
||||
|
|
Loading…
Reference in New Issue