core, miner: ChainFork as BlockReader. Updated VM Env to use reader fn

ChainFork is now usable as a BlockReader interface and can be used to
read out chain data.

The VM Env is now taking a block-number-to-hash function instead of
BlockChain to create a block-number-to-hash internally. This makes it
slightly more flexible to use and allows us to pass around the ChainFork
NumToHash method.
This commit is contained in:
Jeffrey Wilcke 2016-05-30 14:48:48 +02:00
parent 7d5e2443e2
commit 888b5c706e
11 changed files with 63 additions and 30 deletions

View file

@ -120,7 +120,7 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
data: data,
}
// Execute the call and return
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
vmenv := core.NewEnv(statedb, chainConfig, core.GetHashFn(block.ParentHash(), b.blockchain), msg, block.Header(), vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
out, _, err := core.ApplyMessage(vmenv, msg, gaspool)
@ -168,7 +168,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
data: data,
}
// Execute the call and return
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
vmenv := core.NewEnv(statedb, chainConfig, core.GetHashFn(block.ParentHash(), b.blockchain), msg, block.Header(), vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
_, gas, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()

View file

@ -191,7 +191,7 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr
}
header := be.bc.CurrentBlock().Header()
vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
vmenv := core.NewEnv(statedb, be.config, core.GetHashFn(header.ParentHash, be.bc), msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
res, gas, err := core.ApplyMessage(vmenv, msg, gp)

View file

@ -162,6 +162,11 @@ func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux
return bc, nil
}
// XXX temporary
func (self *BlockChain) Db() ethdb.Database {
return self.chainDb
}
func (self *BlockChain) getProcInterrupt() bool {
return atomic.LoadInt32(&self.procInterrupt) == 1
}
@ -896,7 +901,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
// Process block using the parent state as reference point.
receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig)
receipts, logs, usedGas, err := self.processor.Process(GetHashFn(block.ParentHash(), self), block, statedb, self.config.VmConfig)
if err != nil {
reportBlock(block, err)
return i, err

View file

@ -141,7 +141,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
if err != nil {
return err
}
receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{})
receipts, _, usedGas, err := blockchain.Processor().Process(GetHashFn(block.ParentHash(), blockchain), block, statedb, vm.Config{})
if err != nil {
reportBlock(block, err)
return err
@ -435,7 +435,7 @@ func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return n
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
return nil
}
func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
func (bproc) Process(getHashFn BlockNoToHashFn, block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
return nil, nil, nil, nil
}

View file

@ -69,16 +69,18 @@ type ChainFork struct {
origin *types.Block // origin notes the start of the fork
currentBlock *types.Block // the current block within this transaction
changes []Changes // changes
changes []Changes // changes
hashToIdx map[common.Hash]int // mapping from block hash to array changes index
}
// Fork returns a new blockchain with the given database as backing layer
// for the localised blockchain transaction.
func Fork(config *ChainConfig, blockReader BlockReader, origin common.Hash) (*ChainFork, error) {
fork := &ChainFork{
db: blockReader.Db(),
reader: blockReader,
config: config,
db: blockReader.Db(),
reader: blockReader,
config: config,
hashToIdx: make(map[common.Hash]int),
}
// get the origin block from which this fork originates
@ -120,10 +122,30 @@ func (fork *ChainFork) GetNumHash(n uint64) common.Hash {
return common.Hash{}
}
// State returns the current pending state of the fork. This state is re-used throughout the entire
// session of the fork.
func (fork *ChainFork) State() *state.StateDB {
return fork.state
}
// GetBlock returns the block within the fork that corresponds to the given hash. If the
// block is not found within the fork nil will be returned -- this is subject to change
// and might include blocks that lay outside of the fork, in the future.
func (fork *ChainFork) GetBlock(hash common.Hash) *types.Block {
if len(fork.changes) == 0 {
if fork.origin.Hash() == hash {
return fork.origin
}
return nil
}
if idx, ok := fork.hashToIdx[hash]; ok {
return fork.changes[idx].block
}
return nil
}
// CommitBlock commits a new block to the fork. The block that's being commited their parent hash must
// match the previously committed block or the origin if the fork is empty.
func (fork *ChainFork) CommitBlock(td *big.Int, block *types.Block, receipts types.Receipts) error {
@ -135,6 +157,7 @@ func (fork *ChainFork) CommitBlock(td *big.Int, block *types.Block, receipts typ
return errUnboundedParent
}
fork.hashToIdx[block.Hash()] = len(fork.changes)
fork.changes = append(fork.changes, Changes{
td: td,
block: block,

View file

@ -59,5 +59,10 @@ func TestGetNumHash(t *testing.T) {
if fork.GetNumHash(fork.originN+i+1) != blocks[i].Block.Hash() {
t.Errorf("%d failed: expected %x got %x", i, fork.GetNumHash(fork.originN+i+1), blocks[i].Block.Hash())
}
idx := fork.hashToIdx[blocks[i].Block.Hash()]
if uint64(idx) != i {
t.Errorf("%d failed: expected hash %x to map to %d got %d", i, blocks[i].Block.Hash(), i, idx)
}
}
}

View file

@ -56,7 +56,7 @@ func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
func (p *StateProcessor) Process(getHashFn BlockNoToHashFn, block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
var (
receipts types.Receipts
totalUsedGas = big.NewInt(0)
@ -68,7 +68,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i)
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
receipt, logs, _, err := ApplyTransaction(p.config, getHashFn, gp, statedb, header, tx, totalUsedGas, cfg)
if err != nil {
return nil, nil, totalUsedGas, err
}
@ -85,8 +85,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
//
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
func ApplyTransaction(config *ChainConfig, getHashFn BlockNoToHashFn, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
_, gas, err := ApplyMessage(NewEnv(statedb, config, getHashFn, tx, header, cfg), tx, gp)
if err != nil {
return nil, nil, nil, err
}

View file

@ -61,7 +61,7 @@ type HeaderValidator interface {
// of gas used in the process and return an error if any of the internal rules
// failed.
type Processor interface {
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
Process(getHashFn BlockNoToHashFn, block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
}
// Backend is an interface defining the basic functionality for an operable node

View file

@ -28,9 +28,9 @@ import (
// GetHashFn returns a function for which the VM env can query block hashes through
// up to the limit defined by the Yellow Paper and uses the given block chain
// to query for information.
func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
func GetHashFn(ref common.Hash, bc *BlockChain) func(n uint64) common.Hash {
return func(n uint64) common.Hash {
for block := chain.GetBlock(ref); block != nil; block = chain.GetBlock(block.ParentHash()) {
for block := bc.GetBlock(ref); block != nil; block = bc.GetBlock(block.ParentHash()) {
if block.NumberU64() == n {
return block.Hash()
}
@ -47,20 +47,20 @@ type VMEnv struct {
depth int // Current execution depth
msg Message // Message appliod
header *types.Header // Header information
chain *BlockChain // Blockchain handle
logs []vm.StructLog // Logs for the custom structured logger
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
header *types.Header // Header information
logs []vm.StructLog // Logs for the custom structured logger
getHashFn BlockNoToHashFn // getHashFn callback is used to retrieve block hashes
}
func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
type BlockNoToHashFn func(n uint64) common.Hash
func NewEnv(state *state.StateDB, chainConfig *ChainConfig, getHashFn BlockNoToHashFn, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
env := &VMEnv{
chainConfig: chainConfig,
chain: chain,
state: state,
header: header,
msg: msg,
getHashFn: GetHashFn(header.ParentHash, chain),
getHashFn: getHashFn, //GetHashFn(header.ParentHash, reader),
}
// if no log collector is present set self as the collector

View file

@ -777,7 +777,7 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
}
// Execute the call and return
vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), s.config.VmConfig)
vmenv := core.NewEnv(stateDb, s.config, core.GetHashFn(block.ParentHash(), s.bc), msg, block.Header(), s.config.VmConfig)
gp := new(core.GasPool).AddGas(common.MaxBig)
res, requiredGas, _, err := core.NewStateTransition(vmenv, msg, gp).TransitionDb()
@ -1753,7 +1753,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (b
return false, collector.traces, err
}
receipts, _, usedGas, err := processor.Process(block, statedb, *config)
receipts, _, usedGas, err := processor.Process(core.GetHashFn(block.ParentHash(), api.eth.BlockChain()), block, statedb, *config)
if err != nil {
return false, collector.traces, err
}
@ -1871,7 +1871,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC
}
// Mutate the state if we haven't reached the tracing transaction yet
if uint64(idx) < txIndex {
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
vmenv := core.NewEnv(stateDb, api.config, core.GetHashFn(block.ParentHash(), api.eth.BlockChain()), msg, block.Header(), vm.Config{})
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err)
@ -1879,7 +1879,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC
continue
}
// Otherwise trace the transaction and return
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger})
vmenv := core.NewEnv(stateDb, api.config, core.GetHashFn(block.ParentHash(), api.eth.BlockChain()), msg, block.Header(), vm.Config{Debug: true, Logger: *logger})
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err)
@ -1933,7 +1933,7 @@ func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber)
}
// Execute the call and return
vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), vm.Config{
vmenv := core.NewEnv(stateDb, s.config, core.GetHashFn(block.ParentHash(), s.bc), msg, block.Header(), vm.Config{
Debug: true,
})
gp := new(core.GasPool).AddGas(common.MaxBig)

View file

@ -656,7 +656,7 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, g
}
config.ForceJit = false // disable forcing jit
receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config)
receipt, logs, _, err := core.ApplyTransaction(env.config, core.GetHashFn(env.header.ParentHash, bc), gp, env.state, env.header, tx, env.header.GasUsed, config)
if err != nil {
env.state.Set(snap)
return err, nil