core/state: improved state API and copying mechanism

The old state copy mechanism made deep copy for every object in state,
most of them not required by the new state and therefor wasting a ton of
resources. The copy of a state can be made using the `state.Fork`
function, which will allocate a new State and return it. This new object
holds a refernce to it's parent which can be used to query for any
information not currently available. When an object can not be found it
will recusively call the parent for the object until it reached the root
state and checks the state trie. Objects are now copied on demand and
will never be copied during the `state.Fork` function call.

State can also be flattened in to one single object, merging all the
state from root to current, leaving a single state with all recent
changes. State can be flattened using `state.Flatten`

In addition the following methods have changed to functions instead:

* state.Commit
* state.BatchCommit
* state.IntermediateRoot
This commit is contained in:
Jeffrey Wilcke 2016-08-05 13:03:49 +02:00
parent 7c8d8eb5f9
commit 931c6a4936
40 changed files with 899 additions and 877 deletions

View file

@ -49,8 +49,8 @@ type SimulatedBackend struct {
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
mu sync.Mutex mu sync.Mutex
pendingBlock *types.Block // Currently pending block that will be imported on request pendingBlock *types.Block // Currently pending block that will be imported on request
pendingState *state.StateDB // Currently pending state that will be the active on on request pendingState *state.State // Currently pending state that will be the active on on request
} }
// NewSimulatedBackend creates a new binding backend using a simulated blockchain // NewSimulatedBackend creates a new binding backend using a simulated blockchain
@ -176,7 +176,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy()) rval, _, err := b.callContract(ctx, call, b.pendingBlock, state.Fork(b.pendingState))
return rval, err return rval, err
} }
@ -201,13 +201,13 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
_, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy()) _, gas, err := b.callContract(ctx, call, b.pendingBlock, state.Fork(b.pendingState))
return gas, err return gas, err
} }
// callContract implemens common code between normal and pending contract calls. // callContract implemens common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary. // state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.State) ([]byte, *big.Int, error) {
// Ensure message is initialized properly. // Ensure message is initialized properly.
if call.GasPrice == nil { if call.GasPrice == nil {
call.GasPrice = big.NewInt(1) call.GasPrice = big.NewInt(1)

View file

@ -115,17 +115,14 @@ func run(ctx *cli.Context) error {
glog.SetToStderr(true) glog.SetToStderr(true)
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
sender := statedb.CreateAccount(common.StringToAddress("sender"))
logger := vm.NewStructLogger(nil) logger := vm.NewStructLogger(nil)
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{ vmenv := NewEnv(common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{
Debug: ctx.GlobalBool(DebugFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name),
ForceJit: ctx.GlobalBool(ForceJitFlag.Name), ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name), EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
Tracer: logger, Tracer: logger,
}) })
sender := vmenv.state.CreateAccount(common.StringToAddress("sender"))
tstart := time.Now() tstart := time.Now()
@ -143,7 +140,7 @@ func run(ctx *cli.Context) error {
common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(ValueFlag.Name)),
) )
} else { } else {
receiver := statedb.CreateAccount(common.StringToAddress("receiver")) receiver := vmenv.state.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
ret, err = vmenv.Call( ret, err = vmenv.Call(
sender, sender,
@ -156,8 +153,8 @@ func run(ctx *cli.Context) error {
vmdone := time.Since(tstart) vmdone := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) { if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit() state.Commit(vmenv.state)
fmt.Println(string(statedb.Dump())) fmt.Println(string(vmenv.state.Dump()))
} }
vm.StdErrFormat(logger.StructLogs()) vm.StdErrFormat(logger.StructLogs())
@ -190,7 +187,7 @@ func main() {
} }
type VMEnv struct { type VMEnv struct {
state *state.StateDB state *state.State
block *types.Block block *types.Block
transactor *common.Address transactor *common.Address
@ -204,9 +201,18 @@ type VMEnv struct {
evm *vm.EVM evm *vm.EVM
} }
func NewEnv(state *state.StateDB, transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv { func NewEnv(transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv {
db, err := ethdb.NewMemDatabase()
if err != nil {
panic(err)
}
st, err := state.New(common.Hash{}, db)
if err != nil {
panic(err)
}
env := &VMEnv{ env := &VMEnv{
state: state, state: st,
transactor: &transactor, transactor: &transactor,
value: value, value: value,
price: price, price: price,
@ -222,29 +228,34 @@ type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true } func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} } func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
func (self *VMEnv) Vm() vm.Vm { return self.evm } func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state } func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() } func (self *VMEnv) Origin() common.Address { return *self.transactor }
func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) } func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
func (self *VMEnv) Origin() common.Address { return *self.transactor } func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 } func (self *VMEnv) Time() *big.Int { return self.time }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor } func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
func (self *VMEnv) Time() *big.Int { return self.time } func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 } func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) Value() *big.Int { return self.value } func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) } func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash { func (self *VMEnv) GetHash(n uint64) common.Hash {
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 { if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
return self.block.Hash() return self.block.Hash()
} }
return common.Hash{} return common.Hash{}
} }
func (self *VMEnv) ForkState() vm.Database {
self.state = state.Fork(self.state)
return self.state
}
func (self *VMEnv) SetState(st vm.Database) {
self.state = st.(*state.State)
}
func (self *VMEnv) AddLog(log *vm.Log) { func (self *VMEnv) AddLog(log *vm.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }

View file

@ -110,7 +110,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
// transition, such as amount of used gas, the receipt roots and the state root // transition, such as amount of used gas, the receipt roots and the state root
// itself. ValidateState returns a database batch if the validation was a success // itself. ValidateState returns a database batch if the validation was a success
// otherwise nil and an error is returned. // otherwise nil and an error is returned.
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) { func (v *BlockValidator) ValidateState(block, parent *types.Block, st *state.State, receipts types.Receipts, usedGas *big.Int) (err error) {
header := block.Header() header := block.Header()
if block.GasUsed().Cmp(usedGas) != 0 { if block.GasUsed().Cmp(usedGas) != 0 {
return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas)) return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas))
@ -128,7 +128,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
} }
// Validate the state root against the received state root and throw // Validate the state root against the received state root and throw
// an error if they don't match. // an error if they don't match.
if root := statedb.IntermediateRoot(); header.Root != root { if root := state.IntermediateRoot(st); header.Root != root {
return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
} }
return nil return nil

View file

@ -347,7 +347,7 @@ func (self *BlockChain) Processor() Processor {
func (self *BlockChain) AuxValidator() pow.PoW { return self.pow } func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
// State returns a new mutable state based on the current HEAD block. // State returns a new mutable state based on the current HEAD block.
func (self *BlockChain) State() (*state.StateDB, error) { func (self *BlockChain) State() (*state.State, error) {
return state.New(self.CurrentBlock().Root(), self.chainDb) return state.New(self.CurrentBlock().Root(), self.chainDb)
} }
@ -826,7 +826,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
tstart = time.Now() tstart = time.Now()
nonceChecked = make([]bool, len(chain)) nonceChecked = make([]bool, len(chain))
statedb *state.StateDB st *state.State
) )
// Start the parallel nonce verifier. // Start the parallel nonce verifier.
@ -893,29 +893,31 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// Create a new statedb using the parent block and report an // Create a new statedb using the parent block and report an
// error if it fails. // error if it fails.
if statedb == nil { if st == nil {
statedb, err = state.New(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), self.chainDb) st, err = state.New(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), self.chainDb)
} else { } else {
err = statedb.Reset(chain[i-1].Root()) err = st.Reset(chain[i-1].Root())
} }
if err != nil { if err != nil {
reportBlock(block, err) reportBlock(block, err)
return i, err return i, err
} }
// Process block using the parent state as reference point. // 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(block, st, self.config.VmConfig)
if err != nil { if err != nil {
reportBlock(block, err) reportBlock(block, err)
return i, err return i, err
} }
// Validate the state using the default validator // Validate the state using the default validator
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas) err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), st, receipts, usedGas)
if err != nil { if err != nil {
reportBlock(block, err) reportBlock(block, err)
return i, err return i, err
} }
// flatten the state before committing.
st = state.Flatten(st)
// Write state changes to database // Write state changes to database
_, err = statedb.Commit() _, err = state.Commit(st)
if err != nil { if err != nil {
return i, err return i, err
} }

View file

@ -154,7 +154,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.mu.Lock() blockchain.mu.Lock()
WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
WriteBlock(blockchain.chainDb, block) WriteBlock(blockchain.chainDb, block)
statedb.Commit() state.Commit(statedb)
blockchain.mu.Unlock() blockchain.mu.Unlock()
} }
return nil return nil
@ -432,10 +432,10 @@ type bproc struct{}
func (bproc) ValidateBlock(*types.Block) error { return nil } func (bproc) ValidateBlock(*types.Block) error { return nil }
func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil } func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil }
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error { func (bproc) ValidateState(block, parent *types.Block, state *state.State, receipts types.Receipts, usedGas *big.Int) error {
return nil return nil
} }
func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) { func (bproc) Process(block *types.Block, statedb *state.State, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
return nil, nil, nil, nil return nil, nil, nil, nil
} }

View file

