all: implement EIP-1153 transient storage (#26003)

This commit is contained in:
Daniel Liu 2024-11-15 19:39:18 +08:00
parent 1c5e564433
commit ed242b4763
18 changed files with 446 additions and 67 deletions

View file

@ -381,7 +381,7 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs
var gasUsed = new(uint64) var gasUsed = new(uint64)
var receipts types.Receipts var receipts types.Receipts
for i, tx := range txs { for i, tx := range txs {
statedb.Prepare(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
if err != nil { if err != nil {
return nil, fmt.Errorf("%v when applying transaction", err) return nil, fmt.Errorf("%v when applying transaction", err)

View file

@ -698,7 +698,7 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs
var gasUsed = new(uint64) var gasUsed = new(uint64)
var receipts types.Receipts var receipts types.Receipts
for i, tx := range txs { for i, tx := range txs {
statedb.Prepare(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
if err != nil { if err != nil {
return nil, fmt.Errorf("%v when applying transaction", err) return nil, fmt.Errorf("%v when applying transaction", err)

View file

@ -1512,3 +1512,100 @@ func TestEIP2718Transition(t *testing.T) {
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed()) t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
} }
} }
// TestTransientStorageReset ensures the transient storage is wiped correctly
// between transactions.
func TestTransientStorageReset(t *testing.T) {
var (
engine = ethash.NewFaker()
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
destAddress = crypto.CreateAddress(address, 0)
funds = big.NewInt(1000000000000000)
vmConfig = vm.Config{
ExtraEips: []int{1153}, // Enable transient storage EIP
}
)
code := append([]byte{
// TLoad value with location 1
byte(vm.PUSH1), 0x1,
byte(vm.TLOAD),
// PUSH location
byte(vm.PUSH1), 0x1,
// SStore location:value
byte(vm.SSTORE),
}, make([]byte, 32-6)...)
initCode := []byte{
// TSTORE 1:1
byte(vm.PUSH1), 0x1,
byte(vm.PUSH1), 0x1,
byte(vm.TSTORE),
// Get the runtime-code on the stack
byte(vm.PUSH32)}
initCode = append(initCode, code...)
initCode = append(initCode, []byte{
byte(vm.PUSH1), 0x0, // offset
byte(vm.MSTORE),
byte(vm.PUSH1), 0x6, // size
byte(vm.PUSH1), 0x0, // offset
byte(vm.RETURN), // return 6 bytes of zero-code
}...)
gspec := &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{
address: {Balance: funds},
},
}
nonce := uint64(0)
signer := types.HomesteadSigner{}
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
fee := big.NewInt(1)
if b.header.BaseFee != nil {
fee = b.header.BaseFee
}
b.SetCoinbase(common.Address{1})
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
Nonce: nonce,
GasPrice: new(big.Int).Set(fee),
Gas: 100000,
Data: initCode,
})
nonce++
b.AddTxWithVMConfig(tx, vmConfig)
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
Nonce: nonce,
GasPrice: new(big.Int).Set(fee),
Gas: 100000,
To: &destAddress,
})
b.AddTxWithVMConfig(tx, vmConfig)
nonce++
})
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)
// Initialize the blockchain with 1153 enabled.
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vmConfig)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
// Import the blocks
if _, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("failed to insert into chain: %v", err)
}
// Check the storage
state, err := chain.StateAt(chain.CurrentHeader().Root)
if err != nil {
t.Fatalf("Failed to load state %v", err)
}
loc := common.BytesToHash([]byte{1})
slot := state.GetState(destAddress, loc)
if slot != (common.Hash{}) {
t.Fatalf("Unexpected dirty storage slot")
}
}

View file

