434 lines
15 KiB
Go
434 lines
15 KiB
Go
package blockpool
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// using the mock framework in blockpool_util_test
|
|
// we test various scenarios here
|
|
|
|
func TestPeerWithKnownBlock(t *testing.T) {
|
|
_, 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)
|
|
blockPool.Stop()
|
|
// no request on known block
|
|
peer0.checkBlockHashesRequests()
|
|
}
|
|
|
|
func TestPeerWithKnownParentBlock(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.initRefBlockChain(1)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPool.Start()
|
|
|
|
peer0 := blockPoolTester.newPeer("0", 1, 1)
|
|
peer0.AddPeer()
|
|
peer0.serveBlocks(0, 1)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
peer0.checkBlocksRequests([]int{1})
|
|
peer0.checkBlockHashesRequests()
|
|
blockPoolTester.refBlockChain[1] = []int{}
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestSimpleChain(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(2)
|
|
|
|
blockPool.Start()
|
|
|
|
peer1 := blockPoolTester.newPeer("peer1", 2, 2)
|
|
peer1.AddPeer()
|
|
peer1.serveBlocks(1, 2)
|
|
go peer1.serveBlockHashes(2, 1, 0)
|
|
peer1.serveBlocks(0, 1)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[2] = []int{}
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestChainConnectingWithParentHash(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(3)
|
|
|
|
blockPool.Start()
|
|
|
|
peer1 := blockPoolTester.newPeer("peer1", 3, 3)
|
|
peer1.AddPeer()
|
|
go peer1.serveBlocks(2, 3)
|
|
go peer1.serveBlockHashes(3, 2, 1)
|
|
peer1.serveBlocks(0, 1, 2)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[3] = []int{}
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestMultiSectionChain(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(5)
|
|
|
|
blockPool.Start()
|
|
|
|
peer1 := blockPoolTester.newPeer("peer1", 5, 5)
|
|
|
|
peer1.AddPeer()
|
|
go peer1.serveBlocks(4, 5)
|
|
go peer1.serveBlockHashes(5, 4, 3)
|
|
go peer1.serveBlocks(2, 3, 4)
|
|
go peer1.serveBlockHashes(3, 2, 1, 0)
|
|
peer1.serveBlocks(0, 1, 2)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[5] = []int{}
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestNewBlocksOnPartialChain(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(7)
|
|
blockPool.Start()
|
|
|
|
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 = 7
|
|
peer1.currentBlock = 7
|
|
peer1.AddPeer()
|
|
peer1.sendBlocks(6, 7)
|
|
go peer1.serveBlockHashes(7, 6, 5)
|
|
go peer1.serveBlocks(2, 3)
|
|
go peer1.serveBlocks(5, 6)
|
|
go peer1.serveBlockHashes(3, 2, 1) // tests that hash request from known chain root is remembered
|
|
peer1.serveBlocks(0, 1, 2)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[7] = []int{}
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestPeerSwitchUp(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(7)
|
|
|
|
blockPool.Start()
|
|
|
|
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.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
|
|
peer2.serveBlocks(0, 1, 2) // final blocks linking to blockchain sent
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[7] = []int{}
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestPeerSwitchDownOverlapSectionWithoutRootBlock(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(6)
|
|
blockPool.Start()
|
|
|
|
peer1 := blockPoolTester.newPeer("peer1", 4, 4)
|
|
peer2 := blockPoolTester.newPeer("peer2", 6, 6)
|
|
|
|
peer2.AddPeer()
|
|
peer2.serveBlocks(5, 6) // partially complete, section will be preserved
|
|
peer2.serveBlockHashes(6, 5, 4) // no go: make sure skeleton is created
|
|
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
|
blockPool.RemovePeer("peer2") // peer2 disconnects
|
|
go peer1.serveBlockHashes(4, 3, 2, 1, 0) //
|
|
go peer1.serveBlocks(3, 4) //
|
|
go peer1.serveBlocks(4, 5) // tests that section set by demoted peer is remembered and blocks are accepted from new peer if they have it even if peers original TD is lower
|
|
peer1.serveBlocks(0, 1, 2, 3)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[6] = []int{} // tests that idle sections are not inserted in blockchain
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestPeerSwitchDownOverlapSectionWithRootBlock(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(6)
|
|
blockPool.Start()
|
|
|
|
peer1 := blockPoolTester.newPeer("peer1", 4, 4)
|
|
peer2 := blockPoolTester.newPeer("peer2", 6, 6)
|
|
|
|
peer2.AddPeer()
|
|
peer2.serveBlocks(5, 6) // partially complete, section will be preserved
|
|
go peer2.serveBlockHashes(6, 5, 4) //
|
|
peer2.serveBlocks(3, 4) // !incomplete section
|
|
time.Sleep(100 * time.Millisecond) // make sure block 4 added
|
|
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
|
blockPool.RemovePeer("peer2") // peer2 disconnects
|
|
go peer1.serveBlockHashes(4, 3, 2, 1, 0) // tests that hash request are directly connecting if the head block exists
|
|
go peer1.serveBlocks(4, 5) // tests that section set by demoted peer is remembered and blocks are accepted from new peer if they have it even if peers original TD is lower
|
|
peer1.serveBlocks(0, 1, 2, 3)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[6] = []int{} // tests that idle sections are not inserted in blockchain
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestPeerSwitchDownDisjointSection(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(3)
|
|
blockPool.Start()
|
|
|
|
peer1 := blockPoolTester.newPeer("peer1", 3, 3)
|
|
peer2 := blockPoolTester.newPeer("peer2", 6, 6)
|
|
|
|
peer2.AddPeer()
|
|
peer2.serveBlocks(5, 6) // partially complete, section will be preserved
|
|
go peer2.serveBlockHashes(6, 5, 4) //
|
|
peer2.serveBlocks(3, 4, 5) //
|
|
time.Sleep(100 * time.Millisecond) // make sure blocks are received
|
|
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
|
blockPool.RemovePeer("peer2") // peer2 disconnects
|
|
go peer1.serveBlocks(2, 3) //
|
|
go peer1.serveBlockHashes(3, 2, 1) //
|
|
peer1.serveBlocks(0, 1, 2) //
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[3] = []int{} // tests that idle sections are not inserted in blockchain
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestPeerSwitchBack(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(8)
|
|
|
|
blockPool.Start()
|
|
|
|
peer1 := blockPoolTester.newPeer("peer1", 11, 11)
|
|
peer2 := blockPoolTester.newPeer("peer2", 8, 8)
|
|
|
|
peer2.AddPeer()
|
|
go peer2.serveBlocks(7, 8)
|
|
go peer2.serveBlockHashes(8, 7, 6)
|
|
go peer2.serveBlockHashes(6, 5, 4)
|
|
peer2.serveBlocks(4, 5) // section partially complete
|
|
peer1.AddPeer() // peer1 is promoted as best peer
|
|
peer1.serveBlocks(10, 11) //
|
|
peer1.serveBlockHashes(11, 10) // only gives useless results
|
|
blockPool.RemovePeer("peer1") // peer1 disconnects
|
|
go peer2.serveBlockHashes(4, 3, 2, 1, 0) // tests that asking for hashes from 4 is remembered
|
|
go peer2.serveBlocks(3, 4, 5, 6, 7, 8) // tests that section 4, 5, 6 and 7, 8 are remembered for missing blocks
|
|
peer2.serveBlocks(0, 1, 2, 3)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
blockPool.Stop()
|
|
blockPoolTester.refBlockChain[8] = []int{}
|
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
|
}
|
|
|
|
func TestForkSimple(t *testing.T) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(9)
|
|
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
delete(blockPoolTester.refBlockChain, 6)
|
|
|
|
blockPool.Start()
|
|
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)
|
|
go peer1.serveBlockHashes(9, 8, 7, 3, 2)
|
|
peer1.serveBlocks(1, 2, 3, 7, 8)
|
|
peer2.AddPeer() // peer2 is promoted as best peer
|
|
go peer2.serveBlocks(5, 6) //
|
|
go peer2.serveBlockHashes(6, 5, 4, 3, 2) // fork on 3 -> 4 (earlier child: 7)
|
|
go peer2.serveBlocks(1, 2, 3, 4, 5)
|
|
go peer2.serveBlockHashes(2, 1, 0)
|
|
peer2.serveBlocks(0, 1, 2)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
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) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(11)
|
|
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
delete(blockPoolTester.refBlockChain, 6)
|
|
|
|
blockPool.Start()
|
|
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) //
|
|
go peer1.serveBlockHashes(9, 8, 7, 3, 2) //
|
|
peer1.serveBlocks(7, 8) // partial section
|
|
// time.Sleep(1 * time.Second)
|
|
peer2.AddPeer() //
|
|
go peer2.serveBlocks(5, 6) //
|
|
go peer2.serveBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
|
peer2.serveBlocks(1, 2, 3, 4, 5) //
|
|
|
|
// peer1 finds new blocks
|
|
peer1.td = 11
|
|
peer1.currentBlock = 11
|
|
peer1.AddPeer()
|
|
go peer1.serveBlocks(10, 11)
|
|
go peer1.serveBlockHashes(11, 10, 9)
|
|
go peer1.serveBlocks(9, 10)
|
|
// time.Sleep(1 * time.Second)
|
|
go peer1.serveBlocks(3, 7) // tests that block requests on earlier fork are remembered
|
|
go peer1.serveBlockHashes(2, 1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
|
|
peer1.serveBlocks(0, 1)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
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) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(9)
|
|
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
delete(blockPoolTester.refBlockChain, 6)
|
|
|
|
blockPool.Start()
|
|
|
|
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)
|
|
go peer1.serveBlockHashes(9, 8, 7, 3, 2)
|
|
peer1.serveBlocks(7, 8)
|
|
peer2.AddPeer()
|
|
go peer2.serveBlocks(5, 6) //
|
|
go peer2.serveBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
|
peer2.serveBlocks(2, 3, 4, 5) //
|
|
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
|
go peer1.serveBlocks(1, 2) //
|
|
go peer1.serveBlockHashes(2, 1, 0) //
|
|
go peer1.serveBlocks(3, 7) // tests that block requests on earlier fork are remembered and orphan section relinks to existing parent block
|
|
peer1.serveBlocks(0, 1)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
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) {
|
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
|
blockPoolTester.blockChain[0] = nil
|
|
blockPoolTester.initRefBlockChain(9)
|
|
blockPoolTester.refBlockChain[3] = []int{4, 7}
|
|
delete(blockPoolTester.refBlockChain, 6)
|
|
|
|
blockPool.Start()
|
|
|
|
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)
|
|
go peer1.serveBlockHashes(9, 8, 7)
|
|
peer1.serveBlocks(3, 7, 8) // make sure this section is complete
|
|
// time.Sleep(2 * time.Second) //
|
|
peer1.serveBlockHashes(7, 3, 2) // block 3/7 is section boundary
|
|
peer1.serveBlocks(2, 3) // partially complete sections block 2 missing
|
|
peer2.AddPeer() //
|
|
go peer2.serveBlocks(5, 6) //
|
|
go peer2.serveBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
|
time.Sleep(100 * time.Millisecond) //
|
|
peer2.serveBlocks(2, 3, 4, 5) // block 2 still missing.
|
|
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
|
go peer1.serveBlockHashes(2, 1) //
|
|
peer1.serveBlocks(0, 1, 2)
|
|
|
|
blockPool.Wait(waitTimeout)
|
|
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)
|
|
|
|
}
|