@ -63,11 +63,11 @@ var (
// BlockGen creates blocks for testing. // BlockGen creates blocks for testing.
// See GenerateChain for a detailed explanation. // See GenerateChain for a detailed explanation.
type BlockGen struct { type BlockGen struct {
i int i int
parent *types.Block parent *types.Block
chain []*types.Block chain []*types.Block
header *types.Header header *types.Header
statedb *state.StateDB state *state.State
gasPool *GasPool gasPool *GasPool
txs []*types.Transaction txs []*types.Transaction
@ -105,11 +105,12 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
if b.gasPool == nil { if b.gasPool == nil {
b.SetCoinbase(common.Address{}) b.SetCoinbase(common.Address{})
} }
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) b.state.PrepareIntermediate(tx.Hash(), common.Hash{}, len(b.txs))
receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) st, receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.state, b.header, tx, b.header.GasUsed, vm.Config{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
b.state = st
b.txs = append(b.txs, tx) b.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt) b.receipts = append(b.receipts, receipt)
} }
@ -131,10 +132,10 @@ func (b *BlockGen) AddUncheckedReceipt(receipt *types.Receipt) {
// TxNonce returns the next valid transaction nonce for the // TxNonce returns the next valid transaction nonce for the
// account at addr. It panics if the account does not exist. // account at addr. It panics if the account does not exist.
func (b *BlockGen) TxNonce(addr common.Address) uint64 { func (b *BlockGen) TxNonce(addr common.Address) uint64 {
if !b.statedb.HasAccount(addr) { if !b.state.HasAccount(addr) {
panic("account does not exist") panic("account does not exist")
} }
return b.statedb.GetNonce(addr) return b.state.GetNonce(addr)
} }
// AddUncle adds an uncle header to the generated block. // AddUncle adds an uncle header to the generated block.
@ -180,8 +181,12 @@ func (b *BlockGen) OffsetTime(seconds int64) {
// a similar non-validating proof of work implementation. // a similar non-validating proof of work implementation.
func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) { genblock := func(i int, h *types.Header, st *state.State) (*types.Block, types.Receipts) {
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb} b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, state: st}
// this looks a bit weird, but will catch annoying issues. st should not reused for
// any code here. b.state should be used instead. If anything uses the st now by accident
// it will hard crash instead and directly point towards this comment.
st = nil
// Mutate the state and block according to any hard-fork specs // Mutate the state and block according to any hard-fork specs
if config == nil { if config == nil {
@ -196,14 +201,14 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
} }
} }
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 { if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 {
ApplyDAOHardFork(statedb) ApplyDAOHardFork(b.state)
} }
// Execute any user modifications to the block and finalize it // Execute any user modifications to the block and finalize it
if gen != nil { if gen != nil {
gen(i, b) gen(i, b)
} }
AccumulateRewards(statedb, h, b.uncles) AccumulateRewards(b.state, h, b.uncles)
root, err := statedb.Commit() root, err := state.Commit(b.state)
if err != nil { if err != nil {
panic(fmt.Sprintf("state write error: %v", err)) panic(fmt.Sprintf("state write error: %v", err))
} }
@ -211,12 +216,12 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
return types.NewBlock(h, b.txs, b.uncles, b.receipts), b.receipts return types.NewBlock(h, b.txs, b.uncles, b.receipts), b.receipts
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), db) st, err := state.New(parent.Root(), db)
if err != nil { if err != nil {
panic(err) panic(err)
} }
header := makeHeader(parent, statedb) header := makeHeader(parent, st)
block, receipt := genblock(i, header, statedb) block, receipt := genblock(i, header, st)
blocks[i] = block blocks[i] = block
receipts[i] = receipt receipts[i] = receipt
parent = block parent = block
@ -224,7 +229,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
return blocks, receipts return blocks, receipts
} }
func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { func makeHeader(parent *types.Block, st *state.State) *types.Header {
var time *big.Int var time *big.Int
if parent.Time() == nil { if parent.Time() == nil {
time = big.NewInt(10) time = big.NewInt(10)
@ -232,7 +237,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
} }
return &types.Header{ return &types.Header{
Root: state.IntermediateRoot(), Root: state.IntermediateRoot(st),
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(), Coinbase: parent.Coinbase(),
Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),

View file

@ -60,13 +60,13 @@ func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork // ApplyDAOHardFork modifies the state database according to the DAO hard-fork
// rules, transferring all balances of a set of DAO accounts to a single refund // rules, transferring all balances of a set of DAO accounts to a single refund
// contract. // contract.
func ApplyDAOHardFork(statedb *state.StateDB) { func ApplyDAOHardFork(state *state.State) {
// Retrieve the contract to refund balances into // Retrieve the contract to refund balances into
refund := statedb.GetOrNewStateObject(params.DAORefundContract) refund := state.GetOrNewStateObject(params.DAORefundContract)
// Move every DAO account and extra-balance account funds into the refund contract // Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList { for _, addr := range params.DAODrainList {
if account := statedb.GetStateObject(addr); account != nil { if account := state.GetStateObject(addr); account != nil {
refund.AddBalance(account.Balance()) refund.AddBalance(account.Balance())
account.SetBalance(new(big.Int)) account.SetBalance(new(big.Int))
} }

View file

@ -85,7 +85,9 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
createAccount = true createAccount = true
} }
snapshotPreTransfer := env.MakeSnapshot() snapshotPreTransfer := env.Db()
// preserve the envs state and move to a new state
env.ForkState()
var ( var (
from = env.Db().GetAccount(caller.Address()) from = env.Db().GetAccount(caller.Address())
to vm.Account to vm.Account
@ -129,7 +131,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
contract.UseGas(contract.Gas()) contract.UseGas(contract.Gas())
env.SetSnapshot(snapshotPreTransfer) env.SetState(snapshotPreTransfer)
} }
return ret, addr, err return ret, addr, err
@ -144,7 +146,8 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
return nil, common.Address{}, vm.DepthError return nil, common.Address{}, vm.DepthError
} }
snapshot := env.MakeSnapshot() snapshot := env.Db()
env.ForkState()
var to vm.Account var to vm.Account
if !env.Db().Exist(*toAddr) { if !env.Db().Exist(*toAddr) {
@ -162,7 +165,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
if err != nil { if err != nil {
contract.UseGas(contract.Gas()) contract.UseGas(contract.Gas())
env.SetSnapshot(snapshot) env.SetState(snapshot)
} }
return ret, addr, err return ret, addr, err

View file

@ -64,16 +64,16 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
} }
// creating with empty hash always works // creating with empty hash always works
statedb, _ := state.New(common.Hash{}, chainDb) st, _ := state.New(common.Hash{}, chainDb)
for addr, account := range genesis.Alloc { for addr, account := range genesis.Alloc {
address := common.HexToAddress(addr) address := common.HexToAddress(addr)
statedb.AddBalance(address, common.String2Big(account.Balance)) st.AddBalance(address, common.String2Big(account.Balance))
statedb.SetCode(address, common.Hex2Bytes(account.Code)) st.SetCode(address, common.Hex2Bytes(account.Code))
for key, value := range account.Storage { for key, value := range account.Storage {
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) st.SetState(address, common.HexToHash(key), common.HexToHash(value))
} }
} }
root, stateBatch := statedb.CommitBatch() root, stateBatch := state.CommitBatch(st)
difficulty := common.String2Big(genesis.Difficulty) difficulty := common.String2Big(genesis.Difficulty)
block := types.NewBlock(&types.Header{ block := types.NewBlock(&types.Header{
@ -125,10 +125,10 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
// GenesisBlockForTesting creates a block in which addr has the given wei balance. // GenesisBlockForTesting creates a block in which addr has the given wei balance.
// The state trie of the block is written to db. the passed db needs to contain a state root // The state trie of the block is written to db. the passed db needs to contain a state root
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
statedb, _ := state.New(common.Hash{}, db) st, _ := state.New(common.Hash{}, db)
obj := statedb.GetOrNewStateObject(addr) obj := st.GetOrNewStateObject(addr)
obj.SetBalance(balance) obj.SetBalance(balance)
root, err := statedb.Commit() root, err := state.Commit(st)
if err != nil { if err != nil {
panic(fmt.Sprintf("cannot write state: %v", err)) panic(fmt.Sprintf("cannot write state: %v", err))
} }

View file

@ -37,16 +37,16 @@ type World struct {
Accounts map[string]Account `json:"accounts"` Accounts map[string]Account `json:"accounts"`
} }
func (self *StateDB) RawDump() World { func (self *State) RawDump() World {
world := World{ world := World{
Root: common.Bytes2Hex(self.trie.Root()), Root: common.Bytes2Hex(self.Trie.Root()),
Accounts: make(map[string]Account), Accounts: make(map[string]Account),
} }
it := self.trie.Iterator() it := self.Trie.Iterator()
for it.Next() { for it.Next() {
addr := self.trie.GetKey(it.Key) addr := self.Trie.GetKey(it.Key)
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value) stateObject, err := DecodeObject(common.BytesToAddress(addr), self.Db, it.Value)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -61,14 +61,14 @@ func (self *StateDB) RawDump() World {
} }
storageIt := stateObject.trie.Iterator() storageIt := stateObject.trie.Iterator()
for storageIt.Next() { for storageIt.Next() {
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) account.Storage[common.Bytes2Hex(self.Trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
} }
world.Accounts[common.Bytes2Hex(addr)] = account world.Accounts[common.Bytes2Hex(addr)] = account
} }
return world return world
} }
func (self *StateDB) Dump() []byte { func (self *State) Dump() []byte {
json, err := json.MarshalIndent(self.RawDump(), "", " ") json, err := json.MarshalIndent(self.RawDump(), "", " ")
if err != nil { if err != nil {
fmt.Println("dump err", err) fmt.Println("dump err", err)

View file

@ -29,7 +29,7 @@ import (
// NodeIterator is an iterator to traverse the entire state trie post-order, // NodeIterator is an iterator to traverse the entire state trie post-order,
// including all of the contract code and contract state tries. // including all of the contract code and contract state tries.
type NodeIterator struct { type NodeIterator struct {
state *StateDB // State being iterated state *State // State being iterated
stateIt *trie.NodeIterator // Primary iterator for the global state trie stateIt *trie.NodeIterator // Primary iterator for the global state trie
dataIt *trie.NodeIterator // Secondary iterator for the data trie of a contract dataIt *trie.NodeIterator // Secondary iterator for the data trie of a contract
@ -46,7 +46,7 @@ type NodeIterator struct {
} }
// NewNodeIterator creates an post-order state node iterator. // NewNodeIterator creates an post-order state node iterator.
func NewNodeIterator(state *StateDB) *NodeIterator { func NewNodeIterator(state *State) *NodeIterator {
return &NodeIterator{ return &NodeIterator{
state: state, state: state,
} }
@ -76,7 +76,7 @@ func (it *NodeIterator) step() error {
} }
// Initialize the iterator if we've just started // Initialize the iterator if we've just started
if it.stateIt == nil { if it.stateIt == nil {
it.stateIt = trie.NewNodeIterator(it.state.trie.Trie) it.stateIt = trie.NewNodeIterator(it.state.Trie.Trie)
} }
// If we had data nodes previously, we surely have at least state nodes // If we had data nodes previously, we surely have at least state nodes
if it.dataIt != nil { if it.dataIt != nil {
@ -115,7 +115,7 @@ func (it *NodeIterator) step() error {
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account); err != nil { if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account); err != nil {
return err return err
} }
dataTrie, err := trie.New(account.Root, it.state.db) dataTrie, err := trie.New(account.Root, it.state.Db)
if err != nil { if err != nil {
return err return err
} }
@ -125,7 +125,7 @@ func (it *NodeIterator) step() error {
} }
if bytes.Compare(account.CodeHash, emptyCodeHash) != 0 { if bytes.Compare(account.CodeHash, emptyCodeHash) != 0 {
it.codeHash = common.BytesToHash(account.CodeHash) it.codeHash = common.BytesToHash(account.CodeHash)
it.code, err = it.state.db.Get(account.CodeHash) it.code, err = it.state.Db.Get(account.CodeHash)
if err != nil { if err != nil {
return fmt.Errorf("code %x: %v", account.CodeHash, err) return fmt.Errorf("code %x: %v", account.CodeHash, err)
} }

View file

@ -29,7 +29,7 @@ type account struct {
} }
type ManagedState struct { type ManagedState struct {
*StateDB *State
mu sync.RWMutex mu sync.RWMutex
@ -37,18 +37,18 @@ type ManagedState struct {
} }
// ManagedState returns a new managed state with the statedb as it's backing layer // ManagedState returns a new managed state with the statedb as it's backing layer
func ManageState(statedb *StateDB) *ManagedState { func ManageState(state *State) *ManagedState {
return &ManagedState{ return &ManagedState{
StateDB: statedb.Copy(), State: Fork(state),
accounts: make(map[string]*account), accounts: make(map[string]*account),
} }
} }
// SetState sets the backing layer of the managed state // SetState sets the backing layer of the managed state
func (ms *ManagedState) SetState(statedb *StateDB) { func (ms *ManagedState) SetState(state *State) {
ms.mu.Lock() ms.mu.Lock()
defer ms.mu.Unlock() defer ms.mu.Unlock()
ms.StateDB = statedb ms.State = state
} }
// RemoveNonce removed the nonce from the managed state and all future pending nonces // RemoveNonce removed the nonce from the managed state and all future pending nonces
@ -91,7 +91,7 @@ func (ms *ManagedState) GetNonce(addr common.Address) uint64 {
account := ms.getAccount(addr) account := ms.getAccount(addr)
return uint64(len(account.nonces)) + account.nstart return uint64(len(account.nonces)) + account.nstart
} else { } else {
return ms.StateDB.GetNonce(addr) return ms.State.GetNonce(addr)
} }
} }
@ -127,7 +127,7 @@ func (ms *ManagedState) getAccount(addr common.Address) *account {
} else { } else {
// Always make sure the state account nonce isn't actually higher // Always make sure the state account nonce isn't actually higher
// than the tracked one. // than the tracked one.
so := ms.StateDB.GetStateObject(addr) so := ms.State.GetStateObject(addr)
if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce { if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce {
ms.accounts[straddr] = newAccount(so) ms.accounts[straddr] = newAccount(so)
} }

View file

@ -30,7 +30,7 @@ func create() (*ManagedState, *account) {
statedb, _ := New(common.Hash{}, db) statedb, _ := New(common.Hash{}, db)
ms := ManageState(statedb) ms := ManageState(statedb)
so := &StateObject{address: addr, nonce: 100} so := &StateObject{address: addr, nonce: 100}
ms.StateDB.stateObjects[addr.Str()] = so ms.State.StateObjects[addr] = so
ms.accounts[addr.Str()] = newAccount(so) ms.accounts[addr.Str()] = newAccount(so)
return ms, ms.accounts[addr.Str()] return ms, ms.accounts[addr.Str()]
@ -92,7 +92,7 @@ func TestRemoteNonceChange(t *testing.T) {
account.nonces = append(account.nonces, nn...) account.nonces = append(account.nonces, nn...)
nonce := ms.NewNonce(addr) nonce := ms.NewNonce(addr)
ms.StateDB.stateObjects[addr.Str()].nonce = 200 ms.State.StateObjects[addr].nonce = 200
nonce = ms.NewNonce(addr) nonce = ms.NewNonce(addr)
if nonce != 200 { if nonce != 200 {
t.Error("expected nonce after remote update to be", 201, "got", nonce) t.Error("expected nonce after remote update to be", 201, "got", nonce)
@ -100,7 +100,7 @@ func TestRemoteNonceChange(t *testing.T) {
ms.NewNonce(addr) ms.NewNonce(addr)
ms.NewNonce(addr) ms.NewNonce(addr)
ms.NewNonce(addr) ms.NewNonce(addr)
ms.StateDB.stateObjects[addr.Str()].nonce = 200 ms.State.StateObjects[addr].nonce = 200
nonce = ms.NewNonce(addr) nonce = ms.NewNonce(addr)
if nonce != 204 { if nonce != 204 {
t.Error("expected nonce after remote update to be", 201, "got", nonce) t.Error("expected nonce after remote update to be", 201, "got", nonce)
@ -118,7 +118,7 @@ func TestSetNonce(t *testing.T) {
} }
addr[0] = 1 addr[0] = 1
ms.StateDB.SetNonce(addr, 1) ms.State.SetNonce(addr, 1)
if ms.GetNonce(addr) != 1 { if ms.GetNonce(addr) != 1 {
t.Error("Expected nonce of 1, got", ms.GetNonce(addr)) t.Error("Expected nonce of 1, got", ms.GetNonce(addr))

394
core/state/state.go Normal file
View file

@ -0,0 +1,394 @@
package state
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
var StartingNonce uint64 // StartingNonce determines the nonce used when a new account is initialised.
// State keeps a Log of changes that occured during the sessions such as state object changes, Refunds and Logs. It also keeps a reference to it's parent state to retrieve data if anything is missing from this snapshot. This will allow us to copy on demand.
type State struct {
Db ethdb.Database
Trie *trie.SecureTrie
parent *State
StateObjects map[common.Address]*StateObject
refund *big.Int
logIdx uint
logs []*vm.Log
interInfo intermediateInfo
MarkedTransition bool // marks the transition between transactions
}
type intermediateInfo struct {
txHash, blockHash common.Hash
txIdx uint
}
// Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*State, error) {
tr, err := trie.NewSecure(root, db)
if err != nil {
return nil, err
}
return &State{
Db: db,
Trie: tr,
StateObjects: make(map[common.Address]*StateObject),
refund: new(big.Int),
}, nil
}
func (s *State) PrepareIntermediate(txHash, blockHash common.Hash, txIdx int) {
if s.parent != nil {
s.parent.MarkedTransition = true
}
s.interInfo = intermediateInfo{txHash: txHash, blockHash: blockHash, txIdx: uint(txIdx)}
s.refund = new(big.Int)
}
// Flatten flattens one's own state
func (s *State) Flatten() {
*s = *Flatten(s)
}
// Read fetches the state object with the given address by first checking its own cache of state objects. If that didn't result in an object it will attempt to check it's line of ancestors.
func (s *State) Read(address common.Address) (object *StateObject, inCache bool) {
stateObject := s.StateObjects[address]
if stateObject == nil && s.parent != nil {
stateObject, _ = s.parent.Read(address)
} else if stateObject != nil {
inCache = true
}
if stateObject != nil {
if stateObject.deleted {
return nil, false
}
return stateObject, inCache
}
data := s.Trie.Get(address[:])
if len(data) == 0 {
return nil, false
}
var err error
stateObject, err = DecodeObject(address, s.Db, data)
if err != nil {
glog.Errorf("can't decode object at %x: %v", address[:], err)
return nil, false
}
return stateObject, false
}
func (s *State) GetOrNewStateObject(address common.Address) *StateObject {
stateObject := s.GetStateObject(address)
if stateObject == nil || stateObject.deleted {
stateObject = NewStateObject(address, s.Db)
stateObject.SetNonce(StartingNonce)
s.StateObjects[address] = stateObject
}
return stateObject
}
func (s *State) GetStateObject(address common.Address) *StateObject {
account, inCache := s.Read(address)
if account != nil {
if !inCache {
s.StateObjects[address] = account.Copy()
}
return s.StateObjects[address]
}
return nil
}
func (s *State) CreateStateObject(address common.Address) *StateObject {
// Get previous (if any)
so := s.GetStateObject(address)
// Create a new one
stateObject := NewStateObject(address, s.Db)
stateObject.SetNonce(StartingNonce)
s.StateObjects[address] = stateObject
// If it existed set the balance to the new account
if so != nil {
stateObject.balance = so.balance
}
return stateObject
}
func (s *State) AddBalance(address common.Address, amount *big.Int) {
stateObject := s.GetOrNewStateObject(address)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
func (s *State) GetBalance(address common.Address) *big.Int {
stateObject := s.GetStateObject(address)
if stateObject != nil {
return stateObject.balance
}
return common.Big0
}
func (s *State) GetNonce(address common.Address) uint64 {
stateObject := s.GetStateObject(address)
if stateObject != nil {
return stateObject.nonce
}
return StartingNonce
}
func (s *State) SetNonce(address common.Address, nonce uint64) {
stateObject := s.GetOrNewStateObject(address)
if stateObject != nil {
stateObject.SetNonce(nonce)
}
}
func (s *State) GetCode(address common.Address) []byte {
stateObject := s.GetStateObject(address)
if stateObject != nil {
return stateObject.code
}
return nil
}
func (s *State) SetCode(address common.Address, code []byte) {
stateObject := s.GetOrNewStateObject(address)
if stateObject != nil {
stateObject.SetCode(code)
}
}
func (s *State) AddRefund(gas *big.Int) {
s.refund.Add(s.refund, gas)
}
func (s *State) GetRefund() *big.Int {
var refund *big.Int
if s.MarkedTransition {
return new(big.Int)
} else if s.parent == nil {
refund = new(big.Int)
} else {
refund = s.parent.GetRefund()
}
return refund.Add(refund, s.refund)
}
func (s *State) GetState(address common.Address, stateAddress common.Hash) common.Hash {
stateObject := s.GetStateObject(address)
if stateObject != nil {
return stateObject.GetState(stateAddress)
}
return common.Hash{}
}
func (s *State) SetState(address common.Address, stateAddress common.Hash, value common.Hash) {
stateObject := s.GetOrNewStateObject(address)
if stateObject != nil {
stateObject.SetState(stateAddress, value)
}
}
func (s *State) Delete(address common.Address) bool {
stateObject := s.GetStateObject(address)
if stateObject != nil {
stateObject.MarkForDeletion()
stateObject.balance = new(big.Int)
return true
}
return false
}
func (s *State) HasAccount(addr common.Address) bool {
return s.GetStateObject(addr) != nil
}
func (s *State) Exist(address common.Address) bool {
return s.GetStateObject(address) != nil
}
func (s *State) IsDeleted(address common.Address) bool {
stateObject := s.GetStateObject(address)
if stateObject != nil {
return stateObject.remove
}
return false
}
func (s *State) GetAccount(address common.Address) vm.Account {
return s.GetStateObject(address)
}
func (s *State) CreateAccount(address common.Address) vm.Account {
return s.CreateStateObject(address)
}
func (s *State) AddLog(log *vm.Log) {
log.TxIndex = s.interInfo.txIdx
log.TxHash = s.interInfo.txHash
log.BlockHash = s.interInfo.blockHash
log.Index = s.logIdx
s.logs = append(s.logs, log)
s.logIdx++
}
// Logs returns the logs of it's entire ancestory chain or until
// the marked transition is found (state between transactions).
func (s *State) Logs() []*vm.Log {
var logs []*vm.Log
if s.MarkedTransition {
return nil
} else if s.parent != nil {
logs = s.parent.Logs()
}
return append(logs, s.logs...)
}
func (s *State) DeleteStateObject(stateObject *StateObject) {
stateObject.deleted = true
addr := stateObject.Address()
s.Trie.Delete(addr[:])
}
func (s *State) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
s.Trie.Update(addr[:], data)
}
func (s *State) Reset(root common.Hash) error {
var (
err error
tr = s.Trie
)
if s.Trie.Hash() != root {
if tr, err = trie.NewSecure(root, s.Db); err != nil {
return err
}
}
*s = State{
Db: s.Db,
Trie: tr,
StateObjects: make(map[common.Address]*StateObject),
refund: new(big.Int),
}
return nil
}
// DeleteSuicides flags the suicided objects for deletion so that it
// won't be referenced again when called / queried up on.
//
// DeleteSuicides should not be used for consensus related updates
// under any circumstances.
func (s *State) DeleteSuicides() {
// Delete parents first
if s.parent != nil {
s.DeleteSuicides()
}
// Reset refund so that any used-gas calculations can use
// this method.
s.refund = new(big.Int)
for _, stateObject := range s.StateObjects {
if stateObject.dirty {
// If the object has been removed by a suicide
// flag the object as deleted.
if stateObject.remove {
stateObject.deleted = true
}
stateObject.dirty = false
}
}
}
// Fork preserve the given state and returns a handle to a new modifiable state
// that does not affect the preserved state.
func Fork(parent *State) *State {
return &State{
Db: parent.Db,
Trie: parent.Trie,
parent: parent,
StateObjects: make(map[common.Address]*StateObject),
refund: new(big.Int),
logIdx: parent.logIdx,
logs: nil,
}
}
func (s *State) Set(o *State) {
*s = *o
}
// Flatten flattens the state in to a single new state, including all changes of all ancestors.
func Flatten(s *State) *State {
// first commit the parent so we can overwrite changes
// later.
var flattenedState *State
if s.parent != nil {
flattenedState = Flatten(s.parent)
} else {
flattenedState = &State{
Db: s.Db,
Trie: s.Trie,
refund: new(big.Int),
StateObjects: make(map[common.Address]*StateObject),
}
}
for address, object := range s.StateObjects {
flattenedState.StateObjects[address] = object
}
flattenedState.logs = append(flattenedState.logs, s.logs...)
flattenedState.refund.Add(flattenedState.refund, s.refund)
return flattenedState
}
func IntermediateRoot(state *State) common.Hash {
s := Flatten(state)
for _, stateObject := range s.StateObjects {
if stateObject.dirty {
if stateObject.remove {
s.DeleteStateObject(stateObject)
} else {
stateObject.Update()
s.UpdateStateObject(stateObject)
}
stateObject.dirty = false
}
}
return s.Trie.Hash()
}

View file

@ -0,0 +1,15 @@
package state
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)
// StateChanges includes all changes after a StateSnapshot has been plattened and includes the state objects, refunds and logs.
type StateChanges struct {
StateObjects map[common.Address]*StateObject
Refund *big.Int
Logs []*vm.Log
}

View file

@ -1,19 +1,3 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package state package state
import ( import (
@ -24,11 +8,138 @@ import (
checker "gopkg.in/check.v1" checker "gopkg.in/check.v1"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
func TestStateForking(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
var address common.Address
object := NewStateObject(address, db)
object.SetBalance(big.NewInt(1))
state.StateObjects[address] = object
ss1 := Fork(state)
ss2 := Fork(ss1)
ss2.AddBalance(address, big.NewInt(1))
if ss1.GetBalance(address).Cmp(big.NewInt(1)) != 0 {
t.Error("expected ss2 balance to be 1")
}
if ss2.GetBalance(address).Cmp(big.NewInt(2)) != 0 {
t.Error("expected ss2 balance to be 2")
}
}
func TestCaching(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
var address common.Address
object := NewStateObject(address, db)
object.SetBalance(big.NewInt(1))
state.StateObjects[address] = object
ss1 := Fork(state)
ss2 := Fork(ss1)
if o := ss2.GetStateObject(address); o == nil {
t.Error("expected object to exist")
}
ss1objects := ss1.StateObjects
ss2objects := ss2.StateObjects
if len(ss1objects) != 0 {
t.Error("expected ss1 objects to be empty")
}
if len(ss2objects) != 1 {
t.Error("expected ss2 objects to be 1")
}
}
func TestFlatten(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
var address common.Address
object := NewStateObject(address, db)
object.SetBalance(big.NewInt(1))
state.StateObjects[address] = object
ss1 := Fork(state)
ss1.AddBalance(address, big.NewInt(1))
ss2 := Fork(ss1)
ss2.AddBalance(address, big.NewInt(1))
flat1 := Flatten(ss1)
if flat1.StateObjects[address].Balance().Cmp(big.NewInt(2)) != 0 {
t.Error("expected flat1 to have balance of 2")
}
flat2 := Flatten(ss2)
if flat2.StateObjects[address].Balance().Cmp(big.NewInt(3)) != 0 {
t.Error("expected flat2 to have balance of 3")
}
}
func TestLogs(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
state.PrepareIntermediate(common.Hash{}, common.Hash{}, 0)
state.AddLog(&vm.Log{})
unforkedLogs := state.Logs()
fork := Fork(state)
fork.PrepareIntermediate(common.Hash{}, common.Hash{}, 0)
fork.AddLog(&vm.Log{})
fork.AddLog(&vm.Log{})
forkedLogs := fork.Logs()
if len(unforkedLogs) != 1 {
t.Error("expected unforked state to have 1 log")
}
if len(forkedLogs) != 2 {
t.Error("expected forked state to have 2 log")
}
}
func BenchmarkFork(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
b.StartTimer()
revertEvery := 5
nesting := 20
for x := 0; x < nesting; x++ {
pstate := state
nstate := Fork(state)
nstate.AddBalance(common.Address{byte(x)}, big.NewInt(10))
// at the very least get the balance of each previous object
nstate.GetBalance(common.Address{byte(x - 1)})
if x%revertEvery == 0 {
state = pstate
}
}
}
}
type StateSuite struct { type StateSuite struct {
state *StateDB state *State
} }
var _ = checker.Suite(&StateSuite{}) var _ = checker.Suite(&StateSuite{})
@ -47,7 +158,8 @@ func (s *StateSuite) TestDump(c *checker.C) {
// write some of them to the trie // write some of them to the trie
s.state.UpdateStateObject(obj1) s.state.UpdateStateObject(obj1)
s.state.UpdateStateObject(obj2) s.state.UpdateStateObject(obj2)
s.state.Commit()
Commit(s.state)
// check that dump contains the state objects that are in trie // check that dump contains the state objects that are in trie
got := string(s.state.Dump()) got := string(s.state.Dump())
@ -99,35 +211,13 @@ func TestNull(t *testing.T) {
//value := common.FromHex("0x823140710bf13990e4500136726d8b55") //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
var value common.Hash var value common.Hash
state.SetState(address, common.Hash{}, value) state.SetState(address, common.Hash{}, value)
state.Commit() Commit(state)
value = state.GetState(address, common.Hash{}) value = state.GetState(address, common.Hash{})
if !common.EmptyHash(value) { if !common.EmptyHash(value) {
t.Errorf("expected empty hash. got %x", value) t.Errorf("expected empty hash. got %x", value)
} }
} }
func (s *StateSuite) TestSnapshot(c *checker.C) {
stateobjaddr := toAddr([]byte("aa"))
var storageaddr common.Hash
data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43})
// set initial state object value
s.state.SetState(stateobjaddr, storageaddr, data1)
// get snapshot of current state
snapshot := s.state.Copy()
// set new state object value
s.state.SetState(stateobjaddr, storageaddr, data2)
// restore snapshot
s.state.Set(snapshot)
// get state storage value
res := s.state.GetState(stateobjaddr, storageaddr)
c.Assert(data1, checker.DeepEquals, res)
}
// use testing instead of checker because checker does not support // use testing instead of checker because checker does not support
// printing/logging in tests (-check.vv does not work) // printing/logging in tests (-check.vv does not work)
func TestSnapshot2(t *testing.T) { func TestSnapshot2(t *testing.T) {
@ -152,8 +242,7 @@ func TestSnapshot2(t *testing.T) {
so0.remove = false so0.remove = false
so0.deleted = false so0.deleted = false
so0.dirty = true so0.dirty = true
state.SetStateObject(so0) state.StateObjects[so0.Address()] = so0
state.Commit()
// and one with deleted == true // and one with deleted == true
so1 := state.GetStateObject(stateobjaddr1) so1 := state.GetStateObject(stateobjaddr1)
@ -163,14 +252,17 @@ func TestSnapshot2(t *testing.T) {
so1.remove = true so1.remove = true
so1.deleted = true so1.deleted = true
so1.dirty = true so1.dirty = true
state.SetStateObject(so1) state.StateObjects[so1.Address()] = so1
so1 = state.GetStateObject(stateobjaddr1) so1 = state.GetStateObject(stateobjaddr1)
if so1 != nil { if so1 != nil {
t.Fatalf("deleted object not nil when getting") t.Fatalf("deleted object not nil when getting")
} }
snapshot := state.Copy() snapshot := state
state = Fork(state)
state.AddBalance(stateobjaddr0, big.NewInt(1))
state.AddBalance(stateobjaddr1, big.NewInt(1))
state.Set(snapshot) state.Set(snapshot)
so0Restored := state.GetStateObject(stateobjaddr0) so0Restored := state.GetStateObject(stateobjaddr0)
@ -222,24 +314,3 @@ func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
t.Fatalf("Dirty mismatch: have %v, want %v", so0.dirty, so1.dirty) t.Fatalf("Dirty mismatch: have %v, want %v", so0.dirty, so1.dirty)
} }
} }
func BenchmarkFork(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
b.StartTimer()
revertEvery := 5
nesting := 20
for x := 0; x < nesting; x++ {
pstate := state.Copy()
state.AddBalance(common.Address{byte(x)}, big.NewInt(10))
// at the very least get the balance of each previous object
state.GetBalance(common.Address{byte(x - 1)})
if x%revertEvery == 0 {
state.Set(pstate)
}
}
}
}

65
core/state/state_write.go Normal file
View file

@ -0,0 +1,65 @@
package state
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
// Commit commits all state changes to the database.
func Commit(state *State) (common.Hash, error) {
root, batch := CommitBatch(state)
return root, batch.Write()
}
// CommitBatch commits all state changes to a write batch but does not
// execute the batch. It is used to validate state changes against
// the root hash stored in a block.
func CommitBatch(state *State) (common.Hash, ethdb.Batch) {
batch := state.Db.NewBatch()
root, _ := stateCommit(state, batch)
return root, batch
}
func stateCommit(state *State, db trie.DatabaseWriter) (common.Hash, error) {
// make sure the state is flattened before committing
state = Flatten(state)
for _, stateObject := range state.StateObjects {
if stateObject.remove {
// If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie.
stateObject.deleted = true
state.Trie.Delete(stateObject.Address().Bytes()[:])
} else {
// Write any contract code associated with the state object
if len(stateObject.code) > 0 {
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {
return common.Hash{}, err
}
}
// Write any storage changes in the state object to its trie.
stateObject.Update()
// Commit the trie of the object to the batch.
// This updates the trie root internally, so
// getting the root hash of the storage trie
// through UpdateStateObject is fast.
if _, err := stateObject.trie.CommitTo(db); err != nil {
return common.Hash{}, err
}
// Update the object in the account trie.
addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
state.Trie.Update(addr[:], data)
}
stateObject.dirty = false
}
return state.Trie.CommitTo(db)
}

View file

@ -1,452 +0,0 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package state provides a caching layer atop the Ethereum state trie.
package state
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
// The starting nonce determines the default nonce when new accounts are being
// created.
var StartingNonce uint64
// StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
// * Contracts
// * Accounts
type StateDB struct {
db ethdb.Database
trie *trie.SecureTrie
stateObjects map[string]*StateObject
refund *big.Int
thash, bhash common.Hash
txIndex int
logs map[common.Hash]vm.Logs
logSize uint
}
// Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
tr, err := trie.NewSecure(root, db)
if err != nil {
return nil, err
}
return &StateDB{
db: db,
trie: tr,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}, nil
}
// Reset clears out all emphemeral state objects from the state db, but keeps
// the underlying state trie to avoid reloading data for the next operations.
func (self *StateDB) Reset(root common.Hash) error {
var (
err error
tr = self.trie
)
if self.trie.Hash() != root {
if tr, err = trie.NewSecure(root, self.db); err != nil {
return err
}
}
*self = StateDB{
db: self.db,
trie: tr,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}
return nil
}
func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
self.thash = thash
self.bhash = bhash
self.txIndex = ti
}
func (self *StateDB) AddLog(log *vm.Log) {
log.TxHash = self.thash
log.BlockHash = self.bhash
log.TxIndex = uint(self.txIndex)
log.Index = self.logSize
self.logs[self.thash] = append(self.logs[self.thash], log)
self.logSize++
}
func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
return self.logs[hash]
}
func (self *StateDB) Logs() vm.Logs {
var logs vm.Logs
for _, lgs := range self.logs {
logs = append(logs, lgs...)
}
return logs
}
func (self *StateDB) AddRefund(gas *big.Int) {
self.refund.Add(self.refund, gas)
}
func (self *StateDB) HasAccount(addr common.Address) bool {
return self.GetStateObject(addr) != nil
}
func (self *StateDB) Exist(addr common.Address) bool {
return self.GetStateObject(addr) != nil
}
func (self *StateDB) GetAccount(addr common.Address) vm.Account {
return self.GetStateObject(addr)
}
// Retrieve the balance from the given address or 0 if object not found
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.balance
}
return common.Big0
}
func (self *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.nonce
}
return StartingNonce
}
func (self *StateDB) GetCode(addr common.Address) []byte {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.code
}
return nil
}
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
stateObject := self.GetStateObject(a)
if stateObject != nil {
return stateObject.GetState(b)
}
return common.Hash{}
}
func (self *StateDB) IsDeleted(addr common.Address) bool {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.remove
}
return false
}
/*
* SETTERS
*/
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetNonce(nonce)
}
}
func (self *StateDB) SetCode(addr common.Address, code []byte) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetCode(code)
}
}
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetState(key, value)
}
}
func (self *StateDB) Delete(addr common.Address) bool {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.MarkForDeletion()
stateObject.balance = new(big.Int)
return true
}
return false
}
//
// Setting, updating & deleting state object methods
//
// Update the given state object and apply it to state trie
func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
self.trie.Update(addr[:], data)
}
// Delete the given state object and delete it from the state trie
func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
stateObject.deleted = true
addr := stateObject.Address()
self.trie.Delete(addr[:])
//delete(self.stateObjects, addr.Str())
}
// Retrieve a state object given my the address. Nil if not found
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
stateObject = self.stateObjects[addr.Str()]
if stateObject != nil {
if stateObject.deleted {
stateObject = nil
}
return stateObject
}
data := self.trie.Get(addr[:])
if len(data) == 0 {
return nil
}
stateObject, err := DecodeObject(addr, self.db, data)
if err != nil {
glog.Errorf("can't decode object at %x: %v", addr[:], err)
return nil
}
self.SetStateObject(stateObject)
return stateObject
}
func (self *StateDB) SetStateObject(object *StateObject) {
self.stateObjects[object.Address().Str()] = object
}
// Retrieve a state object or create a new state object if nil
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
stateObject := self.GetStateObject(addr)
if stateObject == nil || stateObject.deleted {
stateObject = self.CreateStateObject(addr)
}
return stateObject
}
// NewStateObject create a state object whether it exist in the trie or not
func (self *StateDB) newStateObject(addr common.Address) *StateObject {
if glog.V(logger.Core) {
glog.Infof("(+) %x\n", addr)
}
stateObject := NewStateObject(addr, self.db)
stateObject.SetNonce(StartingNonce)
self.stateObjects[addr.Str()] = stateObject
return stateObject
}
// Creates creates a new state object and takes ownership. This is different from "NewStateObject"
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
// Get previous (if any)
so := self.GetStateObject(addr)
// Create a new one
newSo := self.newStateObject(addr)
// If it existed set the balance to the new account
if so != nil {
newSo.balance = so.balance
}
return newSo
}
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
return self.CreateStateObject(addr)
}
//
// Setting, copying of the state methods
//
func (self *StateDB) Copy() *StateDB {
// ignore error - we assume state-to-be-copied always exists
state, _ := New(common.Hash{}, self.db)
state.trie = self.trie
for k, stateObject := range self.stateObjects {
if stateObject.dirty {
state.stateObjects[k] = stateObject.Copy()
}
}
state.refund.Set(self.refund)
for hash, logs := range self.logs {
state.logs[hash] = make(vm.Logs, len(logs))
copy(state.logs[hash], logs)
}
state.logSize = self.logSize
return state
}
func (self *StateDB) Set(state *StateDB) {
self.trie = state.trie
self.stateObjects = state.stateObjects
self.refund = state.refund
self.logs = state.logs
self.logSize = state.logSize
}
func (self *StateDB) GetRefund() *big.Int {
return self.refund
}
// IntermediateRoot computes the current root hash of the state trie.
// It is called in between transactions to get the root hash that
// goes into transaction receipts.
func (s *StateDB) IntermediateRoot() common.Hash {
s.refund = new(big.Int)
for _, stateObject := range s.stateObjects {
if stateObject.dirty {
if stateObject.remove {
s.DeleteStateObject(stateObject)
} else {
stateObject.Update()
s.UpdateStateObject(stateObject)
}
}
}
return s.trie.Hash()
}
// DeleteSuicides flags the suicided objects for deletion so that it
// won't be referenced again when called / queried up on.
//
// DeleteSuicides should not be used for consensus related updates
// under any circumstances.
func (s *StateDB) DeleteSuicides() {
// Reset refund so that any used-gas calculations can use
// this method.
s.refund = new(big.Int)
for _, stateObject := range s.stateObjects {
if stateObject.dirty {
// If the object has been removed by a suicide
// flag the object as deleted.
if stateObject.remove {
stateObject.deleted = true
}
stateObject.dirty = false
}
}
}
// Commit commits all state changes to the database.
func (s *StateDB) Commit() (root common.Hash, err error) {
root, batch := s.CommitBatch()
return root, batch.Write()
}
// CommitBatch commits all state changes to a write batch but does not
// execute the batch. It is used to validate state changes against
// the root hash stored in a block.
func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
batch = s.db.NewBatch()
root, _ = s.commit(batch)
return root, batch
}
func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
s.refund = new(big.Int)
for _, stateObject := range s.stateObjects {
if stateObject.remove {
// If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie.
s.DeleteStateObject(stateObject)
} else {
// Write any contract code associated with the state object
if len(stateObject.code) > 0 {
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {
return common.Hash{}, err
}
}
// Write any storage changes in the state object to its trie.
stateObject.Update()
// Commit the trie of the object to the batch.
// This updates the trie root internally, so
// getting the root hash of the storage trie
// through UpdateStateObject is fast.
if _, err := stateObject.trie.CommitTo(db); err != nil {
return common.Hash{}, err
}
// Update the object in the account trie.
s.UpdateStateObject(stateObject)
}
stateObject.dirty = false
}
return s.trie.CommitTo(db)
}
func (self *StateDB) Refunds() *big.Int {
return self.refund
}
// Debug stuff
func (self *StateDB) CreateOutputForDiff() {
for _, stateObject := range self.stateObjects {
stateObject.CreateOutputForDiff()
}
}