@ -75,6 +75,31 @@ func (b *BlockGen) SetExtra(data []byte) {
b.header.Extra = data b.header.Extra = data
} }
// addTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
//
// There are a few options can be passed as well in order to run some
// customized rules.
// - bc: enables the ability to query historical block hashes for BLOCKHASH
// - vmConfig: extends the flexibility for customizing evm rules, e.g. enable extra EIPs
func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transaction) {
if b.gasPool == nil {
b.SetCoinbase(common.Address{})
}
feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb)
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vmConfig)
if err != nil {
panic(err)
}
b.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt)
if tokenFeeUsed {
fee := common.GetGasFee(b.header.Number.Uint64(), gas)
state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee)
}
}
// AddTx adds a transaction to the generated block. If no coinbase has // AddTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address. // been set, the block's coinbase is set to the zero address.
// //
@ -84,7 +109,7 @@ func (b *BlockGen) SetExtra(data []byte) {
// added. Notably, contract code relying on the BLOCKHASH instruction // added. Notably, contract code relying on the BLOCKHASH instruction
// will panic during execution. // will panic during execution.
func (b *BlockGen) AddTx(tx *types.Transaction) { func (b *BlockGen) AddTx(tx *types.Transaction) {
b.AddTxWithChain(nil, tx) b.addTx(nil, vm.Config{}, tx)
} }
// AddTxWithChain adds a transaction to the generated block. If no coinbase has // AddTxWithChain adds a transaction to the generated block. If no coinbase has
@ -96,21 +121,14 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
// added. If contract code relies on the BLOCKHASH instruction, // added. If contract code relies on the BLOCKHASH instruction,
// the block in chain will be returned. // the block in chain will be returned.
func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
if b.gasPool == nil { b.addTx(bc, vm.Config{}, tx)
b.SetCoinbase(common.Address{})
}
feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb)
b.statedb.Prepare(tx.Hash(), len(b.txs))
receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
b.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt)
if tokenFeeUsed {
fee := common.GetGasFee(b.header.Number.Uint64(), gas)
state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee)
} }
// AddTxWithVMConfig adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
// The evm interpreter can be customized with the provided vm config.
func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
b.addTx(nil, config, tx)
} }
// AddUncheckedTx forcefully adds a transaction to the block without any // AddUncheckedTx forcefully adds a transaction to the block without any

View file

