aboutsummaryrefslogtreecommitdiffstats
path: root/core/chain_makers.go
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2015-06-19 07:57:16 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2015-06-30 00:51:47 +0800
commitceaf1c080b4047c26d433e778d4d3142f4e29d24 (patch)
tree5f1f5b8cfad997ac3be2070e7a6a0c9876d8d5d9 /core/chain_makers.go
parent1d42888d3047dabfb352c94a2051e7af14d2a509 (diff)
downloaddexon-ceaf1c080b4047c26d433e778d4d3142f4e29d24.tar
dexon-ceaf1c080b4047c26d433e778d4d3142f4e29d24.tar.gz
dexon-ceaf1c080b4047c26d433e778d4d3142f4e29d24.tar.bz2
dexon-ceaf1c080b4047c26d433e778d4d3142f4e29d24.tar.lz
dexon-ceaf1c080b4047c26d433e778d4d3142f4e29d24.tar.xz
dexon-ceaf1c080b4047c26d433e778d4d3142f4e29d24.tar.zst
dexon-ceaf1c080b4047c26d433e778d4d3142f4e29d24.zip
core: add GenerateChain, GenesisBlockForTesting
Diffstat (limited to 'core/chain_makers.go')
-rw-r--r--core/chain_makers.go201
1 files changed, 127 insertions, 74 deletions
diff --git a/core/chain_makers.go b/core/chain_makers.go
index e704fd088..72ae7970e 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -1,7 +1,6 @@
package core
import (
- "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
@@ -11,7 +10,8 @@ import (
"github.com/ethereum/go-ethereum/pow"
)
-// So we can generate blocks easily
+// FakePow is a non-validating proof of work implementation.
+// It returns true from Verify for any block.
type FakePow struct{}
func (f FakePow) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte) {
@@ -23,69 +23,125 @@ func (f FakePow) Turbo(bool) {}
// So we can deterministically seed different blockchains
var (
- CanonicalSeed = 1
- ForkSeed = 2
+ canonicalSeed = 1
+ forkSeed = 2
)
-// Utility functions for making chains on the fly
-// Exposed for sake of testing from other packages (eg. go-ethash)
-func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block {
- return types.NewBlock(makeHeader(parent, i, db, seed), nil, nil, nil)
+// BlockGen creates blocks for testing.
+// See GenerateChain for a detailed explanation.
+type BlockGen struct {
+ i int
+ parent *types.Block
+ chain []*types.Block
+ header *types.Header
+ statedb *state.StateDB
+
+ coinbase *state.StateObject
+ txs []*types.Transaction
+ receipts []*types.Receipt
+ uncles []*types.Header
}
-func MakeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks {
- return makeChain(bman, parent, max, db, seed)
+// SetCoinbase sets the coinbase of the generated block.
+// It can be called at most once.
+func (b *BlockGen) SetCoinbase(addr common.Address) {
+ if b.coinbase != nil {
+ if len(b.txs) > 0 {
+ panic("coinbase must be set before adding transactions")
+ }
+ panic("coinbase can only be set once")
+ }
+ b.header.Coinbase = addr
+ b.coinbase = b.statedb.GetOrNewStateObject(addr)
+ b.coinbase.SetGasLimit(b.header.GasLimit)
}
-func NewChainMan(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager {
- return newChainManager(block, eventMux, db)
+// SetExtra sets the extra data field of the generated block.
+func (b *BlockGen) SetExtra(data []byte) {
+ b.header.Extra = data
}
-func NewBlockProc(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
- return newBlockProcessor(db, cman, eventMux)
+// AddTx adds a transaction to the generated block. If no coinbase has
+// been set, the block's coinbase is set to the zero address.
+//
+// AddTx panics if the transaction cannot be executed. In addition to
+// the protocol-imposed limitations (gas limit, etc.), there are some
+// further limitations on the content of transactions that can be
+// added. Notably, contract code relying on the BLOCKHASH instruction
+// will panic during execution.
+func (b *BlockGen) AddTx(tx *types.Transaction) {
+ if b.coinbase == nil {
+ b.SetCoinbase(common.Address{})
+ }
+ _, gas, err := ApplyMessage(NewEnv(b.statedb, nil, tx, b.header), tx, b.coinbase)
+ if err != nil {
+ panic(err)
+ }
+ b.statedb.Update()
+ b.header.GasUsed.Add(b.header.GasUsed, gas)
+ receipt := types.NewReceipt(b.statedb.Root().Bytes(), b.header.GasUsed)
+ logs := b.statedb.GetLogs(tx.Hash())
+ receipt.SetLogs(logs)
+ receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
+ b.txs = append(b.txs, tx)
+ b.receipts = append(b.receipts, receipt)
}
-func NewCanonical(n int, db common.Database) (*BlockProcessor, error) {
- return newCanonical(n, db)
+// TxNonce returns the next valid transaction nonce for the
+// account at addr. It panics if the account does not exist.
+func (b *BlockGen) TxNonce(addr common.Address) uint64 {
+ if !b.statedb.HasAccount(addr) {
+ panic("account does not exist")
+ }
+ return b.statedb.GetNonce(addr)
}
-// makeHeader creates the header for a new empty block, simulating
-// what miner would do. We seed chains by the first byte of the coinbase.
-func makeHeader(parent *types.Block, i int, db common.Database, seed int) *types.Header {
- var addr common.Address
- addr[0], addr[19] = byte(seed), byte(i) // 'random' coinbase
- time := parent.Time() + 10 // block time is fixed at 10 seconds
-
- // ensure that the block's coinbase has the block reward in the state.
- state := state.New(parent.Root(), db)
- cbase := state.GetOrNewStateObject(addr)
- cbase.SetGasLimit(CalcGasLimit(parent))
- cbase.AddBalance(BlockReward)
- state.Update()
+// AddUncle adds an uncle header to the generated block.
+func (b *BlockGen) AddUncle(h *types.Header) {
+ b.uncles = append(b.uncles, h)
+}
- return &types.Header{
- Root: state.Root(),
- ParentHash: parent.Hash(),
- Coinbase: addr,
- Difficulty: CalcDifficulty(time, parent.Time(), parent.Difficulty()),
- Number: new(big.Int).Add(parent.Number(), common.Big1),
- Time: uint64(time),
- GasLimit: CalcGasLimit(parent),
+// PrevBlock returns a previously generated block by number. It panics if
+// num is greater or equal to the number of the block being generated.
+// For index -1, PrevBlock returns the parent block given to GenerateChain.
+func (b *BlockGen) PrevBlock(index int) *types.Block {
+ if index >= b.i {
+ panic("block index out of range")
}
+ if index == -1 {
+ return b.parent
+ }
+ return b.chain[index]
}
-// makeChain creates a valid chain of empty blocks.
-func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks {
- bman.bc.currentBlock = parent
- blocks := make(types.Blocks, max)
- for i := 0; i < max; i++ {
- block := types.NewBlock(makeHeader(parent, i, db, seed), nil, nil, nil)
- // Use ProcessWithParent to verify that we have produced a valid block.
- _, err := bman.processWithParent(block, parent)
- if err != nil {
- fmt.Println("process with parent failed", err)
- panic(err)
+// GenerateChain creates a chain of n blocks. The first block's
+// parent will be the provided parent. db is used to store
+// intermediate states and should contain the parent's state trie.
+//
+// The generator function is called with a new block generator for
+// every block. Any transactions and uncles added to the generator
+// become part of the block. If gen is nil, the blocks will be empty
+// and their coinbase will be the zero address.
+//
+// Blocks created by GenerateChain do not contain valid proof of work
+// values. Inserting them into ChainManager requires use of FakePow or
+// a similar non-validating proof of work implementation.
+func GenerateChain(parent *types.Block, db common.Database, n int, gen func(int, *BlockGen)) []*types.Block {
+ statedb := state.New(parent.Root(), db)
+ blocks := make(types.Blocks, n)
+ genblock := func(i int, h *types.Header) *types.Block {
+ b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
+ if gen != nil {
+ gen(i, b)
}
+ AccumulateRewards(statedb, h, b.uncles)
+ statedb.Update()
+ h.Root = statedb.Root()
+ return types.NewBlock(h, b.txs, b.uncles, b.receipts)
+ }
+ for i := 0; i < n; i++ {
+ header := makeHeader(parent, statedb)
+ block := genblock(i, header)
block.Td = CalcTD(block, parent)
blocks[i] = block
parent = block
@@ -93,41 +149,38 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Dat
return blocks
}
-// Create a new chain manager starting from given block
-// Effectively a fork factory
-func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager {
- genesis := GenesisBlock(0, db)
- bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: eventMux, pow: FakePow{}}
- bc.txState = state.ManageState(state.New(genesis.Root(), db))
- bc.futureBlocks = NewBlockCache(1000)
- if block == nil {
- bc.Reset()
- } else {
- bc.currentBlock = block
- bc.td = block.Td
+func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
+ time := parent.Time() + 10 // block time is fixed at 10 seconds
+ return &types.Header{
+ Root: state.Root(),
+ ParentHash: parent.Hash(),
+ Coinbase: parent.Coinbase(),
+ Difficulty: CalcDifficulty(time, parent.Time(), parent.Difficulty()),
+ GasLimit: CalcGasLimit(parent),
+ GasUsed: new(big.Int),
+ Number: new(big.Int).Add(parent.Number(), common.Big1),
+ Time: uint64(time),
}
- return bc
-}
-
-// block processor with fake pow
-func newBlockProcessor(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
- chainMan := newChainManager(nil, eventMux, db)
- bman := NewBlockProcessor(db, db, FakePow{}, chainMan, eventMux)
- return bman
}
-// Make a new, deterministic canonical chain by running InsertChain
-// on result of makeChain.
+// newCanonical creates a new deterministic canonical chain by running
+// InsertChain on the result of makeChain.
func newCanonical(n int, db common.Database) (*BlockProcessor, error) {
- eventMux := &event.TypeMux{}
-
- bman := newBlockProcessor(db, newChainManager(nil, eventMux, db), eventMux)
+ evmux := &event.TypeMux{}
+ chainman, _ := NewChainManager(GenesisBlock(0, db), db, db, FakePow{}, evmux)
+ bman := NewBlockProcessor(db, db, FakePow{}, chainman, evmux)
bman.bc.SetProcessor(bman)
parent := bman.bc.CurrentBlock()
if n == 0 {
return bman, nil
}
- lchain := makeChain(bman, parent, n, db, CanonicalSeed)
+ lchain := makeChain(parent, n, db, canonicalSeed)
_, err := bman.bc.InsertChain(lchain)
return bman, err
}
+
+func makeChain(parent *types.Block, n int, db common.Database, seed int) []*types.Block {
+ return GenerateChain(parent, db, n, func(i int, b *BlockGen) {
+ b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
+ })
+}