View file

@ -1,120 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package state
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
)
// Tests that updating a state trie does not leak any database writes prior to
// actually committing the state.
func TestUpdateLeaks(t *testing.T) {
// Create an empty state database
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
// Update it with some accounts
for i := byte(0); i < 255; i++ {
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(big.NewInt(int64(11 * i)))
obj.SetNonce(uint64(42 * i))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i})
}
state.UpdateStateObject(obj)
}
// Ensure that no data was leaked into the database
for _, key := range db.Keys() {
value, _ := db.Get(key)
t.Errorf("State leaked into database: %x -> %x", key, value)
}
}
// Tests that no intermediate state of an object is stored into the database,
// only the one right before the commit.
func TestIntermediateLeaks(t *testing.T) {
// Create two state databases, one transitioning to the final state, the other final from the beginning
transDb, _ := ethdb.NewMemDatabase()
finalDb, _ := ethdb.NewMemDatabase()
transState, _ := New(common.Hash{}, transDb)
finalState, _ := New(common.Hash{}, finalDb)
// Update the states with some objects
for i := byte(0); i < 255; i++ {
// Create a new state object with some data into the transition database
obj := transState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.SetBalance(big.NewInt(int64(11 * i)))
obj.SetNonce(uint64(42 * i))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.BytesToHash([]byte{i, i, i, i, 0}))
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i, 0})
}
transState.UpdateStateObject(obj)
// Overwrite all the data with new values in the transition database
obj.SetBalance(big.NewInt(int64(11*i + 1)))
obj.SetNonce(uint64(42*i + 1))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.Hash{})
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i, 1})
}
transState.UpdateStateObject(obj)
// Create the final state object directly in the final database
obj = finalState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.SetBalance(big.NewInt(int64(11*i + 1)))
obj.SetNonce(uint64(42*i + 1))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i, 1})
}
finalState.UpdateStateObject(obj)
}
if _, err := transState.Commit(); err != nil {
t.Fatalf("failed to commit transition state: %v", err)
}
if _, err := finalState.Commit(); err != nil {
t.Fatalf("failed to commit final state: %v", err)
}
// Cross check the databases to ensure they are the same
for _, key := range finalDb.Keys() {
if _, err := transDb.Get(key); err != nil {
val, _ := finalDb.Get(key)
t.Errorf("entry missing from the transition database: %x -> %x", key, val)
}
}
for _, key := range transDb.Keys() {
if _, err := finalDb.Get(key); err != nil {
val, _ := transDb.Get(key)
t.Errorf("extra entry in the transition database: %x -> %x", key, val)
}
}
}