@ -88,6 +88,11 @@ type (
address *common.Address address *common.Address
slot *common.Hash slot *common.Hash
} }
transientStorageChange struct {
account *common.Address
key, prevalue common.Hash
}
) )
func (ch createObjectChange) undo(s *StateDB) { func (ch createObjectChange) undo(s *StateDB) {
@ -134,6 +139,10 @@ func (ch storageChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
} }
func (ch transientStorageChange) undo(s *StateDB) {
s.setTransientState(*ch.account, ch.key, ch.prevalue)
}
func (ch refundChange) undo(s *StateDB) { func (ch refundChange) undo(s *StateDB) {
s.refund = ch.prev s.refund = ch.prev
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie" "github.com/XinFinOrg/XDPoSChain/trie"
) )
@ -69,6 +70,9 @@ type StateDB struct {
// Per-transaction access list // Per-transaction access list
accessList *accessList accessList *accessList
// Transient storage
transientStorage transientStorage
// Journal of state modifications. This is the backbone of // Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot. // Snapshot and RevertToSnapshot.
journal journal journal journal
@ -117,6 +121,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
logs: make(map[common.Hash][]*types.Log), logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte), preimages: make(map[common.Hash][]byte),
accessList: newAccessList(), accessList: newAccessList(),
transientStorage: newTransientStorage(),
}, nil }, nil
} }
@ -406,6 +411,35 @@ func (s *StateDB) Suicide(addr common.Address) bool {
return true return true
} }
// SetTransientState sets transient storage for a given account. It
// adds the change to the journal so that it can be rolled back
// to its previous value if there is a revert.
func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) {
prev := s.GetTransientState(addr, key)
if prev == value {
return
}
s.journal = append(s.journal, transientStorageChange{
account: &addr,
key: key,
prevalue: prev,
})
s.setTransientState(addr, key, value)
}
// setTransientState is a lower level setter for transient storage. It
// is called during a revert to prevent modifications to the journal.
func (s *StateDB) setTransientState(addr common.Address, key, value common.Hash) {
s.transientStorage.Set(addr, key, value)
}
// GetTransientState gets transient storage for a given account.
func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
return s.transientStorage.Get(addr, key)
}
// //
// Setting, updating & deleting state object methods. // Setting, updating & deleting state object methods.
// //
@ -577,6 +611,9 @@ func (s *StateDB) Copy() *StateDB {
// However, it doesn't cost us much to copy an empty list, so we do it anyway // However, it doesn't cost us much to copy an empty list, so we do it anyway
// to not blow up if we ever decide copy it in the middle of a transaction // to not blow up if we ever decide copy it in the middle of a transaction
state.accessList = s.accessList.Copy() state.accessList = s.accessList.Copy()
state.transientStorage = s.transientStorage.Copy()
return state return state
} }
@ -638,9 +675,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
return s.trie.Hash() return s.trie.Hash()
} }
// Prepare sets the current transaction hash and index and block hash which is // SetTxContext sets the current transaction hash and index which are
// used when the EVM emits new state logs. // used when the EVM emits new state logs. It should be invoked before
func (s *StateDB) Prepare(thash common.Hash, ti int) { // transaction execution.
func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
s.thash = thash s.thash = thash
s.txIndex = ti s.txIndex = ti
} }
@ -717,16 +755,19 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
return root, err return root, err
} }
// PrepareAccessList handles the preparatory steps for executing a state transition with // Prepare handles the preparatory steps for executing a state transition with.
// regards to both EIP-2929 and EIP-2930: // This method must be invoked before state transition.
// //
// Berlin fork:
// - Add sender to access list (2929) // - Add sender to access list (2929)
// - Add destination to access list (2929) // - Add destination to access list (2929)
// - Add precompiles to access list (2929) // - Add precompiles to access list (2929)
// - Add the contents of the optional tx access list (2930) // - Add the contents of the optional tx access list (2930)
// //
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. // Potential EIPs:
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { // - Reset transient storage(1153)
func (s *StateDB) Prepare(rules params.Rules, sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
if rules.IsEIP1559 {
// Clear out any leftover from previous executions // Clear out any leftover from previous executions
s.accessList = newAccessList() s.accessList = newAccessList()
@ -745,6 +786,9 @@ func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address,
} }
} }
} }
// Reset transient storage at the beginning of transaction execution
s.transientStorage = newTransientStorage()
}
// AddAddressToAccessList adds the given address to the access list // AddAddressToAccessList adds the given address to the access list
func (s *StateDB) AddAddressToAccessList(addr common.Address) { func (s *StateDB) AddAddressToAccessList(addr common.Address) {

View file

@ -297,6 +297,16 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
}, },
args: make([]int64, 1), args: make([]int64, 1),
}, },
{
name: "SetTransientState",
fn: func(a testAction, s *StateDB) {
var key, val common.Hash
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
s.SetTransientState(addr, key, val)
},
args: make([]int64, 2),
},
} }
action := actions[r.Intn(len(actions))] action := actions[r.Intn(len(actions))]
var nameargs []string var nameargs []string
@ -615,3 +625,37 @@ func TestStateDBAccessList(t *testing.T) {
t.Fatalf("expected empty, got %d", got) t.Fatalf("expected empty, got %d", got)
} }
} }
func TestStateDBTransientStorage(t *testing.T) {
memDb := rawdb.NewMemoryDatabase()
db := NewDatabase(memDb)
state, _ := New(common.Hash{}, db)
key := common.Hash{0x01}
value := common.Hash{0x02}
addr := common.Address{}
state.SetTransientState(addr, key, value)
if exp, got := 1, state.journal.length(); exp != got {
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
}
// the retrieved value should equal what was set
if got := state.GetTransientState(addr, key); got != value {
t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
}
// revert the transient state being set and then check that the
// value is now the empty hash
state.journal[0].undo(state)
if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got {
t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
}
// set transient state and then copy the statedb and ensure that
// the transient state is copied
state.SetTransientState(addr, key, value)
cpy := state.Copy()
if got := cpy.GetTransientState(addr, key); got != value {
t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
}
}

View file

