(feat)all: add EIP-1153 - transient storage (#721)

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

Implements TSTORE and TLOAD as specified by the following EIP:

https://eips.ethereum.org/EIPS/eip-1153
https://ethereum-magicians.org/t/eip-1153-transient-storage-opcodes/553

Co-authored-by: Sara Reynolds <snreynolds2506@gmail.com>
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Gary Rong <garyrong0905@gmail.com>

* core/vm: move TSTORE,TLOAD to correct opcode nums (#27613)

* core/vm: move TSTORE,TLOAD to correct opcode nums

* core/vm: cleanup

* fix tests, rename

* goimports

* enable 1153 at Curie

* bump version

* comment fix

* improve test

* version

* testchainconfig

* fix another test that affects newly added

* fix previous test to clenaup after

---------

Co-authored-by: Mark Tyneway <mark.tyneway@gmail.com>
Co-authored-by: Sara Reynolds <snreynolds2506@gmail.com>
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Gary Rong <garyrong0905@gmail.com>
Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
This commit is contained in:
Nazarii Denha 2024-04-28 09:59:26 +02:00 committed by GitHub
parent a860446eba
commit 3592139384
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 478 additions and 84 deletions

View file

@ -162,7 +162,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
vmConfig.Tracer = tracer
vmConfig.Debug = (tracer != nil)
statedb.Prepare(tx.Hash(), txIndex)
statedb.SetTxContext(tx.Hash(), txIndex)
txContext := core.NewEVMTxContext(msg)
snapshot := statedb.Snapshot()
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)

View file

@ -3188,6 +3188,9 @@ func TestTransactionCountLimit(t *testing.T) {
config := params.TestChainConfig
config.Scroll.MaxTxPerBlock = new(int)
*config.Scroll.MaxTxPerBlock = 1
defer func() {
config.Scroll.MaxTxPerBlock = nil
}()
var (
engine = ethash.NewFaker()
@ -3359,6 +3362,10 @@ func TestL1MessageValidationFailure(t *testing.T) {
config.Scroll.L1Config.NumL1MessagesPerBlock = 1
maxPayload := 1024
config.Scroll.MaxTxPayloadBytesPerBlock = &maxPayload
defer func() {
config.Scroll.MaxTxPayloadBytesPerBlock = nil
config.Scroll.L1Config.NumL1MessagesPerBlock = 0
}()
genspec := &Genesis{
Config: config,
@ -3437,7 +3444,9 @@ func TestBlockPayloadSizeLimit(t *testing.T) {
config := params.TestChainConfig
config.Scroll.MaxTxPayloadBytesPerBlock = new(int)
*config.Scroll.MaxTxPayloadBytesPerBlock = 150
config.Scroll.MaxTxPerBlock = nil
defer func() {
config.Scroll.MaxTxPayloadBytesPerBlock = nil
}()
var (
engine = ethash.NewFaker()
@ -3607,3 +3616,99 @@ func TestEIP3651(t *testing.T) {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
}
}
// TestTransientStorageReset ensures the transient storage is wiped correctly
// between transactions.
func TestTransientStorageReset(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
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},
},
}
genesis := gspec.MustCommit(db)
nonce := uint64(0)
signer := types.HomesteadSigner{}
blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 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++
})
// Initialize the blockchain with 1153 enabled.
chain, err := NewBlockChain(db, nil, gspec.Config, engine, vmConfig, nil, nil)
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

@ -79,6 +79,26 @@ func (b *BlockGen) SetDifficulty(diff *big.Int) {
b.header.Difficulty = diff
}
// 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{})
}
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
if err != nil {
panic(err)
}
b.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt)
}
// AddTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
//
@ -88,7 +108,7 @@ func (b *BlockGen) SetDifficulty(diff *big.Int) {
// added. Notably, contract code relying on the BLOCKHASH instruction
// will panic during execution.
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
@ -100,16 +120,14 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
// added. If contract code relies on the BLOCKHASH instruction,
// the block in chain will be returned.
func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
if b.gasPool == nil {
b.SetCoinbase(common.Address{})
}
b.statedb.Prepare(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, 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)
b.addTx(bc, vm.Config{}, tx)
}
// 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)
}
// GetBalance returns the balance of the given address at the generated block.

View file