View file

@ -60,7 +60,7 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
state.UpdateStateObject(obj) state.UpdateStateObject(obj)
accounts = append(accounts, acc) accounts = append(accounts, acc)
} }
root, _ := state.Commit() root, _ := Commit(state)
// Remove any potentially cached data from the test state creation // Remove any potentially cached data from the test state creation
trie.ClearGlobalCache() trie.ClearGlobalCache()

View file

@ -56,7 +56,7 @@ func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
// Process returns the receipts and logs accumulated during the process and // 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 // 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. // 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(block *types.Block, st *state.State, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
var ( var (
receipts types.Receipts receipts types.Receipts
totalUsedGas = big.NewInt(0) totalUsedGas = big.NewInt(0)
@ -64,22 +64,31 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
header = block.Header() header = block.Header()
allLogs vm.Logs allLogs vm.Logs
gp = new(GasPool).AddGas(block.GasLimit()) gp = new(GasPool).AddGas(block.GasLimit())
forkState = state.Fork(st)
) )
// Mutate the the block and state according to any hard-fork specs // Mutate the the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
ApplyDAOHardFork(statedb) ApplyDAOHardFork(forkState)
} }
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i) forkState.PrepareIntermediate(tx.Hash(), block.Hash(), i)
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
txPostState, receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, forkState, header, tx, totalUsedGas, cfg)
if err != nil { if err != nil {
return nil, nil, totalUsedGas, err return nil, nil, totalUsedGas, err
} }
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
allLogs = append(allLogs, logs...) allLogs = append(allLogs, logs...)
// fork the transaction post state for the next cycle (if any)
forkState = state.Fork(txPostState)
} }
AccumulateRewards(statedb, header, block.Uncles()) AccumulateRewards(forkState, header, block.Uncles())
st.Set(state.Flatten(forkState))
return receipts, allLogs, totalUsedGas, err return receipts, allLogs, totalUsedGas, err
} }
@ -89,36 +98,36 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// //
// ApplyTransactions returns the generated receipts and vm logs during the // ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase. // 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) { func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, st *state.State, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*state.State, *types.Receipt, vm.Logs, *big.Int, error) {
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) env := NewEnv(st, config, bc, tx, header, cfg)
_, gas, err := ApplyMessage(env, tx, gp)
if err != nil { if err != nil {
return nil, nil, nil, err return env.state, nil, nil, nil, err
} }
// Update the state with pending changes // Update the state with pending changes
usedGas.Add(usedGas, gas) usedGas.Add(usedGas, gas)
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) receipt := types.NewReceipt(state.IntermediateRoot(env.state).Bytes(), usedGas)
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas) receipt.GasUsed = new(big.Int).Set(gas)
if MessageCreatesContract(tx) { if MessageCreatesContract(tx) {
from, _ := tx.From() from, _ := tx.From()
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
} }
receipt.Logs = env.state.Logs()
logs := statedb.GetLogs(tx.Hash())
receipt.Logs = logs
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
glog.V(logger.Debug).Infoln(receipt) glog.V(logger.Debug).Infoln(receipt)
return receipt, logs, gas, err return env.state, receipt, receipt.Logs, gas, err
} }
// AccumulateRewards credits the coinbase of the given block with the // AccumulateRewards credits the coinbase of the given block with the
// mining reward. The total reward consists of the static block reward // mining reward. The total reward consists of the static block reward
// and rewards for included uncles. The coinbase of each uncle block is // and rewards for included uncles. The coinbase of each uncle block is
// also rewarded. // also rewarded.
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { func AccumulateRewards(st *state.State, header *types.Header, uncles []*types.Header) {
reward := new(big.Int).Set(BlockReward) reward := new(big.Int).Set(BlockReward)
r := new(big.Int) r := new(big.Int)
for _, uncle := range uncles { for _, uncle := range uncles {
@ -126,10 +135,10 @@ func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*t
r.Sub(r, header.Number) r.Sub(r, header.Number)
r.Mul(r, BlockReward) r.Mul(r, BlockReward)
r.Div(r, big8) r.Div(r, big8)
statedb.AddBalance(uncle.Coinbase, r) st.AddBalance(uncle.Coinbase, r)
r.Div(BlockReward, big32) r.Div(BlockReward, big32)
reward.Add(reward, r) reward.Add(reward, r)
} }
statedb.AddBalance(header.Coinbase, reward) st.AddBalance(header.Coinbase, reward)
} }

