test forks from arbitrary heights

This commit is contained in:
Ethan Buchman 2014-11-29 15:26:43 -05:00
parent 24523b893d
commit f6914301d3

View file

@ -20,6 +20,7 @@ func init() {
initDB() initDB()
} }
// Called from each Test to re-init the DB
func initDB() { func initDB() {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db, _ = ethdb.NewMemDatabase() ethutil.Config.Db, _ = ethdb.NewMemDatabase()
@ -49,6 +50,7 @@ func (e *fakeEth) ClientIdentity() wire.ClientIdentity { return n
func (e *fakeEth) Db() ethutil.Database { return nil } func (e *fakeEth) Db() ethutil.Database { return nil }
func (e *fakeEth) EventMux() *event.TypeMux { return nil } func (e *fakeEth) EventMux() *event.TypeMux { return nil }
// Create new block from coinbase and parent
func newBlockFromParent(addr []byte, parent *types.Block) *types.Block { func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
block := types.CreateBlock( block := types.CreateBlock(
parent.Root(), parent.Root(),
@ -65,7 +67,7 @@ func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
} }
// Actually make a block by simulating what miner would do // Actually make a block by simulating what miner would do
func makeblock(bman *BlockManager, parent *types.Block, i int) *types.Block { func makeBlock(bman *BlockManager, parent *types.Block, i int) *types.Block {
addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20) addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20)
block := newBlockFromParent(addr, parent) block := newBlockFromParent(addr, parent)
cbase := block.State().GetOrNewStateObject(addr) cbase := block.State().GetOrNewStateObject(addr)
@ -80,13 +82,13 @@ func makeblock(bman *BlockManager, parent *types.Block, i int) *types.Block {
// Make a chain with real blocks // Make a chain with real blocks
// Runs ProcessWithParent to get proper state roots // Runs ProcessWithParent to get proper state roots
func makechain(bman *BlockManager, parent *types.Block, max int) *BlockChain { func makeChain(bman *BlockManager, parent *types.Block, max int) *BlockChain {
bman.bc.CurrentBlock = parent bman.bc.CurrentBlock = parent
bman.bc.LastBlockHash = parent.Hash() bman.bc.LastBlockHash = parent.Hash()
blocks := make(types.Blocks, max) blocks := make(types.Blocks, max)
var td *big.Int var td *big.Int
for i := 0; i < max; i++ { for i := 0; i < max; i++ {
block := makeblock(bman, parent, i) block := makeBlock(bman, parent, i)
// add the parent and its difficulty to the working chain // add the parent and its difficulty to the working chain
// so ProcessWithParent can access it // so ProcessWithParent can access it
bman.bc.workingChain = NewChain(types.Blocks{parent}) bman.bc.workingChain = NewChain(types.Blocks{parent})
@ -99,13 +101,14 @@ func makechain(bman *BlockManager, parent *types.Block, max int) *BlockChain {
return lchain return lchain
} }
// Make a new canonical chain by running TestChain and InsertChain // Make a new canonical chain n block long
// on result of makechain // by running TestChain and InsertChain
// on result of makeChain
func newCanonical(n int) (*BlockManager, error) { func newCanonical(n int) (*BlockManager, error) {
bman := &BlockManager{bc: NewChainManager(), Pow: fakePow{}, eth: &fakeEth{}} bman := &BlockManager{bc: NewChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
bman.bc.SetProcessor(bman) bman.bc.SetProcessor(bman)
parent := bman.bc.CurrentBlock parent := bman.bc.CurrentBlock
lchain := makechain(bman, parent, 5) lchain := makeChain(bman, parent, n)
_, err := bman.bc.TestChain(lchain) _, err := bman.bc.TestChain(lchain)
if err != nil { if err != nil {
@ -115,14 +118,41 @@ func newCanonical(n int) (*BlockManager, error) {
return bman, nil return bman, nil
} }
// new chain manager without setLastBlock // Create a new chain manager starting from given block
func newChainManager() *ChainManager { // Effectively a fork factory
func newChainManager(block *types.Block) *ChainManager {
bc := &ChainManager{} bc := &ChainManager{}
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis)) bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
if block == nil {
bc.Reset() bc.Reset()
} else {
bc.CurrentBlock = block
bc.SetTotalDifficulty(ethutil.Big("0"))
bc.TD = block.BlockInfo().TD
}
return bc return bc
} }
// Test fork of length N starting from block i
func testFork(t *testing.T, bman *BlockManager, i, N int, f func(td1, td2 *big.Int)) {
var b *types.Block = nil
if i > 0 {
b = bman.bc.GetBlockByNumber(uint64(i))
}
bman2 := &BlockManager{bc: newChainManager(b), Pow: fakePow{}, eth: &fakeEth{}}
bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock
chainB := makeChain(bman2, parent, N)
// test second chain against first
td2, err := bman.bc.TestChain(chainB)
if err != nil && !IsTDError(err) {
t.Error("expected chainB not to give errors:", err)
}
// Compare difficulties
f(bman.bc.TD, td2)
}
// Test basic extension of canonical chain with new blocks
func TestExtendCanonical(t *testing.T) { func TestExtendCanonical(t *testing.T) {
initDB() initDB()
// make first chain starting from genesis // make first chain starting from genesis
@ -131,95 +161,94 @@ func TestExtendCanonical(t *testing.T) {
t.Fatal("Could not make new canonical chain:", err) t.Fatal("Could not make new canonical chain:", err)
} }
// make second chain starting from end of first chain f := func(td1, td2 *big.Int) {
bman2 := &BlockManager{bc: NewChainManager(), Pow: fakePow{}, eth: &fakeEth{}} if td2.Cmp(td1) <= 0 {
bman2.bc.SetProcessor(bman2) t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
parent := bman.bc.CurrentBlock
chainB := makechain(bman2, parent, 3)
// test second chain against first
td2, err := bman.bc.TestChain(chainB)
if err != nil && !IsTDError(err) {
t.Error("expected chainB not to give errors:", err)
}
if td2.Cmp(bman.bc.TD) <= 0 {
t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", bman.bc.TD)
} }
} }
// Start fork from current height (5)
testFork(t, bman, 5, 1, f)
testFork(t, bman, 5, 2, f)
testFork(t, bman, 5, 5, f)
testFork(t, bman, 5, 10, f)
}
// Test a fork with less TD than the canonical chain
func TestShorterFork(t *testing.T) { func TestShorterFork(t *testing.T) {
initDB() initDB()
// make first chain starting from genesis // make first chain starting from genesis
bman, err := newCanonical(5) bman, err := newCanonical(10)
if err != nil { if err != nil {
t.Fatal("Could not make new canonical chain:", err) t.Fatal("Could not make new canonical chain:", err)
} }
// make second, shorter chain, starting from genesis f := func(td1, td2 *big.Int) {
bman2 := &BlockManager{bc: newChainManager(), Pow: fakePow{}, eth: &fakeEth{}} if td2.Cmp(td1) >= 0 {
bman2.bc.SetProcessor(bman2) t.Error("expected chainB to have lower difficulty. Got", td2, "expected less than", td1)
parent := bman2.bc.CurrentBlock
chainB := makechain(bman2, parent, 3)
// test second chain against first
td2, err := bman.bc.TestChain(chainB)
if err != nil && !IsTDError(err) {
t.Error("expected chainB not to give errors:", err)
}
if td2.Cmp(bman.bc.TD) >= 0 {
t.Error("expected chainB to have lower difficulty. Got", td2, "expected less than", bman.bc.TD)
} }
} }
// Sum of numbers must be less than 10
// for this to be a shorter fork
testFork(t, bman, 0, 3, f)
testFork(t, bman, 0, 7, f)
testFork(t, bman, 1, 3, f)
testFork(t, bman, 1, 7, f)
testFork(t, bman, 5, 3, f)
testFork(t, bman, 5, 4, f)
}
// Test a fork with more TD than canonical chain
func TestLongerFork(t *testing.T) { func TestLongerFork(t *testing.T) {
initDB() initDB()
// make first chain starting from genesis // make first chain starting from genesis
bman, err := newCanonical(5) bman, err := newCanonical(10)
if err != nil { if err != nil {
t.Fatal("Could not make new canonical chain:", err) t.Fatal("Could not make new canonical chain:", err)
} }
// make second, longer chain, starting from genesis f := func(td1, td2 *big.Int) {
bman2 := &BlockManager{bc: newChainManager(), Pow: fakePow{}, eth: &fakeEth{}} if td2.Cmp(td1) <= 0 {
bman2.bc.SetProcessor(bman2) t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
parent := bman2.bc.CurrentBlock
chainB := makechain(bman2, parent, 10)
td, err := bman.bc.TestChain(chainB)
if err != nil {
t.Error("expected chainB not to give errors:", err)
}
if td.Cmp(bman.bc.TD) <= 0 {
t.Error("expected chainB to have higher difficulty. Got", td, "expected more than", bman.bc.TD)
} }
} }
// Sum of numbers must be greater than 10
// for this to be a longer fork
testFork(t, bman, 0, 11, f)
testFork(t, bman, 0, 15, f)
testFork(t, bman, 1, 10, f)
testFork(t, bman, 1, 12, f)
testFork(t, bman, 5, 6, f)
testFork(t, bman, 5, 8, f)
}
// Test a fork with equal TD to canonical chain
func TestEqualFork(t *testing.T) { func TestEqualFork(t *testing.T) {
initDB() initDB()
bman, err := newCanonical(5) bman, err := newCanonical(10)
if err != nil { if err != nil {
t.Fatal("Could not make new canonical chain:", err) t.Fatal("Could not make new canonical chain:", err)
} }
bman2 := &BlockManager{bc: newChainManager(), Pow: fakePow{}, eth: &fakeEth{}} f := func(td1, td2 *big.Int) {
bman2.bc.SetProcessor(bman2) if td2.Cmp(td1) != 0 {
parent := bman2.bc.CurrentBlock t.Error("expected chainB to have equal difficulty. Got", td2, "expected less than", td1)
chainB := makechain(bman2, parent, 5)
td, err := bman.bc.TestChain(chainB)
if err != nil && !IsTDError(err) {
t.Error("expected chainB not to give errors:", err)
}
if td.Cmp(bman.bc.TD) != 0 {
t.Error("expected chainB to have equal difficulty. Got", td, "expected less than", bman.bc.TD)
} }
} }
// Sum of numbers must be equal to 10
// for this to be an equal fork
testFork(t, bman, 0, 10, f)
testFork(t, bman, 1, 9, f)
testFork(t, bman, 2, 8, f)
testFork(t, bman, 5, 5, f)
testFork(t, bman, 6, 4, f)
testFork(t, bman, 9, 1, f)
}
// Test a broken chain (no common ancestor)
func TestBrokenChain(t *testing.T) { func TestBrokenChain(t *testing.T) {
initDB() initDB()
bman, err := newCanonical(5) bman, err := newCanonical(5)
@ -231,7 +260,7 @@ func TestBrokenChain(t *testing.T) {
bman2.bc.SetProcessor(bman2) bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock parent := bman2.bc.CurrentBlock
chainB := makechain(bman2, parent, 5) chainB := makeChain(bman2, parent, 5)
chainB.Remove(chainB.Front()) chainB.Remove(chainB.Front())
_, err = bman.bc.TestChain(chainB) _, err = bman.bc.TestChain(chainB)
@ -253,7 +282,7 @@ func BenchmarkChainTesting(b *testing.B) {
bman2.bc.SetProcessor(bman2) bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock parent := bman2.bc.CurrentBlock
chain := makechain(bman2, parent, chainlen) chain := makeChain(bman2, parent, chainlen)
stime := time.Now() stime := time.Now()
bman.bc.TestChain(chain) bman.bc.TestChain(chain)