use real ProcessWithParent for all chain manager tests. passing

This commit is contained in:
Ethan Buchman 2014-11-23 11:18:18 -05:00
parent 6ef0f67b9f
commit 24523b893d
2 changed files with 175 additions and 97 deletions

View file

@ -196,7 +196,6 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
} }
} }
} }
return nil return nil
} }
@ -229,7 +228,8 @@ func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash) return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
} }
parentTd := parent.BlockInfo().TD //parentTd := parent.BlockInfo().TD
parentTd := self.BlockInfo(parent).TD
uncleDiff := new(big.Int) uncleDiff := new(big.Int)
for _, uncle := range block.Uncles { for _, uncle := range block.Uncles {
@ -246,8 +246,20 @@ func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo { func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
bi := types.BlockInfo{} bi := types.BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
if len(data) == 0 {
if bc.workingChain != nil {
// Check the temp chain
for e := bc.workingChain.Front(); e != nil; e = e.Next() {
l := e.Value.(*link)
b := l.Block
if bytes.Compare(b.Hash(), block.Hash()) == 0 {
bi = types.BlockInfo{Number: b.Number.Uint64(), Hash: b.Hash(), Parent: b.PrevHash, TD: l.Td}
return bi
}
}
}
}
bi.RlpDecode(data) bi.RlpDecode(data)
return bi return bi
} }
@ -275,8 +287,10 @@ func (self *ChainManager) InsertChain(chain *BlockChain, call func(*types.Block,
for e := chain.Front(); e != nil; e = e.Next() { for e := chain.Front(); e != nil; e = e.Next() {
link := e.Value.(*link) link := e.Value.(*link)
self.add(link.Block) // must set TD before adding as
// add calls writeBlockInfo which writes the most recent TD
self.SetTotalDifficulty(link.Td) self.SetTotalDifficulty(link.Td)
self.add(link.Block)
call(link.Block, link.Messages) call(link.Block, link.Messages)
} }

View file

@ -16,22 +16,15 @@ import (
"github.com/ethereum/go-ethereum/wire" "github.com/ethereum/go-ethereum/wire"
) )
// In these tests, TD = block.Number
var TD *big.Int
func init() { func init() {
initDB()
}
func initDB() {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db, _ = ethdb.NewMemDatabase() ethutil.Config.Db, _ = ethdb.NewMemDatabase()
} }
type fakeproc struct {
}
func (self fakeproc) ProcessWithParent(a, b *types.Block) (*big.Int, state.Messages, error) {
TD = new(big.Int).Add(TD, big.NewInt(1))
return TD, nil, nil
}
// So we can generate blocks easily // So we can generate blocks easily
type fakePow struct{} type fakePow struct{}
@ -56,61 +49,120 @@ 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 }
func makechain(cman *ChainManager, max int) *BlockChain { func newBlockFromParent(addr []byte, parent *types.Block) *types.Block {
blocks := make(types.Blocks, max) block := types.CreateBlock(
for i := 0; i < max; i++ { parent.Root(),
addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20) parent.Hash(),
block := cman.NewBlock(addr) addr,
if i != 0 { ethutil.BigPow(2, 32),
cman.CurrentBlock = blocks[i-1] nil,
} "")
blocks[i] = block block.MinGasPrice = big.NewInt(10000000000000)
} block.Difficulty = CalcDifficulty(block, parent)
return NewChain(blocks) block.Number = new(big.Int).Add(parent.Number, ethutil.Big1)
block.GasLimit = block.CalcGasLimit(parent)
return block
} }
func makechain2(bman *BlockManager, max int) *BlockChain { // Actually make a block by simulating what miner would do
parent := bman.bc.CurrentBlock func makeblock(bman *BlockManager, parent *types.Block, i int) *types.Block {
addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20)
block := newBlockFromParent(addr, parent)
cbase := block.State().GetOrNewStateObject(addr)
cbase.SetGasPool(block.CalcGasLimit(parent))
receipts, txs, _, _, _ := bman.ProcessTransactions(cbase, block.State(), block, block, types.Transactions{})
block.SetTransactions(txs)
block.SetReceipts(receipts)
bman.AccumelateRewards(block.State(), block, parent)
block.State().Update()
return block
}
// Make a chain with real blocks
// Runs ProcessWithParent to get proper state roots
func makechain(bman *BlockManager, parent *types.Block, max int) *BlockChain {
bman.bc.CurrentBlock = parent
bman.bc.LastBlockHash = parent.Hash()
blocks := make(types.Blocks, max) blocks := make(types.Blocks, max)
var td *big.Int
for i := 0; i < max; i++ { for i := 0; i < max; i++ {
addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20) block := makeblock(bman, parent, i)
block := bman.bc.NewBlock(addr) // add the parent and its difficulty to the working chain
cbase := block.State().GetOrNewStateObject(addr) // so ProcessWithParent can access it
cbase.SetGasPool(block.CalcGasLimit(parent)) bman.bc.workingChain = NewChain(types.Blocks{parent})
receipts, txs, _, _, _ := bman.ProcessTransactions(cbase, block.State(), block, block, types.Transactions{}) bman.bc.workingChain.Back().Value.(*link).Td = td
block.SetTransactions(txs) td, _, _ = bman.bc.processor.ProcessWithParent(block, parent)
block.SetReceipts(receipts)
bman.AccumelateRewards(block.State(), block, parent)
block.State().Update()
lchain := NewChain(types.Blocks{block})
_, err := bman.bc.TestChain(lchain)
if err != nil {
fmt.Println("failed to run test chain!:", err)
}
bman.bc.InsertChain(lchain, func(block *types.Block, _ state.Messages) {})
blocks[i] = block blocks[i] = block
parent = block parent = block
} }
return NewChain(blocks) lchain := NewChain(blocks)
return lchain
}
// Make a new canonical chain by running TestChain and InsertChain
// on result of makechain
func newCanonical(n int) (*BlockManager, error) {
bman := &BlockManager{bc: NewChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
bman.bc.SetProcessor(bman)
parent := bman.bc.CurrentBlock
lchain := makechain(bman, parent, 5)
_, err := bman.bc.TestChain(lchain)
if err != nil {
return nil, err
}
bman.bc.InsertChain(lchain, func(block *types.Block, _ state.Messages) {})
return bman, nil
}
// new chain manager without setLastBlock
func newChainManager() *ChainManager {
bc := &ChainManager{}
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
bc.Reset()
return bc
}
func TestExtendCanonical(t *testing.T) {
initDB()
// make first chain starting from genesis
bman, err := newCanonical(5)
if err != nil {
t.Fatal("Could not make new canonical chain:", err)
}
// make second chain starting from end of first chain
bman2 := &BlockManager{bc: NewChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
bman2.bc.SetProcessor(bman2)
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)
}
} }
func TestShorterFork(t *testing.T) { func TestShorterFork(t *testing.T) {
cman := NewChainManager() initDB()
bman := &BlockManager{bc: cman, Pow: fakePow{}, eth: &fakeEth{}} // make first chain starting from genesis
bman.bc.SetProcessor(bman) bman, err := newCanonical(5)
if err != nil {
t.Fatal("Could not make new canonical chain:", err)
}
makechain2(bman, 5) // make second, shorter chain, starting from genesis
bman2 := &BlockManager{bc: newChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
cman2 := NewChainManager()
cman2.Reset() // so we don't end up with last block of cman1
bman2 := &BlockManager{bc: cman2, Pow: fakePow{}, eth: &fakeEth{}}
bman2.bc.SetProcessor(bman2) bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock
chainB := makechain(bman2, parent, 3)
chainB := makechain2(bman2, 3) // test second chain against first
td2, err := bman.bc.TestChain(chainB) td2, err := bman.bc.TestChain(chainB)
if err != nil && !IsTDError(err) { if err != nil && !IsTDError(err) {
t.Error("expected chainB not to give errors:", err) t.Error("expected chainB not to give errors:", err)
@ -122,76 +174,88 @@ func TestShorterFork(t *testing.T) {
} }
func TestLongerFork(t *testing.T) { func TestLongerFork(t *testing.T) {
cman := NewChainManager() initDB()
cman.SetProcessor(fakeproc{}) // make first chain starting from genesis
bman, err := newCanonical(5)
TD = big.NewInt(1)
chainA := makechain(cman, 5)
TD = big.NewInt(1)
chainB := makechain(cman, 10)
td, err := cman.TestChain(chainA)
if err != nil { if err != nil {
t.Error("unable to create new TD from chainA:", err) t.Fatal("Could not make new canonical chain:", err)
} }
cman.TD = td
_, err = cman.TestChain(chainB) // make second, longer chain, starting from genesis
bman2 := &BlockManager{bc: newChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock
chainB := makechain(bman2, parent, 10)
td, err := bman.bc.TestChain(chainB)
if err != nil { if err != nil {
t.Error("expected chainB not to give errors:", err) 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)
}
} }
func TestEqualFork(t *testing.T) { func TestEqualFork(t *testing.T) {
cman := NewChainManager() initDB()
cman.SetProcessor(fakeproc{}) bman, err := newCanonical(5)
TD = big.NewInt(1)
chainA := makechain(cman, 5)
TD = big.NewInt(2)
chainB := makechain(cman, 5)
td, err := cman.TestChain(chainA)
if err != nil { if err != nil {
t.Error("unable to create new TD from chainA:", err) t.Fatal("Could not make new canonical chain:", err)
} }
cman.TD = td
_, err = cman.TestChain(chainB) bman2 := &BlockManager{bc: newChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
if err != nil { bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock
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) 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)
}
} }
func TestBrokenChain(t *testing.T) { func TestBrokenChain(t *testing.T) {
cman := NewChainManager() initDB()
cman.SetProcessor(fakeproc{}) bman, err := newCanonical(5)
if err != nil {
t.Fatal("Could not make new canonical chain:", err)
}
TD = big.NewInt(1) bman2 := &BlockManager{bc: NewChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
chain := makechain(cman, 5) bman2.bc.SetProcessor(bman2)
chain.Remove(chain.Front()) parent := bman2.bc.CurrentBlock
_, err := cman.TestChain(chain) chainB := makechain(bman2, parent, 5)
chainB.Remove(chainB.Front())
_, err = bman.bc.TestChain(chainB)
if err == nil { if err == nil {
t.Error("expected broken chain to return error") t.Error("expected broken chain to return error")
} }
} }
func BenchmarkChainTesting(b *testing.B) { func BenchmarkChainTesting(b *testing.B) {
initDB()
const chainlen = 1000 const chainlen = 1000
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") bman, err := newCanonical(5)
ethutil.Config.Db, _ = ethdb.NewMemDatabase() if err != nil {
b.Fatal("Could not make new canonical chain:", err)
}
cman := NewChainManager() bman2 := &BlockManager{bc: NewChainManager(), Pow: fakePow{}, eth: &fakeEth{}}
cman.SetProcessor(fakeproc{}) bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock
TD = big.NewInt(1) chain := makechain(bman2, parent, chainlen)
chain := makechain(cman, chainlen)
stime := time.Now() stime := time.Now()
cman.TestChain(chain) bman.bc.TestChain(chain)
fmt.Println(chainlen, "took", time.Since(stime)) fmt.Println(chainlen, "took", time.Since(stime))
} }