aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorEthan Buchman <ethan@coinculture.info>2015-02-18 11:02:15 +0800
committerEthan Buchman <ethan@coinculture.info>2015-02-18 11:02:15 +0800
commit9446489cf3f2eb4b5237b9355b3975fde2886508 (patch)
tree6e0974b606e0e750f2c439253ad4548cc89ea2cc /core
parent2ba65f4fbaea49c1e0d99959b0331e09b153f931 (diff)
downloaddexon-9446489cf3f2eb4b5237b9355b3975fde2886508.tar.gz
dexon-9446489cf3f2eb4b5237b9355b3975fde2886508.tar.zst
dexon-9446489cf3f2eb4b5237b9355b3975fde2886508.zip
core: chain manager forking tests
Diffstat (limited to 'core')
-rw-r--r--core/chain_manager_test.go276
1 files changed, 276 insertions, 0 deletions
diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go
index bc3a264d1..a357a8b1a 100644
--- a/core/chain_manager_test.go
+++ b/core/chain_manager_test.go
@@ -3,6 +3,7 @@ package core
import (
"bytes"
"fmt"
+ "math/big"
"os"
"path"
"runtime"
@@ -13,14 +14,167 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/state"
)
+// So we can generate blocks easily
+type fakePow struct{}
+
+func (f fakePow) Search(block pow.Block, stop <-chan struct{}) []byte { return nil }
+func (f fakePow) Verify(block pow.Block) bool { return true }
+func (f fakePow) GetHashrate() int64 { return 0 }
+func (f fakePow) Turbo(bool) {}
+
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
}
+func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
+ block := types.NewBlock(parent.Hash(), addr, parent.Root(), ethutil.BigPow(2, 32), nil, "")
+
+ block.SetUncles(nil)
+ block.SetTransactions(nil)
+ block.SetReceipts(nil)
+
+ header := block.Header()
+ header.Difficulty = CalcDifficulty(block, parent)
+ header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
+ header.GasLimit = CalcGasLimit(parent, block)
+
+ block.Td = parent.Td
+
+ return block
+}
+
+// Actually make a block by simulating what miner would do
+func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database) *types.Block {
+ addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20)
+ block := newBlockFromParent(addr, parent)
+ state := state.New(block.Root(), db)
+ cbase := state.GetOrNewStateObject(addr)
+ cbase.SetGasPool(CalcGasLimit(parent, block))
+ cbase.AddAmount(BlockReward)
+ state.Update(ethutil.Big0)
+ block.SetRoot(state.Root())
+ return block
+}
+
+// Make a chain with real blocks
+// Runs ProcessWithParent to get proper state roots
+func makeChain(bman *BlockProcessor, parent *types.Block, max int, db ethutil.Database) types.Blocks {
+ bman.bc.currentBlock = parent
+ blocks := make(types.Blocks, max)
+ for i := 0; i < max; i++ {
+ block := makeBlock(bman, parent, i, db)
+ td, err := bman.processWithParent(block, parent)
+ if err != nil {
+ fmt.Println("process with parent failed", err)
+ panic(err)
+ }
+ block.Td = td
+ blocks[i] = block
+ parent = block
+ fmt.Printf("New Block: %x\n", block.Hash())
+ }
+ fmt.Println("Done making chain")
+ return blocks
+}
+
+// Create a new chain manager starting from given block
+// Effectively a fork factory
+func newChainManager(block *types.Block, eventMux *event.TypeMux, db ethutil.Database) *ChainManager {
+ bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: eventMux}
+ if block == nil {
+ bc.Reset()
+ } else {
+ bc.currentBlock = block
+ bc.td = block.Td
+ }
+ return bc
+}
+
+// block processor with fake pow
+func newBlockProcessor(db ethutil.Database, txpool *TxPool, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
+ bman := NewBlockProcessor(db, txpool, newChainManager(nil, eventMux, db), eventMux)
+ bman.Pow = fakePow{}
+ return bman
+}
+
+// Make a new canonical chain by running InsertChain
+// on result of makeChain
+func newCanonical(n int, db ethutil.Database) (*BlockProcessor, error) {
+ eventMux := &event.TypeMux{}
+ txpool := NewTxPool(eventMux)
+
+ bman := newBlockProcessor(db, txpool, newChainManager(nil, eventMux, db), eventMux)
+ bman.bc.SetProcessor(bman)
+ parent := bman.bc.CurrentBlock()
+ if n == 0 {
+ return bman, nil
+ }
+ lchain := makeChain(bman, parent, n, db)
+ bman.bc.InsertChain(lchain)
+ return bman, nil
+}
+
+// Test fork of length N starting from block i
+func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) {
+ fmt.Println("Testing Fork!")
+ var b *types.Block = nil
+ if i > 0 {
+ b = bman.bc.GetBlockByNumber(uint64(i))
+ }
+ _ = b
+ // switch databases to process the new chain
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // copy old chain up to i into new db with deterministic canonical
+ bman2, err := newCanonical(i, db)
+ if err != nil {
+ t.Fatal("could not make new canonical in testFork", err)
+ }
+ bman2.bc.SetProcessor(bman2)
+
+ parent := bman2.bc.CurrentBlock()
+ chainB := makeChain(bman2, parent, N, db)
+ bman2.bc.InsertChain(chainB)
+
+ tdpre := bman.bc.Td()
+ td, err := testChain(chainB, bman)
+ if err != nil {
+ t.Fatal("expected chainB not to give errors:", err)
+ }
+ // Compare difficulties
+ f(tdpre, td)
+}
+
+func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
+ td := new(big.Int)
+ for _, block := range chainB {
+ td2, err := bman.bc.processor.Process(block)
+ if err != nil {
+ if IsKnownBlockErr(err) {
+ continue
+ }
+ return nil, err
+ }
+ block.Td = td2
+ td = td2
+
+ bman.bc.mu.Lock()
+ {
+ bman.bc.write(block)
+ }
+ bman.bc.mu.Unlock()
+ }
+ return td, nil
+}
+
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
fh, err := os.OpenFile(path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm)
if err != nil {
@@ -45,6 +199,128 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
done <- true
}
+func TestExtendCanonical(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // make first chain starting from genesis
+ bman, err := newCanonical(5, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) <= 0 {
+ t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
+ }
+ }
+ // 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)
+}
+
+func TestShorterFork(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // make first chain starting from genesis
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) >= 0 {
+ t.Error("expected chainB to have lower difficulty. Got", td2, "expected less than", td1)
+ }
+ }
+ // 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, 1, f)
+ testFork(t, bman, 1, 7, f)
+ testFork(t, bman, 5, 3, f)
+ testFork(t, bman, 5, 4, f)
+}
+
+func TestLongerFork(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ // make first chain starting from genesis
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) <= 0 {
+ t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1)
+ }
+ }
+ // 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)
+}
+
+func TestEqualFork(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ f := func(td1, td2 *big.Int) {
+ if td2.Cmp(td1) != 0 {
+ t.Error("expected chainB to have equal difficulty. Got", td2, "expected ", td1)
+ }
+ }
+ // Sum of numbers must be equal to 10
+ // for this to be an equal fork
+ 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)
+}
+
+func TestBrokenChain(t *testing.T) {
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ bman, err := newCanonical(10, db)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ db2, err := ethdb.NewMemDatabase()
+ if err != nil {
+ t.Fatal("Failed to create db:", err)
+ }
+ bman2, err := newCanonical(10, db2)
+ if err != nil {
+ t.Fatal("Could not make new canonical chain:", err)
+ }
+ bman2.bc.SetProcessor(bman2)
+ parent := bman2.bc.CurrentBlock()
+ chainB := makeChain(bman2, parent, 5, db2)
+ chainB = chainB[1:]
+ _, err = testChain(chainB, bman)
+ if err == nil {
+ t.Error("expected broken chain to return error")
+ }
+}
+
func TestChainInsertions(t *testing.T) {
t.Skip() // travil fails.