@ -138,6 +138,11 @@ type (
address *common.Address
slot *common.Hash
}
transientStorageChange struct {
account *common.Address
key, prevalue common.Hash
}
)
func (ch createObjectChange) revert(s *StateDB) {
@ -213,6 +218,14 @@ func (ch storageChange) dirtied() *common.Address {
return ch.account
}
func (ch transientStorageChange) revert(s *StateDB) {
s.setTransientState(*ch.account, ch.key, ch.prevalue)
}
func (ch transientStorageChange) dirtied() *common.Address {
return nil
}
func (ch refundChange) revert(s *StateDB) {
s.refund = ch.prev
}

View file

@ -97,6 +97,9 @@ type StateDB struct {
// Per-transaction access list
accessList *accessList
// Transient storage
transientStorage transientStorage
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal *journal
@ -140,6 +143,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
preimages: make(map[common.Hash][]byte),
journal: newJournal(),
accessList: newAccessList(),
transientStorage: newTransientStorage(),
hasher: crypto.NewKeccakState(),
}
if sdb.snaps != nil {
@ -472,6 +476,35 @@ func (s *StateDB) Suicide(addr common.Address) bool {
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(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.
//
@ -635,8 +668,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
// a contract does the following:
//
// 1. sends funds to sha(account ++ (nonce + 1))
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
// 1. sends funds to sha(account ++ (nonce + 1))
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
//
// Carrying over the balance ensures that Ether doesn't disappear.
func (s *StateDB) CreateAccount(addr common.Address) {
@ -741,6 +774,8 @@ func (s *StateDB) Copy() *StateDB {
// to not blow up if we ever decide copy it in the middle of a transaction
state.accessList = s.accessList.Copy()
state.transientStorage = s.transientStorage.Copy()
// If there's a prefetcher running, make an inactive copy of it that can
// only access data but does not actively preload (since the user will not
// know that they need to explicitly terminate an active copy).
@ -913,9 +948,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
return s.trie.Hash()
}
// Prepare sets the current transaction hash and index which are
// used when the EVM emits new state logs.
func (s *StateDB) Prepare(thash common.Hash, ti int) {
// SetTxContext sets the current transaction hash and index which are
// used when the EVM emits new state logs. It should be invoked before
// transaction execution.
func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
s.thash = thash
s.txIndex = ti
s.accessList = newAccessList()
@ -1018,34 +1054,45 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
return root, err
}
// PrepareAccessList handles the preparatory steps for executing a state transition with
// regards to EIP-2929, EIP-2930 and EIP-3651:
// Prepare handles the preparatory steps for executing a state transition with.
// This method must be invoked before state transition.
//
// Berlin fork:
// - Add sender to access list (2929)
// - Add destination to access list (2929)
// - Add precompiles to access list (2929)
// - Add the contents of the optional tx access list (2930)
// - Add coinbase to access list (3651)
//
// This method should only be called if Berlin/2929+2930 is applicable at the current number.
func (s *StateDB) PrepareAccessList(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
s.AddAddressToAccessList(sender)
if dst != nil {
s.AddAddressToAccessList(*dst)
// If it's a create-tx, the destination will be added inside evm.create
}
for _, addr := range precompiles {
s.AddAddressToAccessList(addr)
}
for _, el := range list {
s.AddAddressToAccessList(el.Address)
for _, key := range el.StorageKeys {
s.AddSlotToAccessList(el.Address, key)
// Potential EIPs:
// - Reset access list (Berlin)
// - Add coinbase to access list (EIP-3651)
// - Reset transient storage (EIP-1153)
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
if rules.IsBerlin {
// Clear out any leftover from previous executions
al := newAccessList()
s.accessList = al
al.AddAddress(sender)
if dst != nil {
al.AddAddress(*dst)
// If it's a create-tx, the destination will be added inside evm.create
}
for _, addr := range precompiles {
al.AddAddress(addr)
}
for _, el := range list {
al.AddAddress(el.Address)
for _, key := range el.StorageKeys {
al.AddSlot(el.Address, key)
}
}
if rules.IsShanghai { // EIP-3651: warm coinbase
al.AddAddress(coinbase)
}
}
if rules.IsShanghai { // EIP-3651: warm coinbase
s.AddAddressToAccessList(coinbase)
}
// Reset transient storage at the beginning of transaction execution
s.transientStorage = newTransientStorage()
}
// AddAddressToAccessList adds the given address to the access list

View file

@ -342,6 +342,16 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
},
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))]
var nameargs []string
@ -916,3 +926,37 @@ func TestStateDBAccessList(t *testing.T) {
t.Fatalf("expected empty, got %d", got)
}
}
func TestStateDBTransientStorage(t *testing.T) {
memDb := rawdb.NewMemoryDatabase()
db := NewDatabase(memDb)
state, _ := New(common.Hash{}, db, nil)
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.revert(state, 0)
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/scroll-tech/go-ethereum/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

@ -69,7 +69,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
if err != nil {
return // Also invalid block, bail out
}
statedb.Prepare(tx.Hash(), i)
statedb.SetTxContext(tx.Hash(), i)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {

View file

@ -95,7 +95,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
statedb.Prepare(tx.Hash(), i)
statedb.SetTxContext(tx.Hash(), i)
receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)

View file

@ -381,11 +381,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if rules.IsShanghai && contractCreation && len(st.data) > params.MaxInitCodeSize {
return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize)
}
// Set up the initial access list.
if rules.IsBerlin {
st.state.PrepareAccessList(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
}
// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
var (
ret []byte

View file

@ -22,6 +22,8 @@ import (
"github.com/holiman/uint256"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/params"
)
@ -35,6 +37,7 @@ var activators = map[int]func(*JumpTable){
2200: enable2200,
1884: enable1884,
1344: enable1344,
1153: enable1153,
}
// EnableEIP enables the given EIP on the config.
@ -173,6 +176,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
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee)

View file

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

View file

@ -21,12 +21,16 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"testing"
"strings"
"github.com/holiman/uint256"
"github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/core/state"
"github.com/scroll-tech/go-ethereum/common/math"
"github.com/scroll-tech/go-ethereum/common"
@ -49,6 +53,14 @@ var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffff
var commonParams []*twoOperandParams
var twoOpMethods map[string]executionFunc
type contractRef struct {
addr common.Address
}
func (c contractRef) Address() common.Address {
return c.addr
}
func init() {
// Params is a list of common edgecases that should be used for some common tests
@ -563,6 +575,49 @@ func BenchmarkOpMstore(bench *testing.B) {
}
}
func TestOpTstore(t *testing.T) {
var (
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
env = NewEVM(BlockContext{}, TxContext{}, statedb, 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 BenchmarkOpSHA3(bench *testing.B) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})

View file

@ -55,6 +55,9 @@ type StateDB interface {
GetProofByHash(addrHash common.Hash) ([][]byte, error)
GetStorageProof(a common.Address, key common.Hash) ([][]byte, error)
GetTransientState(addr common.Address, key common.Hash) common.Hash
SetTransientState(addr common.Address, key, value common.Hash)
Suicide(common.Address) bool
HasSuicided(common.Address) bool
@ -65,7 +68,6 @@ type StateDB interface {
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
PrepareAccessList(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
AddressInAccessList(addr common.Address) 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
@ -74,6 +76,7 @@ type StateDB interface {
// 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
AddSlotToAccessList(addr common.Address, slot common.Hash)
Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
RevertToSnapshot(int)
Snapshot() int

View file

@ -70,7 +70,6 @@ type JumpTable [256]*operation
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, and descartes instructions.
func newDescartesInstructionSet() JumpTable {
instructionSet := newCurieInstructionSet()
return instructionSet
}
@ -80,6 +79,7 @@ func newCurieInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
enable5656(&instructionSet) // EIP-5656 (MCOPY opcode)
enable1153(&instructionSet) // EIP-1153 (TLOAD, TSTORE opcodes)
return instructionSet
}

View file

@ -120,6 +120,8 @@ const (
MSIZE OpCode = 0x59
GAS OpCode = 0x5a
JUMPDEST OpCode = 0x5b
TLOAD OpCode = 0x5c
TSTORE OpCode = 0x5d
MCOPY OpCode = 0x5e
PUSH0 OpCode = 0x5f
)
@ -303,6 +305,8 @@ var opCodeToString = map[OpCode]string{
MSIZE: "MSIZE",
GAS: "GAS",
JUMPDEST: "JUMPDEST",
TLOAD: "TLOAD",
TSTORE: "TSTORE",
MCOPY: "MCOPY",
PUSH0: "PUSH0",
@ -379,7 +383,7 @@ var opCodeToString = map[OpCode]string{
LOG3: "LOG3",
LOG4: "LOG4",
// 0xf0 range.
// 0xf0 range - closures.
CREATE: "CREATE",
CALL: "CALL",
RETURN: "RETURN",
@ -468,6 +472,8 @@ var stringToOp = map[string]OpCode{
"MSIZE": MSIZE,
"GAS": GAS,
"JUMPDEST": JUMPDEST,
"TLOAD": TLOAD,
"TSTORE": TSTORE,
"MCOPY": MCOPY,
"PUSH0": PUSH0,
"PUSH1": PUSH1,

View file

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

View file

@ -179,7 +179,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD
from, _ := types.Sender(signer, tx)
// Execute the transaction
env.state.Prepare(tx.Hash(), env.tcount)
env.state.SetTxContext(tx.Hash(), env.tcount)
err = env.commitTransaction(tx, coinbase)
switch err {
case core.ErrGasLimitReached:

View file

@ -37,15 +37,15 @@ import (
// base layer statedb can be passed then it's regarded as the statedb of the
// parent block.
// Parameters:
// - block: The block for which we want the state (== state at the stateRoot of the parent)
// - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
// - base: If the caller is tracing multiple blocks, the caller can provide the parent state
// continuously from the callsite.
// - checklive: if true, then the live 'blockchain' state database is used. If the caller want to
// perform Commit or other 'save-to-disk' changes, this should be set to false to avoid
// storing trash persistently
// - preferDisk: this arg can be used by the caller to signal that even though the 'base' is provided,
// it would be preferrable to start from a fresh state, if we have it on disk.
// - block: The block for which we want the state (== state at the stateRoot of the parent)
// - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
// - base: If the caller is tracing multiple blocks, the caller can provide the parent state
// continuously from the callsite.
// - checklive: if true, then the live 'blockchain' state database is used. If the caller want to
// perform Commit or other 'save-to-disk' changes, this should be set to false to avoid
// storing trash persistently
// - preferDisk: this arg can be used by the caller to signal that even though the 'base' is provided,
// it would be preferrable to start from a fresh state, if we have it on disk.
func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
var (
current *types.Block
@ -191,7 +191,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
statedb.Prepare(tx.Hash(), idx)
statedb.SetTxContext(tx.Hash(), idx)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
return nil, vm.BlockContext{}, nil, err

View file

@ -543,7 +543,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
txContext = core.NewEVMTxContext(msg)
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
)
statedb.Prepare(tx.Hash(), i)
statedb.SetTxContext(tx.Hash(), i)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
@ -649,7 +649,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// Generate the next state snapshot fast without tracing
msg, _ := tx.AsMessage(signer, block.BaseFee())
statedb.Prepare(tx.Hash(), i)
statedb.SetTxContext(tx.Hash(), i)
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
@ -768,7 +768,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
}
// Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
statedb.Prepare(tx.Hash(), i)
statedb.SetTxContext(tx.Hash(), i)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err == nil {
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee)
@ -948,7 +948,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
}
// Call Prepare to clear out the statedb access list
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), l1DataFee)
if err != nil {

View file

@ -59,7 +59,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
msg, _ := tx.AsMessage(signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), leth.blockchain, leth.blockchain.Config(), nil)
statedb.Prepare(tx.Hash(), idx)
statedb.SetTxContext(tx.Hash(), idx)
if idx == txIndex {
return msg, context, statedb, nil
}

View file

@ -1109,7 +1109,7 @@ loop:
continue
}
// Start executing the transaction
w.current.state.Prepare(tx.Hash(), w.current.tcount)
w.current.state.SetTxContext(tx.Hash(), w.current.tcount)
logs, traces, err := w.commitTransaction(tx, coinbase)
switch {

View file

@ -24,7 +24,7 @@ import (
const (
VersionMajor = 5 // Major version component of the current release
VersionMinor = 3 // Minor version component of the current release
VersionPatch = 6 // Patch version component of the current release
VersionPatch = 7 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string
)

View file

@ -226,7 +226,7 @@ func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error
// Generate the next state snapshot fast without tracing
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
env.state.Prepare(tx.Hash(), i)
env.state.SetTxContext(tx.Hash(), i)
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{})
l1DataFee, err := fees.CalculateL1DataFee(tx, env.state)
if err != nil {
@ -331,7 +331,7 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B
vmenv := vm.NewEVM(env.blockCtx, txContext, state, env.chainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
// Call Prepare to clear out the statedb access list
state.Prepare(txctx.TxHash, txctx.TxIndex)
state.SetTxContext(txctx.TxHash, txctx.TxIndex)
// Computes the new state by applying the given message.
l1DataFee, err := fees.CalculateL1DataFee(tx, state)