View file

@ -55,7 +55,6 @@ type StateTransition struct {
initialGas *big.Int initialGas *big.Int
value *big.Int value *big.Int
data []byte data []byte
state vm.Database
env vm.Environment env vm.Environment
} }
@ -116,7 +115,6 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
initialGas: new(big.Int), initialGas: new(big.Int),
value: msg.Value(), value: msg.Value(),
data: msg.Data(), data: msg.Data(),
state: env.Db(),
} }
} }
@ -138,6 +136,7 @@ func (self *StateTransition) from() (vm.Account, error) {
var ( var (
f common.Address f common.Address
err error err error
st = self.env.Db()
) )
if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) { if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
f, err = self.msg.From() f, err = self.msg.From()
@ -147,10 +146,10 @@ func (self *StateTransition) from() (vm.Account, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !self.state.Exist(f) { if !st.Exist(f) {
return self.state.CreateAccount(f), nil return st.CreateAccount(f), nil
} }
return self.state.GetAccount(f), nil return st.GetAccount(f), nil
} }
func (self *StateTransition) to() vm.Account { func (self *StateTransition) to() vm.Account {
@ -162,10 +161,11 @@ func (self *StateTransition) to() vm.Account {
return nil // contract creation return nil // contract creation
} }
if !self.state.Exist(*to) { state := self.env.Db()
return self.state.CreateAccount(*to) if !state.Exist(*to) {
return state.CreateAccount(*to)
} }
return self.state.GetAccount(*to) return state.GetAccount(*to)
} }
func (self *StateTransition) useGas(amount *big.Int) error { func (self *StateTransition) useGas(amount *big.Int) error {
@ -210,7 +210,7 @@ func (self *StateTransition) preCheck() (err error) {
// Make sure this transaction's nonce is correct // Make sure this transaction's nonce is correct
if msg.CheckNonce() { if msg.CheckNonce() {
if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() { if n := self.env.Db().GetNonce(sender.Address()); n != msg.Nonce() {
return NonceError(msg.Nonce(), n) return NonceError(msg.Nonce(), n)
} }
} }
@ -255,7 +255,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
} }
} else { } else {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) self.env.Db().SetNonce(sender.Address(), self.env.Db().GetNonce(sender.Address())+1)
ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value) ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value)
if err != nil { if err != nil {
glog.V(logger.Core).Infoln("VM call err:", err) glog.V(logger.Core).Infoln("VM call err:", err)
@ -274,23 +274,24 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
requiredGas = new(big.Int).Set(self.gasUsed()) requiredGas = new(big.Int).Set(self.gasUsed())
self.refundGas() self.refundGas()
self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) self.env.Db().AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
return ret, requiredGas, self.gasUsed(), err return ret, requiredGas, self.gasUsed(), err
} }
func (self *StateTransition) refundGas() { func (self *StateTransition) refundGas() {
state := self.env.Db()
// Return eth for remaining gas to the sender account, // Return eth for remaining gas to the sender account,
// exchanged at the original rate. // exchanged at the original rate.
sender, _ := self.from() // err already checked sender, _ := self.from() // err already checked
remaining := new(big.Int).Mul(self.gas, self.gasPrice) remaining := new(big.Int).Mul(self.gas, self.gasPrice)
sender.AddBalance(remaining) state.AddBalance(sender.Address(), remaining)
// Apply refund counter, capped to half of the used gas. // Apply refund counter, capped to half of the used gas.
uhalf := remaining.Div(self.gasUsed(), common.Big2) uhalf := remaining.Div(self.gasUsed(), common.Big2)
refund := common.BigMin(uhalf, self.state.GetRefund()) refund := common.BigMin(uhalf, state.GetRefund())
self.gas.Add(self.gas, refund) self.gas.Add(self.gas, refund)
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
// Also return remaining gas to the block gas counter so it is // Also return remaining gas to the block gas counter so it is
// available for the next transaction. // available for the next transaction.

View file

@ -52,7 +52,7 @@ var (
evictionInterval = time.Minute // Time interval to check for evictable transactions evictionInterval = time.Minute // Time interval to check for evictable transactions
) )
type stateFn func() (*state.StateDB, error) type stateFn func() (*state.State, error)
// TxPool contains all currently known transactions. Transactions // TxPool contains all currently known transactions. Transactions
// enter the pool when they are received from the network or submitted // enter the pool when they are received from the network or submitted

View file

@ -41,7 +41,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
newPool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) newPool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.State, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
newPool.resetState() newPool.resetState()
return newPool, key return newPool, key
@ -176,7 +176,7 @@ func TestTransactionChainFork(t *testing.T) {
resetState := func() { resetState := func() {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool.currentState = func() (*state.StateDB, error) { return statedb, nil } pool.currentState = func() (*state.State, error) { return statedb, nil }
currentState, _ := pool.currentState() currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000)) currentState.AddBalance(addr, big.NewInt(100000000000000))
pool.resetState() pool.resetState()
@ -202,7 +202,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
resetState := func() { resetState := func() {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool.currentState = func() (*state.StateDB, error) { return statedb, nil } pool.currentState = func() (*state.State, error) { return statedb, nil }
currentState, _ := pool.currentState() currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000)) currentState.AddBalance(addr, big.NewInt(100000000000000))
pool.resetState() pool.resetState()
@ -482,7 +482,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.State, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState() pool.resetState()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them

View file

@ -40,7 +40,7 @@ import (
type Validator interface { type Validator interface {
HeaderValidator HeaderValidator
ValidateBlock(block *types.Block) error ValidateBlock(block *types.Block) error
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error ValidateState(block, parent *types.Block, st *state.State, receipts types.Receipts, usedGas *big.Int) error
} }
// HeaderValidator is an interface for validating headers only // HeaderValidator is an interface for validating headers only
@ -58,5 +58,5 @@ type HeaderValidator interface {
// of gas used in the process and return an error if any of the internal rules // of gas used in the process and return an error if any of the internal rules
// failed. // failed.
type Processor interface { type Processor interface {
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) Process(block *types.Block, st *state.State, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
} }

View file

@ -22,6 +22,14 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
// Vm is the basic interface for an implementation of the EVM.
type Vm interface {
// Run should execute the given contract with the input given in in
// and return the contract execution return bytes or an error if it
// failed.
Run(c *Contract, in []byte) ([]byte, error)
}
// RuleSet is an interface that defines the current rule set during the // RuleSet is an interface that defines the current rule set during the
// execution of the EVM instructions (e.g. whether it's homestead) // execution of the EVM instructions (e.g. whether it's homestead)
type RuleSet interface { type RuleSet interface {
@ -35,11 +43,17 @@ type Environment interface {
RuleSet() RuleSet RuleSet() RuleSet
// The state database // The state database
Db() Database Db() Database
// PreserveState preserve the current state and returns a new handle
ForkState() Database
// SetState sets the new state handle
SetState(Database)
// Creates a restorable snapshot // Creates a restorable snapshot
MakeSnapshot() Database //MakeSnapshot() Database
// Set database to previous snapshot // Set database to previous snapshot
SetSnapshot(Database) //SetSnapshot(Database)
// Address of the original invoker (first occurrence of the VM invoker) // Address of the original invoker (first occurrence of the VM invoker)
Origin() common.Address Origin() common.Address
// The block number this VM is invoked on // The block number this VM is invoked on
BlockNumber() *big.Int BlockNumber() *big.Int
@ -59,8 +73,6 @@ type Environment interface {
CanTransfer(from common.Address, balance *big.Int) bool CanTransfer(from common.Address, balance *big.Int) bool
// Transfers amount from one account to the other // Transfers amount from one account to the other
Transfer(from, to Account, amount *big.Int) Transfer(from, to Account, amount *big.Int)
// Adds a LOG to the state
AddLog(*Log)
// Type of the VM // Type of the VM
Vm() Vm Vm() Vm
// Get the curret calling depth // Get the curret calling depth
@ -77,14 +89,6 @@ type Environment interface {
Create(me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) Create(me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
} }
// Vm is the basic interface for an implementation of the EVM.
type Vm interface {
// Run should execute the given contract with the input given in in
// and return the contract execution return bytes or an error if it
// failed.
Run(c *Contract, in []byte) ([]byte, error)
}
// Database is a EVM database for full state querying. // Database is a EVM database for full state querying.
type Database interface { type Database interface {
GetAccount(common.Address) Account GetAccount(common.Address) Account
@ -108,6 +112,8 @@ type Database interface {
Delete(common.Address) bool Delete(common.Address) bool
Exist(common.Address) bool Exist(common.Address) bool
IsDeleted(common.Address) bool IsDeleted(common.Address) bool
AddLog(*Log)
} }
// Account represents a contract or basic ethereum account. // Account represents a contract or basic ethereum account.

View file

@ -64,6 +64,8 @@ func (instr instruction) do(program *Program, pc *uint64, env Environment, contr
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Uncomment for debugging
//fmt.Println("OP", instr.op, "GAS", cost, "SIZE", newMemSize)
// Use the calculated gas. When insufficient gas is present, use all gas and return an // Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error // Out Of Gas error
@ -456,7 +458,8 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m
d := memory.Get(mStart.Int64(), mSize.Int64()) d := memory.Get(mStart.Int64(), mSize.Int64())
log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64()) log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
env.AddLog(log) //env.AddLog(log)
env.Db().AddLog(log)
} }
func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
@ -630,7 +633,7 @@ func makeLog(size int) instrFn {
d := memory.Get(mStart.Int64(), mSize.Int64()) d := memory.Get(mStart.Int64(), mSize.Int64())
log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64()) log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
env.AddLog(log) env.Db().AddLog(log)
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright 2015 The go-ethereum Authors //Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of the go-ethereum library.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
@ -75,7 +75,7 @@ func TestCompiling(t *testing.T) {
func TestResetInput(t *testing.T) { func TestResetInput(t *testing.T) {
var sender account var sender account
env := NewEnv(true, true) env := NewEnv(&Config{ForceJit: true, EnableJit: true})
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000)) contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
contract.CodeAddr = &common.Address{} contract.CodeAddr = &common.Address{}
@ -170,8 +170,8 @@ func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
//func (self *Env) PrevHash() []byte { return self.parent } //func (self *Env) PrevHash() []byte { return self.parent }
func (self *Env) Coinbase() common.Address { return common.Address{} } func (self *Env) Coinbase() common.Address { return common.Address{} }
func (self *Env) MakeSnapshot() Database { return nil } func (self *Env) ForkState() Database { return nil }
func (self *Env) SetSnapshot(Database) {} func (self *Env) SetState(Database) {}
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) } func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) } func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
func (self *Env) Db() Database { return nil } func (self *Env) Db() Database { return nil }

View file

@ -29,7 +29,7 @@ import (
type Env struct { type Env struct {
ruleSet vm.RuleSet ruleSet vm.RuleSet
depth int depth int
state *state.StateDB state *state.State
origin common.Address origin common.Address
coinbase common.Address coinbase common.Address
@ -46,7 +46,7 @@ type Env struct {
} }
// NewEnv returns a new vm.Environment // NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { func NewEnv(cfg *Config, state *state.State) vm.Environment {
env := &Env{ env := &Env{
ruleSet: cfg.RuleSet, ruleSet: cfg.RuleSet,
state: state, state: state,
@ -89,11 +89,12 @@ func (self *Env) SetDepth(i int) { self.depth = i }
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool { func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
} }
func (self *Env) MakeSnapshot() vm.Database { func (self *Env) ForkState() vm.Database {
return self.state.Copy() self.state = state.Fork(self.state)
return self.state
} }
func (self *Env) SetSnapshot(copy vm.Database) { func (self *Env) SetState(st vm.Database) {
self.state.Set(copy.(*state.StateDB)) self.state.Set(st.(*state.State))
} }
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {

View file

@ -47,7 +47,7 @@ type Config struct {
DisableJit bool // "disable" so it's enabled by default DisableJit bool // "disable" so it's enabled by default
Debug bool Debug bool
State *state.StateDB State *state.State
GetHashFn func(n uint64) common.Hash GetHashFn func(n uint64) common.Hash
} }
@ -88,7 +88,7 @@ func setDefaults(cfg *Config) {
// Executes sets up a in memory, temporarily, environment for the execution of // Executes sets up a in memory, temporarily, environment for the execution of
// the given code. It enabled the JIT by default and make sure that it's restored // the given code. It enabled the JIT by default and make sure that it's restored
// to it's original state afterwards. // to it's original state afterwards.
func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { func Execute(code, input []byte, cfg *Config) ([]byte, *state.State, error) {
if cfg == nil { if cfg == nil {
cfg = new(Config) cfg = new(Config)
} }

View file

@ -41,18 +41,20 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
} }
type VMEnv struct { type VMEnv struct {
chainConfig *ChainConfig // Chain configuration chainConfig *ChainConfig // Chain configuration
state *state.StateDB // State to use for executing
evm vm.VirtualMachine // The Ethereum Virtual Machine state *state.State
depth int // Current execution depth
msg Message // Message appliod evm vm.VirtualMachine // The Ethereum Virtual Machine
depth int // Current execution depth
msg Message // Message appliod
header *types.Header // Header information header *types.Header // Header information
chain *BlockChain // Blockchain handle chain *BlockChain // Blockchain handle
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes getHashFn func(uint64) common.Hash // 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 { func NewEnv(state *state.State, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
env := &VMEnv{ env := &VMEnv{
chainConfig: chainConfig, chainConfig: chainConfig,
chain: chain, chain: chain,
@ -90,12 +92,13 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
} }
func (self *VMEnv) MakeSnapshot() vm.Database { func (self *VMEnv) ForkState() vm.Database {
return self.state.Copy() self.state = state.Fork(self.state)
return self.state
} }
func (self *VMEnv) SetSnapshot(copy vm.Database) { func (self *VMEnv) SetState(st vm.Database) {
self.state.Set(copy.(*state.StateDB)) self.state = st.(*state.State)
} }
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {

View file

@ -97,8 +97,8 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash) return b.eth.blockchain.GetTdByHash(blockHash)
} }
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, st ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
stateDb := state.(EthApiState).state.Copy() stateDb := state.Fork(st.(EthApiState).state)
addr, _ := msg.From() addr, _ := msg.From()
from := stateDb.GetOrNewStateObject(addr) from := stateDb.GetOrNewStateObject(addr)
from.SetBalance(common.MaxBig) from.SetBalance(common.MaxBig)
@ -185,7 +185,7 @@ func (b *EthApiBackend) AccountManager() *accounts.Manager {
} }
type EthApiState struct { type EthApiState struct {
state *state.StateDB state *state.State
} }
func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) {

View file

@ -50,14 +50,15 @@ func (self *Env) Origin() common.Address { return common.Address{} }
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) } func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
//func (self *Env) PrevHash() []byte { return self.parent } //func (self *Env) PrevHash() []byte { return self.parent }
func (self *Env) Coinbase() common.Address { return common.Address{} } func (self *Env) Coinbase() common.Address { return common.Address{} }
func (self *Env) MakeSnapshot() vm.Database { return nil } func (self *Env) ForkState() vm.Database { return nil }
func (self *Env) SetSnapshot(vm.Database) {} func (self *Env) SetState(vm.Database) {}
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) } func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) } func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
func (self *Env) Db() vm.Database { return nil } func (self *Env) Db() vm.Database { return nil }
func (self *Env) GasLimit() *big.Int { return self.gasLimit } func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() vm.Type { return vm.StdVmTy } func (self *Env) GasPrice() *big.Int { return big.NewInt(0) }
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
func (self *Env) GetHash(n uint64) common.Hash { func (self *Env) GetHash(n uint64) common.Hash {
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
} }
@ -69,16 +70,16 @@ func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return true return true
} }
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {} func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {}
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil return nil, nil
} }
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil return nil, nil
} }
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
return nil, common.Address{}, nil return nil, common.Address{}, nil
} }
func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
return nil, nil return nil, nil
} }
@ -92,14 +93,14 @@ func (account) SetBalance(*big.Int) {}
func (account) SetNonce(uint64) {} func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil } func (account) Balance() *big.Int { return nil }
func (account) Address() common.Address { return common.Address{} } func (account) Address() common.Address { return common.Address{} }
func (account) ReturnGas(*big.Int, *big.Int) {} func (account) ReturnGas(uint64) {}
func (account) SetCode([]byte) {} func (account) SetCode([]byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
func runTrace(tracer *JavascriptTracer) (interface{}, error) { func runTrace(tracer *JavascriptTracer) (interface{}, error) {
env := NewEnv(&vm.Config{Debug: true, Tracer: tracer}) env := NewEnv(&vm.Config{Debug: true, Tracer: tracer})
contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit(), big.NewInt(1)) contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit())
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
_, err := env.Vm().Run(contract, []byte{}) _, err := env.Vm().Run(contract, []byte{})
@ -188,7 +189,7 @@ func TestHaltBetweenSteps(t *testing.T) {
} }
env := NewEnv(&vm.Config{Debug: true, Tracer: tracer}) env := NewEnv(&vm.Config{Debug: true, Tracer: tracer})
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0), big.NewInt(0)) contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0))
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil) tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
timeout := errors.New("stahp") timeout := errors.New("stahp")

