mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
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:
parent
7c8d8eb5f9
commit
931c6a4936
40 changed files with 899 additions and 877 deletions
|
|
@ -50,7 +50,7 @@ type SimulatedBackend struct {
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
@ -225,8 +231,6 @@ 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) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
|
|
||||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
||||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
||||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||||
|
|
@ -245,6 +249,13 @@ func (self *VMEnv) GetHash(n uint64) common.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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ type BlockGen struct {
|
||||||
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()),
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
394
core/state/state.go
Normal 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()
|
||||||
|
}
|
||||||
15
core/state/state_changes.go
Normal file
15
core/state/state_changes.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
65
core/state/state_write.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ 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
|
|
||||||
|
state *state.State
|
||||||
|
|
||||||
evm vm.VirtualMachine // The Ethereum Virtual Machine
|
evm vm.VirtualMachine // The Ethereum Virtual Machine
|
||||||
depth int // Current execution depth
|
depth int // Current execution depth
|
||||||
msg Message // Message appliod
|
msg Message // Message appliod
|
||||||
|
|
@ -52,7 +54,7 @@ type VMEnv struct {
|
||||||
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) {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,13 @@ 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) GasPrice() *big.Int { return big.NewInt(0) }
|
||||||
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
|
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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ 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
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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?
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue