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
|
|
@ -49,8 +49,8 @@ type SimulatedBackend struct {
|
|||
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
||||
|
||||
mu sync.Mutex
|
||||
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
|
||||
pendingBlock *types.Block // Currently pending block that will be imported 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
|
||||
|
|
@ -176,7 +176,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
|||
b.mu.Lock()
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -201,13 +201,13 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
b.mu.Lock()
|
||||
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
|
||||
}
|
||||
|
||||
// callContract implemens common code between normal and pending contract calls.
|
||||
// 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.
|
||||
if call.GasPrice == nil {
|
||||
call.GasPrice = big.NewInt(1)
|
||||
|
|
|
|||
|
|
@ -115,17 +115,14 @@ func run(ctx *cli.Context) error {
|
|||
glog.SetToStderr(true)
|
||||
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)
|
||||
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),
|
||||
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
|
||||
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
|
||||
Tracer: logger,
|
||||
})
|
||||
sender := vmenv.state.CreateAccount(common.StringToAddress("sender"))
|
||||
|
||||
tstart := time.Now()
|
||||
|
||||
|
|
@ -143,7 +140,7 @@ func run(ctx *cli.Context) error {
|
|||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
)
|
||||
} else {
|
||||
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
||||
receiver := vmenv.state.CreateAccount(common.StringToAddress("receiver"))
|
||||
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
|
||||
ret, err = vmenv.Call(
|
||||
sender,
|
||||
|
|
@ -156,8 +153,8 @@ func run(ctx *cli.Context) error {
|
|||
vmdone := time.Since(tstart)
|
||||
|
||||
if ctx.GlobalBool(DumpFlag.Name) {
|
||||
statedb.Commit()
|
||||
fmt.Println(string(statedb.Dump()))
|
||||
state.Commit(vmenv.state)
|
||||
fmt.Println(string(vmenv.state.Dump()))
|
||||
}
|
||||
vm.StdErrFormat(logger.StructLogs())
|
||||
|
||||
|
|
@ -190,7 +187,7 @@ func main() {
|
|||
}
|
||||
|
||||
type VMEnv struct {
|
||||
state *state.StateDB
|
||||
state *state.State
|
||||
block *types.Block
|
||||
|
||||
transactor *common.Address
|
||||
|
|
@ -204,9 +201,18 @@ type VMEnv struct {
|
|||
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{
|
||||
state: state,
|
||||
state: st,
|
||||
transactor: &transactor,
|
||||
value: value,
|
||||
price: price,
|
||||
|
|
@ -222,29 +228,34 @@ type ruleSet struct{}
|
|||
|
||||
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
||||
|
||||
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
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) BlockNumber() *big.Int { return common.Big0 }
|
||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) Time() *big.Int { return self.time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *VMEnv) Depth() int { return 0 }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) Time() *big.Int { return self.time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *VMEnv) Depth() int { return 0 }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) GetHash(n uint64) common.Hash {
|
||||
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
|
||||
return self.block.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) {
|
||||
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
|
||||
// itself. ValidateState returns a database batch if the validation was a success
|
||||
// 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()
|
||||
if block.GasUsed().Cmp(usedGas) != 0 {
|
||||
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
|
||||
// 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 nil
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ func (self *BlockChain) Processor() Processor {
|
|||
func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
@ -826,7 +826,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
tstart = time.Now()
|
||||
|
||||
nonceChecked = make([]bool, len(chain))
|
||||
statedb *state.StateDB
|
||||
st *state.State
|
||||
)
|
||||
|
||||
// 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
|
||||
// error if it fails.
|
||||
if statedb == nil {
|
||||
statedb, err = state.New(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), self.chainDb)
|
||||
if st == nil {
|
||||
st, err = state.New(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), self.chainDb)
|
||||
} else {
|
||||
err = statedb.Reset(chain[i-1].Root())
|
||||
err = st.Reset(chain[i-1].Root())
|
||||
}
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig)
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, st, self.config.VmConfig)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
}
|
||||
// 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 {
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
}
|
||||
// flatten the state before committing.
|
||||
st = state.Flatten(st)
|
||||
// Write state changes to database
|
||||
_, err = statedb.Commit()
|
||||
_, err = state.Commit(st)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
WriteBlock(blockchain.chainDb, block)
|
||||
statedb.Commit()
|
||||
state.Commit(statedb)
|
||||
blockchain.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
|
|
@ -432,10 +432,10 @@ type bproc struct{}
|
|||
|
||||
func (bproc) ValidateBlock(*types.Block) 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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,11 +63,11 @@ var (
|
|||
// BlockGen creates blocks for testing.
|
||||
// See GenerateChain for a detailed explanation.
|
||||
type BlockGen struct {
|
||||
i int
|
||||
parent *types.Block
|
||||
chain []*types.Block
|
||||
header *types.Header
|
||||
statedb *state.StateDB
|
||||
i int
|
||||
parent *types.Block
|
||||
chain []*types.Block
|
||||
header *types.Header
|
||||
state *state.State
|
||||
|
||||
gasPool *GasPool
|
||||
txs []*types.Transaction
|
||||
|
|
@ -105,11 +105,12 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
|
|||
if b.gasPool == nil {
|
||||
b.SetCoinbase(common.Address{})
|
||||
}
|
||||
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
|
||||
receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
b.state.PrepareIntermediate(tx.Hash(), common.Hash{}, len(b.txs))
|
||||
st, receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.state, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b.state = st
|
||||
b.txs = append(b.txs, tx)
|
||||
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
|
||||
// account at addr. It panics if the account does not exist.
|
||||
func (b *BlockGen) TxNonce(addr common.Address) uint64 {
|
||||
if !b.statedb.HasAccount(addr) {
|
||||
if !b.state.HasAccount(addr) {
|
||||
panic("account does not exist")
|
||||
}
|
||||
return b.statedb.GetNonce(addr)
|
||||
return b.state.GetNonce(addr)
|
||||
}
|
||||
|
||||
// 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.
|
||||
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)
|
||||
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
|
||||
genblock := func(i int, h *types.Header, st *state.State) (*types.Block, types.Receipts) {
|
||||
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
|
||||
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 {
|
||||
ApplyDAOHardFork(statedb)
|
||||
ApplyDAOHardFork(b.state)
|
||||
}
|
||||
// Execute any user modifications to the block and finalize it
|
||||
if gen != nil {
|
||||
gen(i, b)
|
||||
}
|
||||
AccumulateRewards(statedb, h, b.uncles)
|
||||
root, err := statedb.Commit()
|
||||
AccumulateRewards(b.state, h, b.uncles)
|
||||
root, err := state.Commit(b.state)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
statedb, err := state.New(parent.Root(), db)
|
||||
st, err := state.New(parent.Root(), db)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
header := makeHeader(parent, statedb)
|
||||
block, receipt := genblock(i, header, statedb)
|
||||
header := makeHeader(parent, st)
|
||||
block, receipt := genblock(i, header, st)
|
||||
blocks[i] = block
|
||||
receipts[i] = receipt
|
||||
parent = block
|
||||
|
|
@ -224,7 +229,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
|
|||
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
|
||||
if parent.Time() == nil {
|
||||
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
|
||||
}
|
||||
return &types.Header{
|
||||
Root: state.IntermediateRoot(),
|
||||
Root: state.IntermediateRoot(st),
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
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
|
||||
// rules, transferring all balances of a set of DAO accounts to a single refund
|
||||
// contract.
|
||||
func ApplyDAOHardFork(statedb *state.StateDB) {
|
||||
func ApplyDAOHardFork(state *state.State) {
|
||||
// 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
|
||||
for _, addr := range params.DAODrainList {
|
||||
if account := statedb.GetStateObject(addr); account != nil {
|
||||
if account := state.GetStateObject(addr); account != nil {
|
||||
refund.AddBalance(account.Balance())
|
||||
account.SetBalance(new(big.Int))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,9 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
|||
createAccount = true
|
||||
}
|
||||
|
||||
snapshotPreTransfer := env.MakeSnapshot()
|
||||
snapshotPreTransfer := env.Db()
|
||||
// preserve the envs state and move to a new state
|
||||
env.ForkState()
|
||||
var (
|
||||
from = env.Db().GetAccount(caller.Address())
|
||||
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) {
|
||||
contract.UseGas(contract.Gas())
|
||||
|
||||
env.SetSnapshot(snapshotPreTransfer)
|
||||
env.SetState(snapshotPreTransfer)
|
||||
}
|
||||
|
||||
return ret, addr, err
|
||||
|
|
@ -144,7 +146,8 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
|||
return nil, common.Address{}, vm.DepthError
|
||||
}
|
||||
|
||||
snapshot := env.MakeSnapshot()
|
||||
snapshot := env.Db()
|
||||
env.ForkState()
|
||||
|
||||
var to vm.Account
|
||||
if !env.Db().Exist(*toAddr) {
|
||||
|
|
@ -162,7 +165,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
|||
if err != nil {
|
||||
contract.UseGas(contract.Gas())
|
||||
|
||||
env.SetSnapshot(snapshot)
|
||||
env.SetState(snapshot)
|
||||
}
|
||||
|
||||
return ret, addr, err
|
||||
|
|
|
|||
|
|
@ -64,16 +64,16 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
|
|||
}
|
||||
|
||||
// creating with empty hash always works
|
||||
statedb, _ := state.New(common.Hash{}, chainDb)
|
||||
st, _ := state.New(common.Hash{}, chainDb)
|
||||
for addr, account := range genesis.Alloc {
|
||||
address := common.HexToAddress(addr)
|
||||
statedb.AddBalance(address, common.String2Big(account.Balance))
|
||||
statedb.SetCode(address, common.Hex2Bytes(account.Code))
|
||||
st.AddBalance(address, common.String2Big(account.Balance))
|
||||
st.SetCode(address, common.Hex2Bytes(account.Code))
|
||||
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)
|
||||
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.
|
||||
// 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 {
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
obj := statedb.GetOrNewStateObject(addr)
|
||||
st, _ := state.New(common.Hash{}, db)
|
||||
obj := st.GetOrNewStateObject(addr)
|
||||
obj.SetBalance(balance)
|
||||
root, err := statedb.Commit()
|
||||
root, err := state.Commit(st)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot write state: %v", err))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ type World struct {
|
|||
Accounts map[string]Account `json:"accounts"`
|
||||
}
|
||||
|
||||
func (self *StateDB) RawDump() World {
|
||||
func (self *State) RawDump() World {
|
||||
world := World{
|
||||
Root: common.Bytes2Hex(self.trie.Root()),
|
||||
Root: common.Bytes2Hex(self.Trie.Root()),
|
||||
Accounts: make(map[string]Account),
|
||||
}
|
||||
|
||||
it := self.trie.Iterator()
|
||||
it := self.Trie.Iterator()
|
||||
for it.Next() {
|
||||
addr := self.trie.GetKey(it.Key)
|
||||
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
|
||||
addr := self.Trie.GetKey(it.Key)
|
||||
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.Db, it.Value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -61,14 +61,14 @@ func (self *StateDB) RawDump() World {
|
|||
}
|
||||
storageIt := stateObject.trie.Iterator()
|
||||
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
|
||||
}
|
||||
return world
|
||||
}
|
||||
|
||||
func (self *StateDB) Dump() []byte {
|
||||
func (self *State) Dump() []byte {
|
||||
json, err := json.MarshalIndent(self.RawDump(), "", " ")
|
||||
if err != nil {
|
||||
fmt.Println("dump err", err)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
// NodeIterator is an iterator to traverse the entire state trie post-order,
|
||||
// including all of the contract code and contract state tries.
|
||||
type NodeIterator struct {
|
||||
state *StateDB // State being iterated
|
||||
state *State // State being iterated
|
||||
|
||||
stateIt *trie.NodeIterator // Primary iterator for the global state trie
|
||||
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.
|
||||
func NewNodeIterator(state *StateDB) *NodeIterator {
|
||||
func NewNodeIterator(state *State) *NodeIterator {
|
||||
return &NodeIterator{
|
||||
state: state,
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ func (it *NodeIterator) step() error {
|
|||
}
|
||||
// Initialize the iterator if we've just started
|
||||
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 it.dataIt != nil {
|
||||
|
|
@ -115,7 +115,7 @@ func (it *NodeIterator) step() error {
|
|||
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account); err != nil {
|
||||
return err
|
||||
}
|
||||
dataTrie, err := trie.New(account.Root, it.state.db)
|
||||
dataTrie, err := trie.New(account.Root, it.state.Db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ func (it *NodeIterator) step() error {
|
|||
}
|
||||
if bytes.Compare(account.CodeHash, emptyCodeHash) != 0 {
|
||||
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 {
|
||||
return fmt.Errorf("code %x: %v", account.CodeHash, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ type account struct {
|
|||
}
|
||||
|
||||
type ManagedState struct {
|
||||
*StateDB
|
||||
*State
|
||||
|
||||
mu sync.RWMutex
|
||||
|
||||
|
|
@ -37,18 +37,18 @@ type ManagedState struct {
|
|||
}
|
||||
|
||||
// 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{
|
||||
StateDB: statedb.Copy(),
|
||||
State: Fork(state),
|
||||
accounts: make(map[string]*account),
|
||||
}
|
||||
}
|
||||
|
||||
// SetState sets the backing layer of the managed state
|
||||
func (ms *ManagedState) SetState(statedb *StateDB) {
|
||||
func (ms *ManagedState) SetState(state *State) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
ms.StateDB = statedb
|
||||
ms.State = state
|
||||
}
|
||||
|
||||
// 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)
|
||||
return uint64(len(account.nonces)) + account.nstart
|
||||
} else {
|
||||
return ms.StateDB.GetNonce(addr)
|
||||
return ms.State.GetNonce(addr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ func (ms *ManagedState) getAccount(addr common.Address) *account {
|
|||
} else {
|
||||
// Always make sure the state account nonce isn't actually higher
|
||||
// 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 {
|
||||
ms.accounts[straddr] = newAccount(so)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func create() (*ManagedState, *account) {
|
|||
statedb, _ := New(common.Hash{}, db)
|
||||
ms := ManageState(statedb)
|
||||
so := &StateObject{address: addr, nonce: 100}
|
||||
ms.StateDB.stateObjects[addr.Str()] = so
|
||||
ms.State.StateObjects[addr] = so
|
||||
ms.accounts[addr.Str()] = newAccount(so)
|
||||
|
||||
return ms, ms.accounts[addr.Str()]
|
||||
|
|
@ -92,7 +92,7 @@ func TestRemoteNonceChange(t *testing.T) {
|
|||
account.nonces = append(account.nonces, nn...)
|
||||
nonce := ms.NewNonce(addr)
|
||||
|
||||
ms.StateDB.stateObjects[addr.Str()].nonce = 200
|
||||
ms.State.StateObjects[addr].nonce = 200
|
||||
nonce = ms.NewNonce(addr)
|
||||
if nonce != 200 {
|
||||
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.StateDB.stateObjects[addr.Str()].nonce = 200
|
||||
ms.State.StateObjects[addr].nonce = 200
|
||||
nonce = ms.NewNonce(addr)
|
||||
if nonce != 204 {
|
||||
t.Error("expected nonce after remote update to be", 201, "got", nonce)
|
||||
|
|
@ -118,7 +118,7 @@ func TestSetNonce(t *testing.T) {
|
|||
}
|
||||
|
||||
addr[0] = 1
|
||||
ms.StateDB.SetNonce(addr, 1)
|
||||
ms.State.SetNonce(addr, 1)
|
||||
|
||||
if ms.GetNonce(addr) != 1 {
|
||||
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
|
||||
|
||||
import (
|
||||
|
|
@ -24,11 +8,138 @@ import (
|
|||
checker "gopkg.in/check.v1"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"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 {
|
||||
state *StateDB
|
||||
state *State
|
||||
}
|
||||
|
||||
var _ = checker.Suite(&StateSuite{})
|
||||
|
|
@ -47,7 +158,8 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
|||
// write some of them to the trie
|
||||
s.state.UpdateStateObject(obj1)
|
||||
s.state.UpdateStateObject(obj2)
|
||||
s.state.Commit()
|
||||
|
||||
Commit(s.state)
|
||||
|
||||
// check that dump contains the state objects that are in trie
|
||||
got := string(s.state.Dump())
|
||||
|
|
@ -99,35 +211,13 @@ func TestNull(t *testing.T) {
|
|||
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
||||
var value common.Hash
|
||||
state.SetState(address, common.Hash{}, value)
|
||||
state.Commit()
|
||||
Commit(state)
|
||||
value = state.GetState(address, common.Hash{})
|
||||
if !common.EmptyHash(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
|
||||
// printing/logging in tests (-check.vv does not work)
|
||||
func TestSnapshot2(t *testing.T) {
|
||||
|
|
@ -152,8 +242,7 @@ func TestSnapshot2(t *testing.T) {
|
|||
so0.remove = false
|
||||
so0.deleted = false
|
||||
so0.dirty = true
|
||||
state.SetStateObject(so0)
|
||||
state.Commit()
|
||||
state.StateObjects[so0.Address()] = so0
|
||||
|
||||
// and one with deleted == true
|
||||
so1 := state.GetStateObject(stateobjaddr1)
|
||||
|
|
@ -163,14 +252,17 @@ func TestSnapshot2(t *testing.T) {
|
|||
so1.remove = true
|
||||
so1.deleted = true
|
||||
so1.dirty = true
|
||||
state.SetStateObject(so1)
|
||||
state.StateObjects[so1.Address()] = so1
|
||||
|
||||
so1 = state.GetStateObject(stateobjaddr1)
|
||||
if so1 != nil {
|
||||
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)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
root, _ := state.Commit()
|
||||
root, _ := Commit(state)
|
||||
|
||||
// Remove any potentially cached data from the test state creation
|
||||
trie.ClearGlobalCache()
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
|
|||
// Process returns the receipts and logs accumulated during the process and
|
||||
// returns the amount of gas that was used in the process. If any of the
|
||||
// transactions failed to execute due to insufficient gas it will return an error.
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||
func (p *StateProcessor) Process(block *types.Block, st *state.State, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
totalUsedGas = big.NewInt(0)
|
||||
|
|
@ -64,22 +64,31 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
header = block.Header()
|
||||
allLogs vm.Logs
|
||||
gp = new(GasPool).AddGas(block.GasLimit())
|
||||
forkState = state.Fork(st)
|
||||
)
|
||||
|
||||
// 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 {
|
||||
ApplyDAOHardFork(statedb)
|
||||
ApplyDAOHardFork(forkState)
|
||||
}
|
||||
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
forkState.PrepareIntermediate(tx.Hash(), block.Hash(), i)
|
||||
|
||||
txPostState, receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, forkState, header, tx, totalUsedGas, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, totalUsedGas, err
|
||||
}
|
||||
receipts = append(receipts, receipt)
|
||||
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
|
||||
}
|
||||
|
|
@ -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
|
||||
// execution of the state transition phase.
|
||||
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
|
||||
func ApplyTransaction(config *ChainConfig, 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) {
|
||||
env := NewEnv(st, config, bc, tx, header, cfg)
|
||||
|
||||
_, gas, err := ApplyMessage(env, tx, gp)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return env.state, nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Update the state with pending changes
|
||||
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.GasUsed = new(big.Int).Set(gas)
|
||||
if MessageCreatesContract(tx) {
|
||||
from, _ := tx.From()
|
||||
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
|
||||
}
|
||||
|
||||
logs := statedb.GetLogs(tx.Hash())
|
||||
receipt.Logs = logs
|
||||
receipt.Logs = env.state.Logs()
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{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
|
||||
// mining reward. The total reward consists of the static block reward
|
||||
// and rewards for included uncles. The coinbase of each uncle block is
|
||||
// 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)
|
||||
r := new(big.Int)
|
||||
for _, uncle := range uncles {
|
||||
|
|
@ -126,10 +135,10 @@ func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*t
|
|||
r.Sub(r, header.Number)
|
||||
r.Mul(r, BlockReward)
|
||||
r.Div(r, big8)
|
||||
statedb.AddBalance(uncle.Coinbase, r)
|
||||
st.AddBalance(uncle.Coinbase, r)
|
||||
|
||||
r.Div(BlockReward, big32)
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
statedb.AddBalance(header.Coinbase, reward)
|
||||
st.AddBalance(header.Coinbase, reward)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ type StateTransition struct {
|
|||
initialGas *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
state vm.Database
|
||||
|
||||
env vm.Environment
|
||||
}
|
||||
|
|
@ -116,7 +115,6 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
|
|||
initialGas: new(big.Int),
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: env.Db(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,6 +136,7 @@ func (self *StateTransition) from() (vm.Account, error) {
|
|||
var (
|
||||
f common.Address
|
||||
err error
|
||||
st = self.env.Db()
|
||||
)
|
||||
if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
|
||||
f, err = self.msg.From()
|
||||
|
|
@ -147,10 +146,10 @@ func (self *StateTransition) from() (vm.Account, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !self.state.Exist(f) {
|
||||
return self.state.CreateAccount(f), nil
|
||||
if !st.Exist(f) {
|
||||
return st.CreateAccount(f), nil
|
||||
}
|
||||
return self.state.GetAccount(f), nil
|
||||
return st.GetAccount(f), nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) to() vm.Account {
|
||||
|
|
@ -162,10 +161,11 @@ func (self *StateTransition) to() vm.Account {
|
|||
return nil // contract creation
|
||||
}
|
||||
|
||||
if !self.state.Exist(*to) {
|
||||
return self.state.CreateAccount(*to)
|
||||
state := self.env.Db()
|
||||
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 {
|
||||
|
|
@ -210,7 +210,7 @@ func (self *StateTransition) preCheck() (err error) {
|
|||
|
||||
// Make sure this transaction's nonce is correct
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -255,7 +255,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
|||
}
|
||||
} else {
|
||||
// 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)
|
||||
if err != nil {
|
||||
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())
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (self *StateTransition) refundGas() {
|
||||
state := self.env.Db()
|
||||
// Return eth for remaining gas to the sender account,
|
||||
// exchanged at the original rate.
|
||||
sender, _ := self.from() // err already checked
|
||||
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.
|
||||
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.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
|
||||
// available for the next transaction.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ var (
|
|||
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
|
||||
// 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)
|
||||
|
||||
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()
|
||||
|
||||
return newPool, key
|
||||
|
|
@ -176,7 +176,7 @@ func TestTransactionChainFork(t *testing.T) {
|
|||
resetState := func() {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
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.AddBalance(addr, big.NewInt(100000000000000))
|
||||
pool.resetState()
|
||||
|
|
@ -202,7 +202,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
|||
resetState := func() {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
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.AddBalance(addr, big.NewInt(100000000000000))
|
||||
pool.resetState()
|
||||
|
|
@ -482,7 +482,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
|
|||
db, _ := ethdb.NewMemDatabase()
|
||||
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()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import (
|
|||
type Validator interface {
|
||||
HeaderValidator
|
||||
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
|
||||
|
|
@ -58,5 +58,5 @@ type HeaderValidator interface {
|
|||
// of gas used in the process and return an error if any of the internal rules
|
||||
// failed.
|
||||
type Processor interface {
|
||||
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
|
||||
Process(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"
|
||||
)
|
||||
|
||||
// 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
|
||||
// execution of the EVM instructions (e.g. whether it's homestead)
|
||||
type RuleSet interface {
|
||||
|
|
@ -35,11 +43,17 @@ type Environment interface {
|
|||
RuleSet() RuleSet
|
||||
// The state 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
|
||||
MakeSnapshot() Database
|
||||
//MakeSnapshot() Database
|
||||
// Set database to previous snapshot
|
||||
SetSnapshot(Database)
|
||||
//SetSnapshot(Database)
|
||||
// Address of the original invoker (first occurrence of the VM invoker)
|
||||
|
||||
Origin() common.Address
|
||||
// The block number this VM is invoked on
|
||||
BlockNumber() *big.Int
|
||||
|
|
@ -59,8 +73,6 @@ type Environment interface {
|
|||
CanTransfer(from common.Address, balance *big.Int) bool
|
||||
// Transfers amount from one account to the other
|
||||
Transfer(from, to Account, amount *big.Int)
|
||||
// Adds a LOG to the state
|
||||
AddLog(*Log)
|
||||
// Type of the VM
|
||||
Vm() Vm
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
type Database interface {
|
||||
GetAccount(common.Address) Account
|
||||
|
|
@ -108,6 +112,8 @@ type Database interface {
|
|||
Delete(common.Address) bool
|
||||
Exist(common.Address) bool
|
||||
IsDeleted(common.Address) bool
|
||||
|
||||
AddLog(*Log)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
// 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())
|
||||
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) {
|
||||
|
|
@ -630,7 +633,7 @@ func makeLog(size int) instrFn {
|
|||
|
||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||
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.
|
||||
//
|
||||
// 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) {
|
||||
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.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) Coinbase() common.Address { return common.Address{} }
|
||||
func (self *Env) MakeSnapshot() Database { return nil }
|
||||
func (self *Env) SetSnapshot(Database) {}
|
||||
func (self *Env) ForkState() Database { return nil }
|
||||
func (self *Env) SetState(Database) {}
|
||||
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) Db() Database { return nil }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
type Env struct {
|
||||
ruleSet vm.RuleSet
|
||||
depth int
|
||||
state *state.StateDB
|
||||
state *state.State
|
||||
|
||||
origin common.Address
|
||||
coinbase common.Address
|
||||
|
|
@ -46,7 +46,7 @@ type Env struct {
|
|||
}
|
||||
|
||||
// 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{
|
||||
ruleSet: cfg.RuleSet,
|
||||
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 {
|
||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
||||
}
|
||||
func (self *Env) MakeSnapshot() vm.Database {
|
||||
return self.state.Copy()
|
||||
func (self *Env) ForkState() vm.Database {
|
||||
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.Set(st.(*state.State))
|
||||
}
|
||||
|
||||
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
|
||||
Debug bool
|
||||
|
||||
State *state.StateDB
|
||||
State *state.State
|
||||
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
|
||||
// the given code. It enabled the JIT by default and make sure that it's restored
|
||||
// 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 {
|
||||
cfg = new(Config)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,18 +41,20 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
|
|||
}
|
||||
|
||||
type VMEnv struct {
|
||||
chainConfig *ChainConfig // Chain configuration
|
||||
state *state.StateDB // State to use for executing
|
||||
evm vm.VirtualMachine // The Ethereum Virtual Machine
|
||||
depth int // Current execution depth
|
||||
msg Message // Message appliod
|
||||
chainConfig *ChainConfig // Chain configuration
|
||||
|
||||
state *state.State
|
||||
|
||||
evm vm.VirtualMachine // The Ethereum Virtual Machine
|
||||
depth int // Current execution depth
|
||||
msg Message // Message appliod
|
||||
|
||||
header *types.Header // Header information
|
||||
chain *BlockChain // Blockchain handle
|
||||
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{
|
||||
chainConfig: chainConfig,
|
||||
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
|
||||
}
|
||||
|
||||
func (self *VMEnv) MakeSnapshot() vm.Database {
|
||||
return self.state.Copy()
|
||||
func (self *VMEnv) ForkState() vm.Database {
|
||||
self.state = state.Fork(self.state)
|
||||
return self.state
|
||||
}
|
||||
|
||||
func (self *VMEnv) SetSnapshot(copy vm.Database) {
|
||||
self.state.Set(copy.(*state.StateDB))
|
||||
func (self *VMEnv) SetState(st vm.Database) {
|
||||
self.state = st.(*state.State)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
|
||||
stateDb := state.(EthApiState).state.Copy()
|
||||
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, st ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
|
||||
stateDb := state.Fork(st.(EthApiState).state)
|
||||
addr, _ := msg.From()
|
||||
from := stateDb.GetOrNewStateObject(addr)
|
||||
from.SetBalance(common.MaxBig)
|
||||
|
|
@ -185,7 +185,7 @@ func (b *EthApiBackend) AccountManager() *accounts.Manager {
|
|||
}
|
||||
|
||||
type EthApiState struct {
|
||||
state *state.StateDB
|
||||
state *state.State
|
||||
}
|
||||
|
||||
func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) {
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ func (self *Env) Origin() common.Address { return common.Address{} }
|
|||
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
|
||||
|
||||
//func (self *Env) PrevHash() []byte { return self.parent }
|
||||
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
||||
func (self *Env) MakeSnapshot() vm.Database { return nil }
|
||||
func (self *Env) SetSnapshot(vm.Database) {}
|
||||
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) Db() vm.Database { return nil }
|
||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
||||
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
||||
func (self *Env) ForkState() vm.Database { return nil }
|
||||
func (self *Env) SetState(vm.Database) {}
|
||||
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) Db() vm.Database { return nil }
|
||||
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) GetHash(n uint64) common.Hash {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -92,14 +93,14 @@ func (account) SetBalance(*big.Int) {}
|
|||
func (account) SetNonce(uint64) {}
|
||||
func (account) Balance() *big.Int { return nil }
|
||||
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) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
|
||||
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}
|
||||
|
||||
_, err := env.Vm().Run(contract, []byte{})
|
||||
|
|
@ -188,7 +189,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
timeout := errors.New("stahp")
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func makeTestState() (common.Hash, ethdb.Database) {
|
|||
so.Update()
|
||||
st.UpdateStateObject(so)
|
||||
}
|
||||
root, _ := st.Commit()
|
||||
root, _ := state.Commit(st)
|
||||
return root, sdb
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ func (self *Miner) SetExtra(extra []byte) error {
|
|||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ type uint64RingBuffer struct {
|
|||
// all of the current state information
|
||||
type Work struct {
|
||||
config *core.ChainConfig
|
||||
state *state.StateDB // apply state changes here
|
||||
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
|
||||
family *set.Set // family set (used for checking uncle invalidity)
|
||||
uncles *set.Set // uncle set
|
||||
tcount int // tx count in cycle
|
||||
state *state.State // apply state changes here
|
||||
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
|
||||
family *set.Set // family set (used for checking uncle invalidity)
|
||||
uncles *set.Set // uncle set
|
||||
tcount int // tx count in cycle
|
||||
ownedAccounts *set.Set
|
||||
lowGasTxs types.Transactions
|
||||
failedTxs types.Transactions
|
||||
|
|
@ -159,7 +159,7 @@ func (self *worker) setEtherbase(addr common.Address) {
|
|||
self.coinbase = addr
|
||||
}
|
||||
|
||||
func (self *worker) pending() (*types.Block, *state.StateDB) {
|
||||
func (self *worker) pending() (*types.Block, *state.State) {
|
||||
self.currentMu.Lock()
|
||||
defer self.currentMu.Unlock()
|
||||
|
||||
|
|
@ -276,7 +276,8 @@ func (self *worker) wait() {
|
|||
}
|
||||
go self.mux.Post(core.NewMinedBlockEvent{Block: block})
|
||||
} else {
|
||||
work.state.Commit()
|
||||
//work.state.Commit()
|
||||
state.Commit(work.state)
|
||||
parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
glog.V(logger.Error).Infoln("Invalid block found during mining")
|
||||
|
|
@ -528,7 +529,7 @@ func (self *worker) commitNewWork() {
|
|||
if atomic.LoadInt32(&self.mining) == 1 {
|
||||
// commit state root after all state transitions.
|
||||
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.
|
||||
|
|
@ -582,8 +583,8 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
|||
|
||||
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)
|
||||
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) {
|
||||
snap := env.state.Copy()
|
||||
|
||||
// this is a bit of a hack to force jit for the miners
|
||||
config := env.config.VmConfig
|
||||
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
|
||||
|
||||
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 {
|
||||
env.state.Set(snap)
|
||||
return err, nil
|
||||
}
|
||||
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
|
||||
// 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)
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, fmt.Errorf("error writing state: %v", err)
|
||||
}
|
||||
|
|
@ -362,7 +362,7 @@ func validateHeader(h *btHeader, h2 *types.Header) error {
|
|||
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
|
||||
for addrString, acct := range t.postAccounts {
|
||||
// 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)
|
||||
for addr, account := range test.Pre {
|
||||
obj := StateObjectFromAccount(db, addr, account)
|
||||
statedb.SetStateObject(obj)
|
||||
statedb.StateObjects[obj.Address()] = obj
|
||||
for a, v := range account.Storage {
|
||||
obj.SetState(common.HexToHash(a), common.HexToHash(v))
|
||||
}
|
||||
|
|
@ -121,13 +121,11 @@ func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string)
|
|||
continue
|
||||
}
|
||||
|
||||
//fmt.Println("StateTest:", name)
|
||||
if err := runStateTest(ruleSet, test); err != nil {
|
||||
return fmt.Errorf("%s: %s\n", name, err.Error())
|
||||
}
|
||||
|
||||
//glog.Infoln("State test passed: ", name)
|
||||
//fmt.Println(string(statedb.Dump()))
|
||||
}
|
||||
return nil
|
||||
|
||||
|
|
@ -138,7 +136,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
|
|||
statedb, _ := state.New(common.Hash{}, db)
|
||||
for addr, account := range test.Pre {
|
||||
obj := StateObjectFromAccount(db, addr, account)
|
||||
statedb.SetStateObject(obj)
|
||||
statedb.StateObjects[obj.Address()] = obj
|
||||
for a, v := range account.Storage {
|
||||
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)
|
||||
|
||||
// Compare expected and actual return
|
||||
rexp := common.FromHex(test.Out)
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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 (
|
||||
data = common.FromHex(tx["data"])
|
||||
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
|
||||
//vm.Precompiled = vm.PrecompiledContracts()
|
||||
snapshot := statedb.Copy()
|
||||
snapshot := statedb
|
||||
nstatedb := state.Fork(snapshot)
|
||||
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
|
||||
|
||||
key, _ := hex.DecodeString(tx["secretKey"])
|
||||
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
|
||||
message := NewMessage(addr, to, data, value, gas, price, nonce)
|
||||
vmenv := NewEnvFromMap(ruleSet, statedb, env, tx)
|
||||
vmenv := NewEnvFromMap(ruleSet, nstatedb, env, tx)
|
||||
vmenv.origin = addr
|
||||
ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
|
||||
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 {
|
||||
ruleSet RuleSet
|
||||
depth int
|
||||
state *state.StateDB
|
||||
state *state.State
|
||||
skipTransfer bool
|
||||
initial bool
|
||||
Gas, gasPrice *big.Int
|
||||
|
|
@ -171,7 +171,7 @@ type Env struct {
|
|||
evm vm.VirtualMachine
|
||||
}
|
||||
|
||||
func NewEnv(ruleSet RuleSet, state *state.StateDB) *Env {
|
||||
func NewEnv(ruleSet RuleSet, state *state.State) *Env {
|
||||
env := &Env{
|
||||
ruleSet: ruleSet,
|
||||
state: state,
|
||||
|
|
@ -179,7 +179,7 @@ func NewEnv(ruleSet RuleSet, state *state.StateDB) *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.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
|
||||
}
|
||||
func (self *Env) MakeSnapshot() vm.Database {
|
||||
return self.state.Copy()
|
||||
func (self *Env) ForkState() vm.Database {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
|
|||
statedb, _ := state.New(common.Hash{}, db)
|
||||
for addr, account := range test.Pre {
|
||||
obj := StateObjectFromAccount(db, addr, account)
|
||||
statedb.SetStateObject(obj)
|
||||
statedb.StateObjects[obj.Address()] = obj
|
||||
for a, v := range account.Storage {
|
||||
obj.SetState(common.HexToHash(a), common.HexToHash(v))
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ func runVmTest(test VmTest) error {
|
|||
statedb, _ := state.New(common.Hash{}, db)
|
||||
for addr, account := range test.Pre {
|
||||
obj := StateObjectFromAccount(db, addr, account)
|
||||
statedb.SetStateObject(obj)
|
||||
statedb.StateObjects[obj.Address()] = obj
|
||||
for a, v := range account.Storage {
|
||||
obj.SetState(common.HexToHash(a), common.HexToHash(v))
|
||||
}
|
||||
|
|
@ -227,7 +227,7 @@ func runVmTest(test VmTest) error {
|
|||
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 (
|
||||
to = common.HexToAddress(exec["address"])
|
||||
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.
|
||||
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.skipTransfer = true
|
||||
vmenv.initial = true
|
||||
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