View file

@ -65,7 +65,7 @@ func makeTestState() (common.Hash, ethdb.Database) {
so.Update() so.Update()
st.UpdateStateObject(so) st.UpdateStateObject(so)
} }
root, _ := st.Commit() root, _ := state.Commit(st)
return root, sdb return root, sdb
} }

View file

@ -182,7 +182,7 @@ func (self *Miner) SetExtra(extra []byte) error {
} }
// Pending returns the currently pending block and associated state. // Pending returns the currently pending block and associated state.
func (self *Miner) Pending() (*types.Block, *state.StateDB) { func (self *Miner) Pending() (*types.Block, *state.State) {
return self.worker.pending() return self.worker.pending()
} }

View file

@ -64,11 +64,11 @@ type uint64RingBuffer struct {
// all of the current state information // all of the current state information
type Work struct { type Work struct {
config *core.ChainConfig config *core.ChainConfig
state *state.StateDB // apply state changes here state *state.State // apply state changes here
ancestors *set.Set // ancestor set (used for checking uncle parent validity) ancestors *set.Set // ancestor set (used for checking uncle parent validity)
family *set.Set // family set (used for checking uncle invalidity) family *set.Set // family set (used for checking uncle invalidity)
uncles *set.Set // uncle set uncles *set.Set // uncle set
tcount int // tx count in cycle tcount int // tx count in cycle
ownedAccounts *set.Set ownedAccounts *set.Set
lowGasTxs types.Transactions lowGasTxs types.Transactions
failedTxs types.Transactions failedTxs types.Transactions
@ -159,7 +159,7 @@ func (self *worker) setEtherbase(addr common.Address) {
self.coinbase = addr self.coinbase = addr
} }
func (self *worker) pending() (*types.Block, *state.StateDB) { func (self *worker) pending() (*types.Block, *state.State) {
self.currentMu.Lock() self.currentMu.Lock()
defer self.currentMu.Unlock() defer self.currentMu.Unlock()
@ -276,7 +276,8 @@ func (self *worker) wait() {
} }
go self.mux.Post(core.NewMinedBlockEvent{Block: block}) go self.mux.Post(core.NewMinedBlockEvent{Block: block})
} else { } else {
work.state.Commit() //work.state.Commit()
state.Commit(work.state)
parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil { if parent == nil {
glog.V(logger.Error).Infoln("Invalid block found during mining") glog.V(logger.Error).Infoln("Invalid block found during mining")
@ -528,7 +529,7 @@ func (self *worker) commitNewWork() {
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
// commit state root after all state transitions. // commit state root after all state transitions.
core.AccumulateRewards(work.state, header, uncles) core.AccumulateRewards(work.state, header, uncles)
header.Root = work.state.IntermediateRoot() header.Root = state.IntermediateRoot(work.state)
} }
// create the new block whose nonce will be mined. // create the new block whose nonce will be mined.
@ -582,8 +583,8 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
continue continue
} }
// Start executing the transaction
env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) env.state.PrepareIntermediate(tx.Hash(), common.Hash{}, env.tcount)
err, logs := env.commitTransaction(tx, bc, gp) err, logs := env.commitTransaction(tx, bc, gp)
switch { switch {
@ -618,8 +619,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
} }
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) {
snap := env.state.Copy()
// this is a bit of a hack to force jit for the miners // this is a bit of a hack to force jit for the miners
config := env.config.VmConfig config := env.config.VmConfig
if !(config.EnableJit && config.ForceJit) { if !(config.EnableJit && config.ForceJit) {
@ -627,9 +626,10 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, g
} }
config.ForceJit = false // disable forcing jit config.ForceJit = false // disable forcing jit
receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) state, receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config)
defer func() { env.state = state }()
if err != nil { if err != nil {
env.state.Set(snap)
return err, nil return err, nil
} }
env.txs = append(env.txs, tx) env.txs = append(env.txs, tx)