@ -0,0 +1,55 @@
// Copyright 2022 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 (
"github.com/XinFinOrg/XDPoSChain/common"
)
// transientStorage is a representation of EIP-1153 "Transient Storage".
type transientStorage map[common.Address]Storage
// newTransientStorage creates a new instance of a transientStorage.
func newTransientStorage() transientStorage {
return make(transientStorage)
}
// Set sets the transient-storage `value` for `key` at the given `addr`.
func (t transientStorage) Set(addr common.Address, key, value common.Hash) {
if _, ok := t[addr]; !ok {
t[addr] = make(Storage)
}
t[addr][key] = value
}
// Get gets the transient storage for `key` at the given `addr`.
func (t transientStorage) Get(addr common.Address, key common.Hash) common.Hash {
val, ok := t[addr]
if !ok {
return common.Hash{}
}
return val[key]
}
// Copy does a deep copy of the transientStorage
func (t transientStorage) Copy() transientStorage {
storage := make(transientStorage)
for key, value := range t {
storage[key] = value.Copy()
}
return storage
}

View file

@ -117,7 +117,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
return nil, nil, 0, err return nil, nil, 0, err
} }
} }
statedb.Prepare(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv) receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, 0, err
@ -197,7 +197,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
return nil, nil, 0, err return nil, nil, 0, err
} }
} }
statedb.Prepare(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv) receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, 0, err
@ -466,6 +466,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
blockContext := NewEVMBlockContext(header, bc, author) blockContext := NewEVMBlockContext(header, bc, author)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg)
coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author) coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author)
// return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv)
return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv) return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv)
} }

View file

@ -329,9 +329,10 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
return nil, 0, false, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil return nil, 0, false, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil
} }
if rules.IsEIP1559 { // Execute the preparatory steps for state transition which includes:
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) // - prepare accessList(post-berlin)
} // - reset transient storage(eip 1153)
st.state.Prepare(rules, msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
var ( var (
evm = st.evm evm = st.evm

View file

@ -33,6 +33,7 @@ var activators = map[int]func(*JumpTable){
2200: enable2200, 2200: enable2200,
1884: enable1884, 1884: enable1884,
1344: enable1344, 1344: enable1344,
1153: enable1153,
} }
// EnableEIP enables the given EIP on the config. // EnableEIP enables the given EIP on the config.
@ -157,6 +158,45 @@ func enable3198(jt *JumpTable) {
} }
} }
// enable1153 applies EIP-1153 "Transient Storage"
// - Adds TLOAD that reads from transient storage
// - Adds TSTORE that writes to transient storage
func enable1153(jt *JumpTable) {
jt[TLOAD] = &operation{
execute: opTload,
constantGas: params.WarmStorageReadCostEIP2929,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
jt[TSTORE] = &operation{
execute: opTstore,
constantGas: params.WarmStorageReadCostEIP2929,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
}
}
// opTload implements TLOAD opcode
func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
loc := scope.Stack.peek()
hash := common.Hash(loc.Bytes32())
val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash)
loc.SetBytes(val.Bytes())
return nil, nil
}
// opTstore implements TSTORE opcode
func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
loc := scope.Stack.pop()
val := scope.Stack.pop()
interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
return nil, nil
}
// opBaseFee implements BASEFEE opcode // opBaseFee implements BASEFEE opcode
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) { func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
baseFee, _ := uint256.FromBig(common.BaseFee) baseFee, _ := uint256.FromBig(common.BaseFee)

View file

@ -539,8 +539,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
} }
loc := scope.Stack.pop() loc := scope.Stack.pop()
val := scope.Stack.pop() val := scope.Stack.pop()
interpreter.evm.StateDB.SetState(scope.Contract.Address(), interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
loc.Bytes32(), val.Bytes32())
return nil, nil return nil, nil
} }

View file