View file

@ -200,7 +200,7 @@ func runBlockTest(homesteadBlock, daoForkBlock *big.Int, test *BlockTest) error
// InsertPreState populates the given database with the genesis // InsertPreState populates the given database with the genesis
// accounts defined by the test. // accounts defined by the test.
func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) { func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.State, error) {
statedb, err := state.New(common.Hash{}, db) statedb, err := state.New(common.Hash{}, db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -227,7 +227,7 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
} }
} }
root, err := statedb.Commit() root, err := state.Commit(statedb)
if err != nil { if err != nil {
return nil, fmt.Errorf("error writing state: %v", err) return nil, fmt.Errorf("error writing state: %v", err)
} }
@ -362,7 +362,7 @@ func validateHeader(h *btHeader, h2 *types.Header) error {
return nil return nil
} }
func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error { func (t *BlockTest) ValidatePostState(statedb *state.State) error {
// validate post state accounts in test file against what we have in state db // validate post state accounts in test file against what we have in state db
for addrString, acct := range t.postAccounts { for addrString, acct := range t.postAccounts {
// XXX: is is worth it checking for errors here? // XXX: is is worth it checking for errors here?

View file

@ -99,7 +99,7 @@ func benchStateTest(ruleSet RuleSet, test VmTest, env map[string]string, b *test
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.StateObjects[obj.Address()] = obj
for a, v := range account.Storage { for a, v := range account.Storage {
obj.SetState(common.HexToHash(a), common.HexToHash(v)) obj.SetState(common.HexToHash(a), common.HexToHash(v))
} }
@ -121,13 +121,11 @@ func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string)
continue continue
} }
//fmt.Println("StateTest:", name)
if err := runStateTest(ruleSet, test); err != nil { if err := runStateTest(ruleSet, test); err != nil {
return fmt.Errorf("%s: %s\n", name, err.Error()) return fmt.Errorf("%s: %s\n", name, err.Error())
} }
//glog.Infoln("State test passed: ", name) //glog.Infoln("State test passed: ", name)
//fmt.Println(string(statedb.Dump()))
} }
return nil return nil
@ -138,7 +136,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.StateObjects[obj.Address()] = obj
for a, v := range account.Storage { for a, v := range account.Storage {
obj.SetState(common.HexToHash(a), common.HexToHash(v)) obj.SetState(common.HexToHash(a), common.HexToHash(v))
} }
@ -169,7 +167,6 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
} }
ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction) ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction)
// Compare expected and actual return // Compare expected and actual return
rexp := common.FromHex(test.Out) rexp := common.FromHex(test.Out)
if bytes.Compare(rexp, ret) != 0 { if bytes.Compare(rexp, ret) != 0 {
@ -201,7 +198,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
} }
} }
root, _ := statedb.Commit() root, _ := state.Commit(statedb)
if common.HexToHash(test.PostStateRoot) != root { if common.HexToHash(test.PostStateRoot) != root {
return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root) return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root)
} }
@ -216,7 +213,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
return nil return nil
} }
func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) { func RunState(ruleSet RuleSet, statedb *state.State, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) {
var ( var (
data = common.FromHex(tx["data"]) data = common.FromHex(tx["data"])
gas = common.Big(tx["gasLimit"]) gas = common.Big(tx["gasLimit"])
@ -232,19 +229,21 @@ func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string
} }
// Set pre compiled contracts // Set pre compiled contracts
//vm.Precompiled = vm.PrecompiledContracts() //vm.Precompiled = vm.PrecompiledContracts()
snapshot := statedb.Copy() snapshot := statedb
nstatedb := state.Fork(snapshot)
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"])) gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
key, _ := hex.DecodeString(tx["secretKey"]) key, _ := hex.DecodeString(tx["secretKey"])
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
message := NewMessage(addr, to, data, value, gas, price, nonce) message := NewMessage(addr, to, data, value, gas, price, nonce)
vmenv := NewEnvFromMap(ruleSet, statedb, env, tx) vmenv := NewEnvFromMap(ruleSet, nstatedb, env, tx)
vmenv.origin = addr vmenv.origin = addr
ret, _, err := core.ApplyMessage(vmenv, message, gaspool) ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
statedb.Set(snapshot) vmenv.SetState(snapshot)
} }
statedb.Commit() statedb.Set(state.Flatten(vmenv.state))
state.Commit(statedb)
return ret, vmenv.state.Logs(), vmenv.Gas, err return ret, statedb.Logs(), vmenv.Gas, err
} }

View file

@ -152,7 +152,7 @@ func (r RuleSet) IsHomestead(n *big.Int) bool {
type Env struct { type Env struct {
ruleSet RuleSet ruleSet RuleSet
depth int depth int
state *state.StateDB state *state.State
skipTransfer bool skipTransfer bool
initial bool initial bool
Gas, gasPrice *big.Int Gas, gasPrice *big.Int
@ -171,7 +171,7 @@ type Env struct {
evm vm.VirtualMachine evm vm.VirtualMachine
} }
func NewEnv(ruleSet RuleSet, state *state.StateDB) *Env { func NewEnv(ruleSet RuleSet, state *state.State) *Env {
env := &Env{ env := &Env{
ruleSet: ruleSet, ruleSet: ruleSet,
state: state, state: state,
@ -179,7 +179,7 @@ func NewEnv(ruleSet RuleSet, state *state.StateDB) *Env {
return env return env
} }
func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { func NewEnvFromMap(ruleSet RuleSet, state *state.State, envValues map[string]string, exeValues map[string]string) *Env {
env := NewEnv(ruleSet, state) env := NewEnv(ruleSet, state)
env.origin = common.HexToAddress(exeValues["caller"]) env.origin = common.HexToAddress(exeValues["caller"])
@ -229,11 +229,13 @@ func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
} }
func (self *Env) MakeSnapshot() vm.Database { func (self *Env) ForkState() vm.Database {
return self.state.Copy() self.state = state.Fork(self.state)
return self.state
} }
func (self *Env) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB)) func (self *Env) SetState(st vm.Database) {
self.state = st.(*state.State)
} }
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {

View file

@ -104,7 +104,7 @@ func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.StateObjects[obj.Address()] = obj
for a, v := range account.Storage { for a, v := range account.Storage {
obj.SetState(common.HexToHash(a), common.HexToHash(v)) obj.SetState(common.HexToHash(a), common.HexToHash(v))
} }
@ -155,7 +155,7 @@ func runVmTest(test VmTest) error {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.StateObjects[obj.Address()] = obj
for a, v := range account.Storage { for a, v := range account.Storage {
obj.SetState(common.HexToHash(a), common.HexToHash(v)) obj.SetState(common.HexToHash(a), common.HexToHash(v))
} }
@ -227,7 +227,7 @@ func runVmTest(test VmTest) error {
return nil return nil
} }
func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) { func RunVm(statedb *state.State, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
var ( var (
to = common.HexToAddress(exec["address"]) to = common.HexToAddress(exec["address"])
from = common.HexToAddress(exec["caller"]) from = common.HexToAddress(exec["caller"])
@ -238,13 +238,16 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
// Reset the pre-compiled contracts for VM tests. // Reset the pre-compiled contracts for VM tests.
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract) vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
caller := state.GetOrNewStateObject(from) caller := statedb.GetOrNewStateObject(from)
vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true}, state, env, exec) vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true}, statedb, env, exec)
vmenv.vmTest = true vmenv.vmTest = true
vmenv.skipTransfer = true vmenv.skipTransfer = true
vmenv.initial = true vmenv.initial = true
ret, err := vmenv.Call(caller, to, data, gas, value) ret, err := vmenv.Call(caller, to, data, gas, value)
return ret, vmenv.state.Logs(), vmenv.Gas, err statedb.Set(state.Flatten(vmenv.state))
state.Commit(statedb)
return ret, statedb.Logs(), vmenv.Gas, err
} }