@ -26,6 +26,8 @@ import (
"testing" "testing"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
@ -46,6 +48,14 @@ var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffff
var commonParams []*twoOperandParams var commonParams []*twoOperandParams
var twoOpMethods map[string]executionFunc var twoOpMethods map[string]executionFunc
type contractRef struct {
addr common.Address
}
func (c contractRef) Address() common.Address {
return c.addr
}
func init() { func init() {
// Params is a list of common edgecases that should be used for some common tests // Params is a list of common edgecases that should be used for some common tests
params := []string{ params := []string{
@ -574,6 +584,49 @@ func BenchmarkOpMstore(bench *testing.B) {
} }
} }
func TestOpTstore(t *testing.T) {
var (
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
env = NewEVM(BlockContext{}, TxContext{}, statedb, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.Config)
caller = common.Address{}
to = common.Address{1}
contractRef = contractRef{caller}
contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0)
scopeContext = ScopeContext{mem, stack, contract}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
)
// Add a stateObject for the caller and the contract being called
statedb.CreateAccount(caller)
statedb.CreateAccount(to)
env.interpreter = evmInterpreter
pc := uint64(0)
// push the value to the stack
stack.push(new(uint256.Int).SetBytes(value))
// push the location to the stack
stack.push(new(uint256.Int))
opTstore(&pc, evmInterpreter, &scopeContext)
// there should be no elements on the stack after TSTORE
if stack.len() != 0 {
t.Fatal("stack wrong size")
}
// push the location to the stack
stack.push(new(uint256.Int))
opTload(&pc, evmInterpreter, &scopeContext)
// there should be one element on the stack after TLOAD
if stack.len() != 1 {
t.Fatal("stack wrong size")
}
val := stack.peek()
if !bytes.Equal(val.Bytes(), value) {
t.Fatal("incorrect element read from transient storage")
}
}
func BenchmarkOpKeccak256(bench *testing.B) { func BenchmarkOpKeccak256(bench *testing.B) {
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})

View file

@ -21,6 +21,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
) )
// StateDB is an EVM database for full state querying. // StateDB is an EVM database for full state querying.
@ -47,6 +48,9 @@ type StateDB interface {
GetState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash) SetState(common.Address, common.Hash, common.Hash)
GetTransientState(addr common.Address, key common.Hash) common.Hash
SetTransientState(addr common.Address, key, value common.Hash)
Suicide(common.Address) bool Suicide(common.Address) bool
HasSuicided(common.Address) bool HasSuicided(common.Address) bool
@ -57,7 +61,6 @@ type StateDB interface {
// is defined according to EIP161 (balance = nonce = code = 0). // is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool Empty(common.Address) bool
PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
AddressInAccessList(addr common.Address) bool AddressInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
@ -66,6 +69,7 @@ type StateDB interface {
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet // even if the feature/fork is not active yet
AddSlotToAccessList(addr common.Address, slot common.Hash) AddSlotToAccessList(addr common.Address, slot common.Hash)
Prepare(rules params.Rules, sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
RevertToSnapshot(int) RevertToSnapshot(int)
Snapshot() int Snapshot() int

View file

@ -116,6 +116,8 @@ const (
MSIZE OpCode = 0x59 MSIZE OpCode = 0x59
GAS OpCode = 0x5a GAS OpCode = 0x5a
JUMPDEST OpCode = 0x5b JUMPDEST OpCode = 0x5b
TLOAD OpCode = 0x5c
TSTORE OpCode = 0x5d
PUSH0 OpCode = 0x5f PUSH0 OpCode = 0x5f
) )
@ -297,6 +299,8 @@ var opCodeToString = [256]string{
MSIZE: "MSIZE", MSIZE: "MSIZE",
GAS: "GAS", GAS: "GAS",
JUMPDEST: "JUMPDEST", JUMPDEST: "JUMPDEST",
TLOAD: "TLOAD",
TSTORE: "TSTORE",
PUSH0: "PUSH0", PUSH0: "PUSH0",
// 0x60 range - push. // 0x60 range - push.
@ -460,6 +464,8 @@ var stringToOp = map[string]OpCode{
"MSIZE": MSIZE, "MSIZE": MSIZE,
"GAS": GAS, "GAS": GAS,
"JUMPDEST": JUMPDEST, "JUMPDEST": JUMPDEST,
"TLOAD": TLOAD,
"TSTORE": TSTORE,
"PUSH0": PUSH0, "PUSH0": PUSH0,
"PUSH1": PUSH1, "PUSH1": PUSH1,
"PUSH2": PUSH2, "PUSH2": PUSH2,

View file

@ -120,10 +120,13 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
address = common.BytesToAddress([]byte("contract")) address = common.BytesToAddress([]byte("contract"))
vmenv = NewEnv(cfg) vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin) sender = vm.AccountRef(cfg.Origin)
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
) )
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 { // Execute the preparatory steps for state transition which includes:
cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) // - prepare accessList(post-berlin)
} // - reset transient storage(eip 1153)
cfg.State.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
cfg.State.CreateAccount(address) cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution. // set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(address, code) cfg.State.SetCode(address, code)
@ -135,7 +138,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.GasLimit, cfg.GasLimit,
cfg.Value, cfg.Value,
) )
return ret, cfg.State, err return ret, cfg.State, err
} }
@ -153,10 +155,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
var ( var (
vmenv = NewEnv(cfg) vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin) sender = vm.AccountRef(cfg.Origin)
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
) )
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 { // Execute the preparatory steps for state transition which includes:
cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) // - prepare accessList(post-berlin)
} // - reset transient storage(eip 1153)
cfg.State.Prepare(rules, cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
// Call the code with the given configuration. // Call the code with the given configuration.
code, address, leftOverGas, err := vmenv.Create( code, address, leftOverGas, err := vmenv.Create(
sender, sender,
@ -175,13 +180,16 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) { func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) {
setDefaults(cfg) setDefaults(cfg)
vmenv := NewEnv(cfg) var (
vmenv = NewEnv(cfg)
sender := cfg.State.GetOrNewStateObject(cfg.Origin) sender = cfg.State.GetOrNewStateObject(cfg.Origin)
statedb := cfg.State statedb = cfg.State
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 { rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) )
} // Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
statedb.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
// Call the code with the given configuration. // Call the code with the given configuration.
ret, leftOverGas, err := vmenv.Call( ret, leftOverGas, err := vmenv.Call(

View file

@ -522,7 +522,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
header := block.Header() header := block.Header()
msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee) msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee)
txContext := core.NewEVMTxContext(msg) txContext := core.NewEVMTxContext(msg)
statedb.Prepare(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{}) vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{})
owner := common.Address{} owner := common.Address{}
@ -746,8 +746,8 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
// Run the transaction with tracing enabled. // Run the transaction with tracing enabled.
vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
// Call Prepare to clear out the statedb access list // Call SetTxContext to clear out the statedb access list
statedb.Prepare(txctx.TxHash, txctx.TxIndex) statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
owner := common.Address{} owner := common.Address{}
ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
@ -799,7 +799,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
usedGas := new(uint64) usedGas := new(uint64)
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
for idx, tx := range block.Transactions() { for idx, tx := range block.Transactions() {
statedb.Prepare(tx.Hash(), idx) statedb.SetTxContext(tx.Hash(), idx)
if idx == txIndex { if idx == txIndex {
var balanceFee *big.Int var balanceFee *big.Int
if tx.To() != nil { if tx.To() != nil {

View file

@ -911,7 +911,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr
} }
} }
// Start executing the transaction // Start executing the transaction
w.state.Prepare(hash, w.tcount) w.state.SetTxContext(hash, w.tcount)
nonce := w.state.GetNonce(from) nonce := w.state.GetNonce(from)
if nonce != tx.Nonce() && !tx.IsSkipNonceTransaction() { if nonce != tx.Nonce() && !tx.IsSkipNonceTransaction() {
@ -1012,7 +1012,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr
continue continue
} }
// Start executing the transaction // Start executing the transaction
w.state.Prepare(hash, w.tcount) w.state.SetTxContext(hash, w.tcount)
nonce := w.state.GetNonce(from) nonce := w.state.GetNonce(from)
if nonce > tx.Nonce() { if nonce > tx.Nonce() {
// New head notification data race between the transaction pool and miner, shift // New head notification data race between the transaction pool and miner, shift