diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index bdfddfff06f6..ae57ed31d607 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -365,9 +365,9 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, setting the final state and // assembling the block. -func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { +func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, statedb vm.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { if !beacon.IsPoSHeader(header) { - return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts) + return beacon.ethone.FinalizeAndAssemble(chain, header, statedb, body, receipts) } shanghai := chain.Config().IsShanghai(header.Number, header.Time) if shanghai { @@ -381,10 +381,10 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea } } // Finalize and assemble the block. - beacon.Finalize(chain, header, state, body) + beacon.Finalize(chain, header, statedb, body) // Assign the final state root to header. - header.Root = state.IntermediateRoot(true) + header.Root = statedb.IntermediateRoot(true) // Assemble the final block. block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) @@ -392,19 +392,19 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Create the block witness and attach to block. // This step needs to happen as late as possible to catch all access events. if chain.Config().IsVerkle(header.Number, header.Time) { - keys := state.AccessEvents().Keys() + keys := statedb.AccessEvents().Keys() // Open the pre-tree to prove the pre-state against parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1) if parent == nil { return nil, fmt.Errorf("nil parent header for block %d", header.Number) } - preTrie, err := state.Database().OpenTrie(parent.Root) + preTrie, err := statedb.(*state.StateDB).Database().OpenTrie(parent.Root) if err != nil { return nil, fmt.Errorf("error opening pre-state tree root: %w", err) } vktPreTrie, okpre := preTrie.(*trie.VerkleTrie) - vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie) + vktPostTrie, okpost := statedb.(*state.StateDB).GetTrie().(*trie.VerkleTrie) // The witness is only attached iff both parent and current block are // using verkle tree. diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index d31efd744510..5acf76dec6d8 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -33,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -580,7 +579,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. -func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { +func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { if len(body.Withdrawals) > 0 { return nil, errors.New("clique does not support withdrawals") } diff --git a/consensus/consensus.go b/consensus/consensus.go index c59b9a474474..9c53920ea06f 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -21,7 +21,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" @@ -93,7 +92,7 @@ type Engine interface { // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) + FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 4f92f1282b9e..4368e57892de 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -511,7 +510,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, accumulating the block and // uncle rewards, setting the final state and assembling the block. -func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { +func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { if len(body.Withdrawals) > 0 { return nil, errors.New("ethash does not support withdrawals") } diff --git a/core/chain_makers.go b/core/chain_makers.go index 8d09390b72d8..d067eaf2f2ec 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -43,7 +43,7 @@ type BlockGen struct { cm *chainMaker parent *types.Block header *types.Header - statedb *state.StateDB + statedb vm.StateDB gasPool *GasPool txs []*types.Transaction @@ -124,7 +124,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti } // Merge the tx-local access event into the "block-local" one, in order to collect // all values, so that the witness can be built. - if b.statedb.GetTrie().IsVerkle() { + if b.statedb.(*state.StateDB).GetTrie().IsVerkle() { b.statedb.AccessEvents().Merge(evm.AccessEvents) } b.txs = append(b.txs, tx) diff --git a/core/sender_cacher.go b/core/sender_cacher.go index 73bd5c85f200..83ba1d788e2b 100644 --- a/core/sender_cacher.go +++ b/core/sender_cacher.go @@ -17,7 +17,6 @@ package core import ( - "runtime" "sync" "github.com/ethereum/go-ethereum/core/types" @@ -25,7 +24,7 @@ import ( // senderCacherOnce is used to ensure that the SenderCacher is initialized only once. var senderCacherOnce = sync.OnceValue(func() *txSenderCacher { - return newTxSenderCacher(runtime.NumCPU()) + return newTxSenderCacher(0) }) // SenderCacher returns the singleton instance of SenderCacher, initializing it if called for the first time. diff --git a/core/state/statedb.go b/core/state/statedb.go index efafdc1aa224..b586c7c09a55 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -126,7 +127,7 @@ type StateDB struct { // Per-transaction access list accessList *accessList - accessEvents *AccessEvents + accessEvents *vm.AccessEvents // Transient storage transientStorage transientStorage @@ -182,7 +183,7 @@ func New(root common.Hash, db Database) (*StateDB, error) { transientStorage: newTransientStorage(), } if db.TrieDB().IsVerkle() { - sdb.accessEvents = NewAccessEvents(db.PointCache()) + sdb.accessEvents = vm.NewAccessEvents(db.PointCache()) } return sdb, nil } @@ -649,7 +650,7 @@ func (s *StateDB) CreateContract(addr common.Address) { // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. -func (s *StateDB) Copy() *StateDB { +func (s *StateDB) Copy() vm.StateDB { // Copy all the basic fields, initialize the memory ones reader, _ := s.db.Reader(s.originalRoot) // impossible to fail state := &StateDB{ @@ -1432,6 +1433,8 @@ func (s *StateDB) Witness() *stateless.Witness { return s.witness } -func (s *StateDB) AccessEvents() *AccessEvents { +func (s *StateDB) AccessEvents() *vm.AccessEvents { return s.accessEvents } + +func (s *StateDB) SetEVM(evm *vm.EVM) {} diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index a2fdfe9a217a..1fdf15c086b7 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -23,146 +23,147 @@ import ( "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" ) -// hookedStateDB represents a statedb which emits calls to tracing-hooks +// HookedStateDB represents a statedb which emits calls to tracing-hooks // on state operations. -type hookedStateDB struct { - inner *StateDB +type HookedStateDB struct { + vm.StateDB hooks *tracing.Hooks } // NewHookedState wraps the given stateDb with the given hooks -func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB { - s := &hookedStateDB{stateDb, hooks} +func NewHookedState(stateDb vm.StateDB, hooks *tracing.Hooks) *HookedStateDB { + s := &HookedStateDB{stateDb, hooks} if s.hooks == nil { s.hooks = new(tracing.Hooks) } return s } -func (s *hookedStateDB) CreateAccount(addr common.Address) { - s.inner.CreateAccount(addr) +func (s *HookedStateDB) CreateAccount(addr common.Address) { + s.StateDB.CreateAccount(addr) } -func (s *hookedStateDB) CreateContract(addr common.Address) { - s.inner.CreateContract(addr) +func (s *HookedStateDB) CreateContract(addr common.Address) { + s.StateDB.CreateContract(addr) } -func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int { - return s.inner.GetBalance(addr) +func (s *HookedStateDB) GetBalance(addr common.Address) *uint256.Int { + return s.StateDB.GetBalance(addr) } -func (s *hookedStateDB) GetNonce(addr common.Address) uint64 { - return s.inner.GetNonce(addr) +func (s *HookedStateDB) GetNonce(addr common.Address) uint64 { + return s.StateDB.GetNonce(addr) } -func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash { - return s.inner.GetCodeHash(addr) +func (s *HookedStateDB) GetCodeHash(addr common.Address) common.Hash { + return s.StateDB.GetCodeHash(addr) } -func (s *hookedStateDB) GetCode(addr common.Address) []byte { - return s.inner.GetCode(addr) +func (s *HookedStateDB) GetCode(addr common.Address) []byte { + return s.StateDB.GetCode(addr) } -func (s *hookedStateDB) GetCodeSize(addr common.Address) int { - return s.inner.GetCodeSize(addr) +func (s *HookedStateDB) GetCodeSize(addr common.Address) int { + return s.StateDB.GetCodeSize(addr) } -func (s *hookedStateDB) AddRefund(u uint64) { - s.inner.AddRefund(u) +func (s *HookedStateDB) AddRefund(u uint64) { + s.StateDB.AddRefund(u) } -func (s *hookedStateDB) SubRefund(u uint64) { - s.inner.SubRefund(u) +func (s *HookedStateDB) SubRefund(u uint64) { + s.StateDB.SubRefund(u) } -func (s *hookedStateDB) GetRefund() uint64 { - return s.inner.GetRefund() +func (s *HookedStateDB) GetRefund() uint64 { + return s.StateDB.GetRefund() } -func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { - return s.inner.GetCommittedState(addr, hash) +func (s *HookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + return s.StateDB.GetCommittedState(addr, hash) } -func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { - return s.inner.GetState(addr, hash) +func (s *HookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + return s.StateDB.GetState(addr, hash) } -func (s *hookedStateDB) GetStorageRoot(addr common.Address) common.Hash { - return s.inner.GetStorageRoot(addr) +func (s *HookedStateDB) GetStorageRoot(addr common.Address) common.Hash { + return s.StateDB.GetStorageRoot(addr) } -func (s *hookedStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { - return s.inner.GetTransientState(addr, key) +func (s *HookedStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { + return s.StateDB.GetTransientState(addr, key) } -func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common.Hash) { - s.inner.SetTransientState(addr, key, value) +func (s *HookedStateDB) SetTransientState(addr common.Address, key, value common.Hash) { + s.StateDB.SetTransientState(addr, key, value) } -func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool { - return s.inner.HasSelfDestructed(addr) +func (s *HookedStateDB) HasSelfDestructed(addr common.Address) bool { + return s.StateDB.HasSelfDestructed(addr) } -func (s *hookedStateDB) Exist(addr common.Address) bool { - return s.inner.Exist(addr) +func (s *HookedStateDB) Exist(addr common.Address) bool { + return s.StateDB.Exist(addr) } -func (s *hookedStateDB) Empty(addr common.Address) bool { - return s.inner.Empty(addr) +func (s *HookedStateDB) Empty(addr common.Address) bool { + return s.StateDB.Empty(addr) } -func (s *hookedStateDB) AddressInAccessList(addr common.Address) bool { - return s.inner.AddressInAccessList(addr) +func (s *HookedStateDB) AddressInAccessList(addr common.Address) bool { + return s.StateDB.AddressInAccessList(addr) } -func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) { - return s.inner.SlotInAccessList(addr, slot) +func (s *HookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) { + return s.StateDB.SlotInAccessList(addr, slot) } -func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) { - s.inner.AddAddressToAccessList(addr) +func (s *HookedStateDB) AddAddressToAccessList(addr common.Address) { + s.StateDB.AddAddressToAccessList(addr) } -func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { - s.inner.AddSlotToAccessList(addr, slot) +func (s *HookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + s.StateDB.AddSlotToAccessList(addr, slot) } -func (s *hookedStateDB) PointCache() *utils.PointCache { - return s.inner.PointCache() +func (s *HookedStateDB) PointCache() *utils.PointCache { + return s.StateDB.PointCache() } -func (s *hookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) { - s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses) +func (s *HookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) { + s.StateDB.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses) } -func (s *hookedStateDB) RevertToSnapshot(i int) { - s.inner.RevertToSnapshot(i) +func (s *HookedStateDB) RevertToSnapshot(i int) { + s.StateDB.RevertToSnapshot(i) } -func (s *hookedStateDB) Snapshot() int { - return s.inner.Snapshot() +func (s *HookedStateDB) Snapshot() int { + return s.StateDB.Snapshot() } -func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) { - s.inner.AddPreimage(hash, bytes) +func (s *HookedStateDB) AddPreimage(hash common.Hash, bytes []byte) { + s.StateDB.AddPreimage(hash, bytes) } -func (s *hookedStateDB) Witness() *stateless.Witness { - return s.inner.Witness() +func (s *HookedStateDB) Witness() *stateless.Witness { + return s.StateDB.Witness() } -func (s *hookedStateDB) AccessEvents() *AccessEvents { - return s.inner.AccessEvents() +func (s *HookedStateDB) AccessEvents() *vm.AccessEvents { + return s.StateDB.AccessEvents() } -func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { - prev := s.inner.SubBalance(addr, amount, reason) +func (s *HookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { + prev := s.StateDB.SubBalance(addr, amount, reason) if s.hooks.OnBalanceChange != nil && !amount.IsZero() { newBalance := new(uint256.Int).Sub(&prev, amount) s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason) @@ -170,8 +171,8 @@ func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, rea return prev } -func (s *hookedStateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { - prev := s.inner.AddBalance(addr, amount, reason) +func (s *HookedStateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { + prev := s.StateDB.AddBalance(addr, amount, reason) if s.hooks.OnBalanceChange != nil && !amount.IsZero() { newBalance := new(uint256.Int).Add(&prev, amount) s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason) @@ -179,9 +180,9 @@ func (s *hookedStateDB) AddBalance(addr common.Address, amount *uint256.Int, rea return prev } -func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64, reason tracing.NonceChangeReason) { - prev := s.inner.GetNonce(address) - s.inner.SetNonce(address, nonce, reason) +func (s *HookedStateDB) SetNonce(address common.Address, nonce uint64, reason tracing.NonceChangeReason) { + prev := s.StateDB.GetNonce(address) + s.StateDB.SetNonce(address, nonce, reason) if s.hooks.OnNonceChangeV2 != nil { s.hooks.OnNonceChangeV2(address, prev, nonce, reason) } else if s.hooks.OnNonceChange != nil { @@ -189,8 +190,8 @@ func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64, reason tr } } -func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte { - prev := s.inner.SetCode(address, code) +func (s *HookedStateDB) SetCode(address common.Address, code []byte) []byte { + prev := s.StateDB.SetCode(address, code) if s.hooks.OnCodeChange != nil { prevHash := types.EmptyCodeHash if len(prev) != 0 { @@ -201,24 +202,24 @@ func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte { return prev } -func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash { - prev := s.inner.SetState(address, key, value) +func (s *HookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash { + prev := s.StateDB.SetState(address, key, value) if s.hooks.OnStorageChange != nil && prev != value { s.hooks.OnStorageChange(address, key, prev, value) } return prev } -func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int { +func (s *HookedStateDB) SelfDestruct(address common.Address) uint256.Int { var prevCode []byte var prevCodeHash common.Hash if s.hooks.OnCodeChange != nil { - prevCode = s.inner.GetCode(address) - prevCodeHash = s.inner.GetCodeHash(address) + prevCode = s.StateDB.GetCode(address) + prevCodeHash = s.StateDB.GetCodeHash(address) } - prev := s.inner.SelfDestruct(address) + prev := s.StateDB.SelfDestruct(address) if s.hooks.OnBalanceChange != nil && !prev.IsZero() { s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) @@ -231,16 +232,16 @@ func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int { return prev } -func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, bool) { +func (s *HookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, bool) { var prevCode []byte var prevCodeHash common.Hash if s.hooks.OnCodeChange != nil { - prevCodeHash = s.inner.GetCodeHash(address) - prevCode = s.inner.GetCode(address) + prevCodeHash = s.StateDB.GetCodeHash(address) + prevCode = s.StateDB.GetCode(address) } - prev, changed := s.inner.SelfDestruct6780(address) + prev, changed := s.StateDB.SelfDestruct6780(address) if s.hooks.OnBalanceChange != nil && changed && !prev.IsZero() { s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) @@ -253,21 +254,25 @@ func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, b return prev, changed } -func (s *hookedStateDB) AddLog(log *types.Log) { +func (s *HookedStateDB) AddLog(log *types.Log) { // The inner will modify the log (add fields), so invoke that first - s.inner.AddLog(log) + s.StateDB.AddLog(log) if s.hooks.OnLog != nil { s.hooks.OnLog(log) } } -func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) { - defer s.inner.Finalise(deleteEmptyObjects) +func (s *HookedStateDB) Finalise(deleteEmptyObjects bool) { + defer s.StateDB.Finalise(deleteEmptyObjects) if s.hooks.OnBalanceChange == nil { return } - for addr := range s.inner.journal.dirties { - obj := s.inner.stateObjects[addr] + statedb, ok := s.StateDB.(*StateDB) + if !ok { + return + } + for addr := range statedb.journal.dirties { + obj := statedb.stateObjects[addr] if obj != nil && obj.selfDestructed { // If ether was sent to account post-selfdestruct it is burnt. if bal := obj.Balance(); bal.Sign() != 0 { diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index e740c64faa3a..aedc2f3e576d 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -184,16 +184,16 @@ func TestCopy(t *testing.T) { // modify all in memory for i := byte(0); i < 255; i++ { origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i})) - ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i})) + copyObj := copy.(*StateDB).getOrNewStateObject(common.BytesToAddress([]byte{i})) + ccopyObj := ccopy.(*StateDB).getOrNewStateObject(common.BytesToAddress([]byte{i})) origObj.AddBalance(uint256.NewInt(2 * uint64(i))) copyObj.AddBalance(uint256.NewInt(3 * uint64(i))) ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i))) orig.updateStateObject(origObj) - copy.updateStateObject(copyObj) - ccopy.updateStateObject(copyObj) + copy.(*StateDB).updateStateObject(copyObj) + ccopy.(*StateDB).updateStateObject(copyObj) } // Finalise the changes on all concurrently @@ -205,15 +205,15 @@ func TestCopy(t *testing.T) { var wg sync.WaitGroup wg.Add(3) go finalise(&wg, orig) - go finalise(&wg, copy) - go finalise(&wg, ccopy) + go finalise(&wg, copy.(*StateDB)) + go finalise(&wg, ccopy.(*StateDB)) wg.Wait() // Verify that the three states have been updated independently for i := byte(0); i < 255; i++ { origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i})) - ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i})) + copyObj := copy.(*StateDB).getOrNewStateObject(common.BytesToAddress([]byte{i})) + ccopyObj := ccopy.(*StateDB).getOrNewStateObject(common.BytesToAddress([]byte{i})) if want := uint256.NewInt(3 * uint64(i)); origObj.Balance().Cmp(want) != 0 { t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) @@ -288,13 +288,13 @@ func TestCopyObjectState(t *testing.T) { } orig.Finalise(true) cpy := orig.Copy() - for _, op := range cpy.mutations { + for _, op := range cpy.(*StateDB).mutations { if have, want := op.applied, false; have != want { t.Fatalf("Error in test itself, the 'done' flag should not be set before Commit, have %v want %v", have, want) } } orig.Commit(0, true, false) - for _, op := range cpy.mutations { + for _, op := range cpy.(*StateDB).mutations { if have, want := op.applied, false; have != want { t.Fatalf("Error: original state affected copy, have %v want %v", have, want) } @@ -537,7 +537,7 @@ func (test *snapshotTest) run() bool { for i, action := range test.actions { if len(test.snapshots) > sindex && i == test.snapshots[sindex] { snapshotRevs[sindex] = state.Snapshot() - checkstates[sindex] = state.Copy() + checkstates[sindex] = state.Copy().(*StateDB) sindex++ } action.fn(action, state) @@ -1186,7 +1186,7 @@ func TestStateDBAccessList(t *testing.T) { } // Check the copy // Make a copy - state = stateCopy1 + state = stateCopy1.(*StateDB) verifyAddrs("aa", "bb") verifySlots("bb", "01", "02") if got, exp := len(state.accessList.addresses), 2; got != exp { diff --git a/core/state_processor.go b/core/state_processor.go index 902ff582e8ba..d813c9f800c8 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -54,7 +54,7 @@ func NewStateProcessor(config *params.ChainConfig, chain *HeaderChain) *StatePro // 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) (*ProcessResult, error) { +func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.Config) (*ProcessResult, error) { var ( receipts types.Receipts usedGas = new(uint64) @@ -132,7 +132,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // ApplyTransactionWithEVM attempts to apply a transaction to the given state database // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. -func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { +func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb vm.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { if hooks := evm.Config.Tracer; hooks != nil { if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) @@ -157,7 +157,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, // Merge the tx-local access event into the "block-local" one, in order to collect // all values, so that the witness can be built. - if statedb.GetTrie().IsVerkle() { + if s, ok := statedb.(*state.StateDB); ok && s.GetTrie().IsVerkle() { statedb.AccessEvents().Merge(evm.AccessEvents) } @@ -165,7 +165,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, } // MakeReceipt generates the receipt object for a transaction given its execution result. -func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { +func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb vm.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { // Create a new receipt for the transaction, storing the intermediate root and gas used // by the tx. receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas} @@ -200,7 +200,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { +func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb vm.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee) if err != nil { return nil, err diff --git a/core/state_transition.go b/core/state_transition.go index e7edd76ced9e..31b94c33a55f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -99,7 +99,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set gas += z * params.TxDataZeroGas if isContractCreation && isEIP3860 { - lenWords := toWordSize(dataLen) + lenWords := ToWordSize(dataLen) if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { return 0, ErrGasUintOverflow } @@ -131,8 +131,8 @@ func FloorDataGas(data []byte) (uint64, error) { return params.TxGas + tokens*params.TxCostFloorPerToken, nil } -// toWordSize returns the ceiled word size required for init code payment calculation. -func toWordSize(size uint64) uint64 { +// ToWordSize returns the ceiled word size required for init code payment calculation. +func ToWordSize(size uint64) uint64 { if size > math.MaxUint64-31 { return math.MaxUint64/32 + 1 } @@ -205,10 +205,10 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In // state and would never be accepted within a block. func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) { evm.SetTxContext(NewEVMTxContext(msg)) - return newStateTransition(evm, msg, gp).execute() + return NewStateTransition(evm, msg, gp, false).Execute() } -// stateTransition represents a state transition. +// StateTransition represents a state transition. // // == The State Transitioning Model // @@ -230,34 +230,36 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, err // // 5. Run Script section // 6. Derive new state root -type stateTransition struct { +type StateTransition struct { gp *GasPool msg *Message gasRemaining uint64 initialGas uint64 state vm.StateDB evm *vm.EVM + feeCharged bool } -// newStateTransition initialises and returns a new state transition object. -func newStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *stateTransition { - return &stateTransition{ - gp: gp, - evm: evm, - msg: msg, - state: evm.StateDB, +// NewStateTransition initialises and returns a new state transition object. +func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool, feeCharged bool) *StateTransition { + return &StateTransition{ + gp: gp, + evm: evm, + msg: msg, + state: evm.StateDB, + feeCharged: feeCharged, } } // to returns the recipient of the message. -func (st *stateTransition) to() common.Address { +func (st *StateTransition) to() common.Address { if st.msg == nil || st.msg.To == nil /* contract creation */ { return common.Address{} } return *st.msg.To } -func (st *stateTransition) buyGas() error { +func (st *StateTransition) BuyGas() error { mgval := new(big.Int).SetUint64(st.msg.GasLimit) mgval.Mul(mgval, st.msg.GasPrice) balanceCheck := new(big.Int).Set(mgval) @@ -286,6 +288,12 @@ func (st *stateTransition) buyGas() error { if have, want := st.state.GetBalance(st.msg.From), balanceCheckU256; have.Cmp(want) < 0 { return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want) } + mgvalU256, _ := uint256.FromBig(mgval) + st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy) + return nil +} + +func (st *StateTransition) initGas() error { if err := st.gp.SubGas(st.msg.GasLimit); err != nil { return err } @@ -296,12 +304,10 @@ func (st *stateTransition) buyGas() error { st.gasRemaining = st.msg.GasLimit st.initialGas = st.msg.GasLimit - mgvalU256, _ := uint256.FromBig(mgval) - st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy) return nil } -func (st *stateTransition) preCheck() error { +func (st *StateTransition) StatelessChecks() error { // Only check transactions that are not fake msg := st.msg if !msg.SkipNonceChecks { @@ -392,7 +398,19 @@ func (st *stateTransition) preCheck() error { return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From) } } - return st.buyGas() + return nil +} + +func (st *StateTransition) preCheck() error { + if !st.feeCharged { + if err := st.StatelessChecks(); err != nil { + return err + } + if err := st.BuyGas(); err != nil { + return err + } + } + return st.initGas() } // execute will transition the state by applying the current message and @@ -405,7 +423,7 @@ func (st *stateTransition) preCheck() error { // // However if any consensus issue encountered, return the error directly with // nil evm execution result. -func (st *stateTransition) execute() (*ExecutionResult, error) { +func (st *StateTransition) Execute() (*ExecutionResult, error) { // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses // @@ -532,7 +550,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { effectiveTip = msg.GasTipCap } } - effectiveTipU256, _ := uint256.FromBig(effectiveTip) if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 { // Skip fee payment when NoBaseFee is set and the fee fields @@ -540,7 +557,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { // the coinbase when simulating calls. } else { fee := new(uint256.Int).SetUint64(st.gasUsed()) - fee.Mul(fee, effectiveTipU256) + // Sei doesn't don't burn the base fee and instead funds the Coinbase address with the base fee + totalFeePerGas := new(big.Int).Add(st.evm.Context.BaseFee, effectiveTip) + fee.Mul(fee, uint256.MustFromBig(totalFeePerGas)) st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee) // add the coinbase to the witness iff the fee is greater than 0 @@ -558,7 +577,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { } // validateAuthorization validates an EIP-7702 authorization against the state. -func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { +func (st *StateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { // Verify chain ID is null or equal to current chain ID. if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 { return authority, ErrAuthorizationWrongChainID @@ -589,7 +608,7 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio } // applyAuthorization applies an EIP-7702 code delegation to the state. -func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) error { +func (st *StateTransition) applyAuthorization(auth *types.SetCodeAuthorization) error { authority, err := st.validateAuthorization(auth) if err != nil { return err @@ -616,7 +635,7 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) } // calcRefund computes refund counter, capped to a refund quotient. -func (st *stateTransition) calcRefund() uint64 { +func (st *StateTransition) calcRefund() uint64 { var refund uint64 if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { // Before EIP-3529: refunds were capped to gasUsed / 2 @@ -636,7 +655,7 @@ func (st *stateTransition) calcRefund() uint64 { // returnGas returns ETH for remaining gas, // exchanged at the original rate. -func (st *stateTransition) returnGas() { +func (st *StateTransition) returnGas() { remaining := uint256.NewInt(st.gasRemaining) remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice)) st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn) @@ -651,11 +670,11 @@ func (st *stateTransition) returnGas() { } // gasUsed returns the amount of gas used up by the state transition. -func (st *stateTransition) gasUsed() uint64 { +func (st *StateTransition) gasUsed() uint64 { return st.initialGas - st.gasRemaining } // blobGasUsed returns the amount of blob gas used by the message. -func (st *stateTransition) blobGasUsed() uint64 { +func (st *StateTransition) blobGasUsed() uint64 { return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob) } diff --git a/core/txpool/legacypool/noncer.go b/core/txpool/legacypool/noncer.go index 2c65dd2caea7..f97b54320f72 100644 --- a/core/txpool/legacypool/noncer.go +++ b/core/txpool/legacypool/noncer.go @@ -21,13 +21,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" ) // noncer is a tiny virtual state database to manage the executable nonces of // accounts in the pool, falling back to reading from a real state database if // an account is unknown. type noncer struct { - fallback *state.StateDB + fallback vm.StateDB nonces map[common.Address]uint64 lock sync.Mutex } diff --git a/core/types.go b/core/types.go index bed20802ab51..41997cfda4dc 100644 --- a/core/types.go +++ b/core/types.go @@ -48,7 +48,7 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) + Process(block *types.Block, statedb vm.StateDB, cfg vm.Config) (*ProcessResult, error) } // ProcessResult contains the values computed by Process. diff --git a/core/types/block.go b/core/types/block.go index c3db4d89e820..91e3bba6c7bf 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -204,10 +204,10 @@ type Body struct { // - We do not copy body data on access because it does not affect the caches, and also // because it would be too expensive. type Block struct { - header *Header - uncles []*Header - transactions Transactions - withdrawals Withdrawals + Header_ *Header + uncles []*Header + Txs Transactions + withdrawals Withdrawals // witness is not an encoded part of the block body. // It is held in Block in order for easy relaying to the places @@ -249,24 +249,24 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher ) if len(txs) == 0 { - b.header.TxHash = EmptyTxsHash + b.Header_.TxHash = EmptyTxsHash } else { - b.header.TxHash = DeriveSha(Transactions(txs), hasher) - b.transactions = make(Transactions, len(txs)) - copy(b.transactions, txs) + b.Header_.TxHash = DeriveSha(Transactions(txs), hasher) + b.Txs = make(Transactions, len(txs)) + copy(b.Txs, txs) } if len(receipts) == 0 { - b.header.ReceiptHash = EmptyReceiptsHash + b.Header_.ReceiptHash = EmptyReceiptsHash } else { - b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher) - b.header.Bloom = CreateBloom(receipts) + b.Header_.ReceiptHash = DeriveSha(Receipts(receipts), hasher) + b.Header_.Bloom = CreateBloom(receipts) } if len(uncles) == 0 { - b.header.UncleHash = EmptyUncleHash + b.Header_.UncleHash = EmptyUncleHash } else { - b.header.UncleHash = CalcUncleHash(uncles) + b.Header_.UncleHash = CalcUncleHash(uncles) b.uncles = make([]*Header, len(uncles)) for i := range uncles { b.uncles[i] = CopyHeader(uncles[i]) @@ -274,13 +274,13 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher } if withdrawals == nil { - b.header.WithdrawalsHash = nil + b.Header_.WithdrawalsHash = nil } else if len(withdrawals) == 0 { - b.header.WithdrawalsHash = &EmptyWithdrawalsHash + b.Header_.WithdrawalsHash = &EmptyWithdrawalsHash b.withdrawals = Withdrawals{} } else { hash := DeriveSha(Withdrawals(withdrawals), hasher) - b.header.WithdrawalsHash = &hash + b.Header_.WithdrawalsHash = &hash b.withdrawals = slices.Clone(withdrawals) } @@ -333,7 +333,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { if err := s.Decode(&eb); err != nil { return err } - b.header, b.uncles, b.transactions, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals + b.Header_, b.uncles, b.Txs, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals b.size.Store(rlp.ListSize(size)) return nil } @@ -341,8 +341,8 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { // EncodeRLP serializes a block as RLP. func (b *Block) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &extblock{ - Header: b.header, - Txs: b.transactions, + Header: b.Header_, + Txs: b.Txs, Uncles: b.uncles, Withdrawals: b.withdrawals, }) @@ -351,18 +351,18 @@ func (b *Block) EncodeRLP(w io.Writer) error { // Body returns the non-header content of the block. // Note the returned data is not an independent copy. func (b *Block) Body() *Body { - return &Body{b.transactions, b.uncles, b.withdrawals} + return &Body{b.Txs, b.uncles, b.withdrawals} } // Accessors for body data. These do not return a copy because the content // of the body slices does not affect the cached hash/size in block. func (b *Block) Uncles() []*Header { return b.uncles } -func (b *Block) Transactions() Transactions { return b.transactions } +func (b *Block) Transactions() Transactions { return b.Txs } func (b *Block) Withdrawals() Withdrawals { return b.withdrawals } func (b *Block) Transaction(hash common.Hash) *Transaction { - for _, transaction := range b.transactions { + for _, transaction := range b.Txs { if transaction.Hash() == hash { return transaction } @@ -372,53 +372,53 @@ func (b *Block) Transaction(hash common.Hash) *Transaction { // Header returns the block header (as a copy). func (b *Block) Header() *Header { - return CopyHeader(b.header) + return CopyHeader(b.Header_) } // Header value accessors. These do copy! -func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } -func (b *Block) GasLimit() uint64 { return b.header.GasLimit } -func (b *Block) GasUsed() uint64 { return b.header.GasUsed } -func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } -func (b *Block) Time() uint64 { return b.header.Time } - -func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } -func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } -func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } -func (b *Block) Bloom() Bloom { return b.header.Bloom } -func (b *Block) Coinbase() common.Address { return b.header.Coinbase } -func (b *Block) Root() common.Hash { return b.header.Root } -func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } -func (b *Block) TxHash() common.Hash { return b.header.TxHash } -func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } -func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } -func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } +func (b *Block) Number() *big.Int { return new(big.Int).Set(b.Header_.Number) } +func (b *Block) GasLimit() uint64 { return b.Header_.GasLimit } +func (b *Block) GasUsed() uint64 { return b.Header_.GasUsed } +func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.Header_.Difficulty) } +func (b *Block) Time() uint64 { return b.Header_.Time } + +func (b *Block) NumberU64() uint64 { return b.Header_.Number.Uint64() } +func (b *Block) MixDigest() common.Hash { return b.Header_.MixDigest } +func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.Header_.Nonce[:]) } +func (b *Block) Bloom() Bloom { return b.Header_.Bloom } +func (b *Block) Coinbase() common.Address { return b.Header_.Coinbase } +func (b *Block) Root() common.Hash { return b.Header_.Root } +func (b *Block) ParentHash() common.Hash { return b.Header_.ParentHash } +func (b *Block) TxHash() common.Hash { return b.Header_.TxHash } +func (b *Block) ReceiptHash() common.Hash { return b.Header_.ReceiptHash } +func (b *Block) UncleHash() common.Hash { return b.Header_.UncleHash } +func (b *Block) Extra() []byte { return common.CopyBytes(b.Header_.Extra) } func (b *Block) BaseFee() *big.Int { - if b.header.BaseFee == nil { + if b.Header_.BaseFee == nil { return nil } - return new(big.Int).Set(b.header.BaseFee) + return new(big.Int).Set(b.Header_.BaseFee) } -func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot } -func (b *Block) RequestsHash() *common.Hash { return b.header.RequestsHash } +func (b *Block) BeaconRoot() *common.Hash { return b.Header_.ParentBeaconRoot } +func (b *Block) RequestsHash() *common.Hash { return b.Header_.RequestsHash } func (b *Block) ExcessBlobGas() *uint64 { var excessBlobGas *uint64 - if b.header.ExcessBlobGas != nil { + if b.Header_.ExcessBlobGas != nil { excessBlobGas = new(uint64) - *excessBlobGas = *b.header.ExcessBlobGas + *excessBlobGas = *b.Header_.ExcessBlobGas } return excessBlobGas } func (b *Block) BlobGasUsed() *uint64 { var blobGasUsed *uint64 - if b.header.BlobGasUsed != nil { + if b.Header_.BlobGasUsed != nil { blobGasUsed = new(uint64) - *blobGasUsed = *b.header.BlobGasUsed + *blobGasUsed = *b.Header_.BlobGasUsed } return blobGasUsed } @@ -441,7 +441,7 @@ func (b *Block) Size() uint64 { // SanityCheck can be used to prevent that unbounded fields are // stuffed with junk data to add processing overhead func (b *Block) SanityCheck() error { - return b.header.SanityCheck() + return b.Header_.SanityCheck() } type writeCounter uint64 @@ -477,18 +477,18 @@ func CalcRequestsHash(requests [][]byte) common.Hash { // header data is copied, changes to header and to the field values // will not affect the block. func NewBlockWithHeader(header *Header) *Block { - return &Block{header: CopyHeader(header)} + return &Block{Header_: CopyHeader(header)} } // WithSeal returns a new block with the data from b but the header replaced with // the sealed one. func (b *Block) WithSeal(header *Header) *Block { return &Block{ - header: CopyHeader(header), - transactions: b.transactions, - uncles: b.uncles, - withdrawals: b.withdrawals, - witness: b.witness, + Header_: CopyHeader(header), + Txs: b.Txs, + uncles: b.uncles, + withdrawals: b.withdrawals, + witness: b.witness, } } @@ -496,11 +496,11 @@ func (b *Block) WithSeal(header *Header) *Block { // provided body. func (b *Block) WithBody(body Body) *Block { block := &Block{ - header: b.header, - transactions: slices.Clone(body.Transactions), - uncles: make([]*Header, len(body.Uncles)), - withdrawals: slices.Clone(body.Withdrawals), - witness: b.witness, + Header_: b.Header_, + Txs: slices.Clone(body.Transactions), + uncles: make([]*Header, len(body.Uncles)), + withdrawals: slices.Clone(body.Withdrawals), + witness: b.witness, } for i := range body.Uncles { block.uncles[i] = CopyHeader(body.Uncles[i]) @@ -510,11 +510,11 @@ func (b *Block) WithBody(body Body) *Block { func (b *Block) WithWitness(witness *ExecutionWitness) *Block { return &Block{ - header: b.header, - transactions: b.transactions, - uncles: b.uncles, - withdrawals: b.withdrawals, - witness: witness, + Header_: b.Header_, + Txs: b.Txs, + uncles: b.uncles, + withdrawals: b.withdrawals, + witness: witness, } } @@ -524,7 +524,7 @@ func (b *Block) Hash() common.Hash { if hash := b.hash.Load(); hash != nil { return *hash } - h := b.header.Hash() + h := b.Header_.Hash() b.hash.Store(&h) return h } diff --git a/core/state/access_events.go b/core/vm/access_events.go similarity index 99% rename from core/state/access_events.go rename to core/vm/access_events.go index b745c383b15c..ac451e561642 100644 --- a/core/state/access_events.go +++ b/core/vm/access_events.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package state +package vm import ( "maps" diff --git a/core/state/access_events_test.go b/core/vm/access_events_test.go similarity index 95% rename from core/state/access_events_test.go rename to core/vm/access_events_test.go index 10630b3181b5..c702db3d9163 100644 --- a/core/state/access_events_test.go +++ b/core/vm/access_events_test.go @@ -14,12 +14,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package state +package vm_test import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/utils" ) @@ -37,7 +38,7 @@ func init() { } func TestAccountHeaderGas(t *testing.T) { - ae := NewAccessEvents(utils.NewPointCache(1024)) + ae := vm.NewAccessEvents(utils.NewPointCache(1024)) // Check cold read cost gas := ae.BasicDataGas(testAddr, false) @@ -92,7 +93,7 @@ func TestAccountHeaderGas(t *testing.T) { // TestContractCreateInitGas checks that the gas cost of contract creation is correctly // calculated. func TestContractCreateInitGas(t *testing.T) { - ae := NewAccessEvents(utils.NewPointCache(1024)) + ae := vm.NewAccessEvents(utils.NewPointCache(1024)) var testAddr [20]byte for i := byte(0); i < 20; i++ { @@ -115,7 +116,7 @@ func TestContractCreateInitGas(t *testing.T) { // TestMessageCallGas checks that the gas cost of message calls is correctly // calculated. func TestMessageCallGas(t *testing.T) { - ae := NewAccessEvents(utils.NewPointCache(1024)) + ae := vm.NewAccessEvents(utils.NewPointCache(1024)) // Check cold read cost, without a value gas := ae.MessageCallGas(testAddr) diff --git a/core/vm/analysis_eof.go b/core/vm/analysis_eof.go index eb78904cfd21..fe4e76021c3a 100644 --- a/core/vm/analysis_eof.go +++ b/core/vm/analysis_eof.go @@ -17,17 +17,17 @@ package vm // eofCodeBitmap collects data locations in code. -func eofCodeBitmap(code []byte) bitvec { +func eofCodeBitmap(code []byte) Bitvec { // The bitmap is 4 bytes longer than necessary, in case the code // ends with a PUSH32, the algorithm will push zeroes onto the // bitvector outside the bounds of the actual code. - bits := make(bitvec, len(code)/8+1+4) - return eofCodeBitmapInternal(code, bits) + bits := make(Bitvec, len(code)/8+1+4) + return EofCodeBitmapInternal(code, bits) } // eofCodeBitmapInternal is the internal implementation of codeBitmap for EOF // code validation. -func eofCodeBitmapInternal(code, bits bitvec) bitvec { +func EofCodeBitmapInternal(code, bits Bitvec) Bitvec { for pc := uint64(0); pc < uint64(len(code)); { var ( op = OpCode(code[pc]) diff --git a/core/vm/analysis_legacy.go b/core/vm/analysis_legacy.go index 38af9084aca1..7307b9a705fa 100644 --- a/core/vm/analysis_legacy.go +++ b/core/vm/analysis_legacy.go @@ -28,13 +28,13 @@ const ( // bitvec is a bit vector which maps bytes in a program. // An unset bit means the byte is an opcode, a set bit means // it's data (i.e. argument of PUSHxx). -type bitvec []byte +type Bitvec []byte -func (bits bitvec) set1(pos uint64) { +func (bits Bitvec) set1(pos uint64) { bits[pos/8] |= 1 << (pos % 8) } -func (bits bitvec) setN(flag uint16, pos uint64) { +func (bits Bitvec) setN(flag uint16, pos uint64) { a := flag << (pos % 8) bits[pos/8] |= byte(a) if b := byte(a >> 8); b != 0 { @@ -42,13 +42,13 @@ func (bits bitvec) setN(flag uint16, pos uint64) { } } -func (bits bitvec) set8(pos uint64) { +func (bits Bitvec) set8(pos uint64) { a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = ^a } -func (bits bitvec) set16(pos uint64) { +func (bits Bitvec) set16(pos uint64) { a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = 0xFF @@ -56,23 +56,23 @@ func (bits bitvec) set16(pos uint64) { } // codeSegment checks if the position is in a code segment. -func (bits *bitvec) codeSegment(pos uint64) bool { +func (bits *Bitvec) codeSegment(pos uint64) bool { return (((*bits)[pos/8] >> (pos % 8)) & 1) == 0 } // codeBitmap collects data locations in code. -func codeBitmap(code []byte) bitvec { +func CodeBitmap(code []byte) Bitvec { // The bitmap is 4 bytes longer than necessary, in case the code // ends with a PUSH32, the algorithm will set bits on the // bitvector outside the bounds of the actual code. - bits := make(bitvec, len(code)/8+1+4) - return codeBitmapInternal(code, bits) + bits := make(Bitvec, len(code)/8+1+4) + return CodeBitmapInternal(code, bits) } // codeBitmapInternal is the internal implementation of codeBitmap. // It exists for the purpose of being able to run benchmark tests // without dynamic allocations affecting the results. -func codeBitmapInternal(code, bits bitvec) bitvec { +func CodeBitmapInternal(code, bits Bitvec) Bitvec { for pc := uint64(0); pc < uint64(len(code)); { op := OpCode(code[pc]) pc++ diff --git a/core/vm/analysis_legacy_test.go b/core/vm/analysis_legacy_test.go index 7f5de225e280..61ba246b7355 100644 --- a/core/vm/analysis_legacy_test.go +++ b/core/vm/analysis_legacy_test.go @@ -14,12 +14,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "math/bits" "testing" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" ) @@ -29,30 +30,30 @@ func TestJumpDestAnalysis(t *testing.T) { exp byte which int }{ - {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0b0000_0010, 0}, - {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0b0000_1010, 0}, - {[]byte{0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1)}, 0b0101_0100, 0}, - {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, bits.Reverse8(0x7F), 0}, - {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 1}, - {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0b1100_0000, 0}, - {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, - {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0010_1110, 0}, - {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, - {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1100, 0}, - {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0011, 1}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1110, 0}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1111, 1}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 2}, - {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b1111_1110, 0}, - {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b0000_0101, 1}, - {[]byte{byte(PUSH32)}, 0b1111_1110, 0}, - {[]byte{byte(PUSH32)}, 0b1111_1111, 1}, - {[]byte{byte(PUSH32)}, 0b1111_1111, 2}, - {[]byte{byte(PUSH32)}, 0b1111_1111, 3}, - {[]byte{byte(PUSH32)}, 0b0000_0001, 4}, + {[]byte{byte(vm.PUSH1), 0x01, 0x01, 0x01}, 0b0000_0010, 0}, + {[]byte{byte(vm.PUSH1), byte(vm.PUSH1), byte(vm.PUSH1), byte(vm.PUSH1)}, 0b0000_1010, 0}, + {[]byte{0x00, byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, byte(vm.PUSH1)}, 0b0101_0100, 0}, + {[]byte{byte(vm.PUSH8), byte(vm.PUSH8), byte(vm.PUSH8), byte(vm.PUSH8), byte(vm.PUSH8), byte(vm.PUSH8), byte(vm.PUSH8), byte(vm.PUSH8), 0x01, 0x01, 0x01}, bits.Reverse8(0x7F), 0}, + {[]byte{byte(vm.PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 1}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(vm.PUSH2), byte(vm.PUSH2), byte(vm.PUSH2), 0x01, 0x01, 0x01}, 0b1100_0000, 0}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(vm.PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, + {[]byte{byte(vm.PUSH3), 0x01, 0x01, 0x01, byte(vm.PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0010_1110, 0}, + {[]byte{byte(vm.PUSH3), 0x01, 0x01, 0x01, byte(vm.PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, + {[]byte{0x01, byte(vm.PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1100, 0}, + {[]byte{0x01, byte(vm.PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0011, 1}, + {[]byte{byte(vm.PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1110, 0}, + {[]byte{byte(vm.PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1111, 1}, + {[]byte{byte(vm.PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 2}, + {[]byte{byte(vm.PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(vm.PUSH1), 0x01}, 0b1111_1110, 0}, + {[]byte{byte(vm.PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(vm.PUSH1), 0x01}, 0b0000_0101, 1}, + {[]byte{byte(vm.PUSH32)}, 0b1111_1110, 0}, + {[]byte{byte(vm.PUSH32)}, 0b1111_1111, 1}, + {[]byte{byte(vm.PUSH32)}, 0b1111_1111, 2}, + {[]byte{byte(vm.PUSH32)}, 0b1111_1111, 3}, + {[]byte{byte(vm.PUSH32)}, 0b0000_0001, 4}, } for i, test := range tests { - ret := codeBitmap(test.code) + ret := vm.CodeBitmap(test.code) if ret[test.which] != test.exp { t.Fatalf("test %d: expected %x, got %02x", i, test.exp, ret[test.which]) } @@ -67,7 +68,7 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { bench.SetBytes(analysisCodeSize) bench.ResetTimer() for i := 0; i < bench.N; i++ { - codeBitmap(code) + vm.CodeBitmap(code) } bench.StopTimer() } @@ -83,53 +84,53 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) { } func BenchmarkJumpdestOpAnalysis(bench *testing.B) { - var op OpCode + var op vm.OpCode bencher := func(b *testing.B) { code := make([]byte, analysisCodeSize) b.SetBytes(analysisCodeSize) for i := range code { code[i] = byte(op) } - bits := make(bitvec, len(code)/8+1+4) + bits := make(vm.Bitvec, len(code)/8+1+4) b.ResetTimer() for i := 0; i < b.N; i++ { clear(bits) - codeBitmapInternal(code, bits) + vm.CodeBitmapInternal(code, bits) } } - for op = PUSH1; op <= PUSH32; op++ { + for op = vm.PUSH1; op <= vm.PUSH32; op++ { bench.Run(op.String(), bencher) } - op = JUMPDEST + op = vm.JUMPDEST bench.Run(op.String(), bencher) - op = STOP + op = vm.STOP bench.Run(op.String(), bencher) } func BenchmarkJumpdestOpEOFAnalysis(bench *testing.B) { - var op OpCode + var op vm.OpCode bencher := func(b *testing.B) { code := make([]byte, analysisCodeSize) b.SetBytes(analysisCodeSize) for i := range code { code[i] = byte(op) } - bits := make(bitvec, len(code)/8+1+4) + bits := make(vm.Bitvec, len(code)/8+1+4) b.ResetTimer() for i := 0; i < b.N; i++ { clear(bits) - eofCodeBitmapInternal(code, bits) + vm.EofCodeBitmapInternal(code, bits) } } - for op = PUSH1; op <= PUSH32; op++ { + for op = vm.PUSH1; op <= vm.PUSH32; op++ { bench.Run(op.String(), bencher) } - op = JUMPDEST + op = vm.JUMPDEST bench.Run(op.String(), bencher) - op = STOP + op = vm.STOP bench.Run(op.String(), bencher) - op = RJUMPV + op = vm.RJUMPV bench.Run(op.String(), bencher) - op = EOFCREATE + op = vm.EOFCREATE bench.Run(op.String(), bencher) } diff --git a/core/vm/common.go b/core/vm/common.go index 658803b82010..b34f235ce48e 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -76,8 +76,8 @@ func getDataAndAdjustedBounds(data []byte, start uint64, size uint64) (codeCopyP return common.RightPadBytes(data[start:end], int(size)), start, end - start } -// toWordSize returns the ceiled word size required for memory expansion. -func toWordSize(size uint64) uint64 { +// ToWordSize returns the ceiled word size required for memory expansion. +func ToWordSize(size uint64) uint64 { if size > math.MaxUint64-31 { return math.MaxUint64/32 + 1 } diff --git a/core/vm/contract.go b/core/vm/contract.go index 5670244e1714..483f5d249892 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -49,8 +49,8 @@ type Contract struct { caller ContractRef self ContractRef - jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis. - analysis bitvec // Locally cached result of JUMPDEST analysis + jumpdests map[common.Hash]Bitvec // Aggregated result of JUMPDEST analysis. + analysis Bitvec // Locally cached result of JUMPDEST analysis Code []byte CodeHash common.Hash @@ -73,7 +73,7 @@ func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas // Reuse JUMPDEST analysis from parent context if available. c.jumpdests = parent.jumpdests } else { - c.jumpdests = make(map[common.Hash]bitvec) + c.jumpdests = make(map[common.Hash]Bitvec) } // Gas should be a pointer so it can safely be reduced through the run @@ -115,7 +115,7 @@ func (c *Contract) isCode(udest uint64) bool { if !exist { // Do the analysis and save in parent context // We do not need to store it in c.analysis - analysis = codeBitmap(c.Code) + analysis = CodeBitmap(c.Code) c.jumpdests[c.CodeHash] = analysis } // Also stash it in current contract for faster access @@ -127,7 +127,7 @@ func (c *Contract) isCode(udest uint64) bool { // we don't have to recalculate it for every JUMP instruction in the execution // However, we don't save it within the parent context if c.analysis == nil { - c.analysis = codeBitmap(c.Code) + c.analysis = CodeBitmap(c.Code) } return c.analysis.codeSegment(udest) } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 06849e65ade3..1f9b828d8952 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -43,8 +43,12 @@ import ( // requires a deterministic gas count based on the input size of the Run method of the // contract. type PrecompiledContract interface { - RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use - Run(input []byte) ([]byte, error) // Run runs the precompiled contract + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(evm *EVM, sender common.Address, callingContract common.Address, input []byte, value *big.Int, readOnly bool, isFromDelegateCall bool) ([]byte, error) // Run runs the precompiled contract +} + +type DynamicGasPrecompiledContract interface { + RunAndCalculateGas(evm *EVM, sender common.Address, callingContract common.Address, input []byte, suppliedGas uint64, value *big.Int, hooks *tracing.Hooks, readOnly bool, isFromDelegateCall bool) (ret []byte, remainingGas uint64, err error) // Run runs the precompiled contract and calculate gas dynamically } // PrecompiledContracts contains the precompiled contracts supported at the given fork. @@ -53,20 +57,20 @@ type PrecompiledContracts map[common.Address]PrecompiledContract // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum // contracts used in the Frontier and Homestead releases. var PrecompiledContractsHomestead = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, + common.BytesToAddress([]byte{0x1}): &Ecrecover{}, + common.BytesToAddress([]byte{0x2}): &Sha256hash{}, + common.BytesToAddress([]byte{0x3}): &Ripemd160hash{}, + common.BytesToAddress([]byte{0x4}): &DataCopy{}, } // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum // contracts used in the Byzantium release. var PrecompiledContractsByzantium = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false}, + common.BytesToAddress([]byte{0x1}): &Ecrecover{}, + common.BytesToAddress([]byte{0x2}): &Sha256hash{}, + common.BytesToAddress([]byte{0x3}): &Ripemd160hash{}, + common.BytesToAddress([]byte{0x4}): &DataCopy{}, + common.BytesToAddress([]byte{0x5}): &BigModExp{Eip2565: false}, common.BytesToAddress([]byte{0x6}): &bn256AddByzantium{}, common.BytesToAddress([]byte{0x7}): &bn256ScalarMulByzantium{}, common.BytesToAddress([]byte{0x8}): &bn256PairingByzantium{}, @@ -75,66 +79,66 @@ var PrecompiledContractsByzantium = PrecompiledContracts{ // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum // contracts used in the Istanbul release. var PrecompiledContractsIstanbul = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false}, - common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x9}): &blake2F{}, + common.BytesToAddress([]byte{0x1}): &Ecrecover{}, + common.BytesToAddress([]byte{0x2}): &Sha256hash{}, + common.BytesToAddress([]byte{0x3}): &Ripemd160hash{}, + common.BytesToAddress([]byte{0x4}): &DataCopy{}, + common.BytesToAddress([]byte{0x5}): &BigModExp{Eip2565: false}, + common.BytesToAddress([]byte{0x6}): &Bn256AddIstanbul{}, + common.BytesToAddress([]byte{0x7}): &Bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{0x8}): &Bn256PairingIstanbul{}, + common.BytesToAddress([]byte{0x9}): &Blake2F{}, } // PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum // contracts used in the Berlin release. var PrecompiledContractsBerlin = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x9}): &blake2F{}, + common.BytesToAddress([]byte{0x1}): &Ecrecover{}, + common.BytesToAddress([]byte{0x2}): &Sha256hash{}, + common.BytesToAddress([]byte{0x3}): &Ripemd160hash{}, + common.BytesToAddress([]byte{0x4}): &DataCopy{}, + common.BytesToAddress([]byte{0x5}): &BigModExp{Eip2565: true}, + common.BytesToAddress([]byte{0x6}): &Bn256AddIstanbul{}, + common.BytesToAddress([]byte{0x7}): &Bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{0x8}): &Bn256PairingIstanbul{}, + common.BytesToAddress([]byte{0x9}): &Blake2F{}, } // PrecompiledContractsCancun contains the default set of pre-compiled Ethereum // contracts used in the Cancun release. var PrecompiledContractsCancun = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x9}): &blake2F{}, - common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{}, + common.BytesToAddress([]byte{0x1}): &Ecrecover{}, + common.BytesToAddress([]byte{0x2}): &Sha256hash{}, + common.BytesToAddress([]byte{0x3}): &Ripemd160hash{}, + common.BytesToAddress([]byte{0x4}): &DataCopy{}, + common.BytesToAddress([]byte{0x5}): &BigModExp{Eip2565: true}, + common.BytesToAddress([]byte{0x6}): &Bn256AddIstanbul{}, + common.BytesToAddress([]byte{0x7}): &Bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{0x8}): &Bn256PairingIstanbul{}, + common.BytesToAddress([]byte{0x9}): &Blake2F{}, + common.BytesToAddress([]byte{0xa}): &KzgPointEvaluation{}, } // PrecompiledContractsPrague contains the set of pre-compiled Ethereum // contracts used in the Prague release. var PrecompiledContractsPrague = PrecompiledContracts{ - common.BytesToAddress([]byte{0x01}): &ecrecover{}, - common.BytesToAddress([]byte{0x02}): &sha256hash{}, - common.BytesToAddress([]byte{0x03}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x04}): &dataCopy{}, - common.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x09}): &blake2F{}, - common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, - common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{}, - common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExp{}, - common.BytesToAddress([]byte{0x0d}): &bls12381G2Add{}, - common.BytesToAddress([]byte{0x0e}): &bls12381G2MultiExp{}, - common.BytesToAddress([]byte{0x0f}): &bls12381Pairing{}, - common.BytesToAddress([]byte{0x10}): &bls12381MapG1{}, - common.BytesToAddress([]byte{0x11}): &bls12381MapG2{}, + common.BytesToAddress([]byte{0x01}): &Ecrecover{}, + common.BytesToAddress([]byte{0x02}): &Sha256hash{}, + common.BytesToAddress([]byte{0x03}): &Ripemd160hash{}, + common.BytesToAddress([]byte{0x04}): &DataCopy{}, + common.BytesToAddress([]byte{0x05}): &BigModExp{Eip2565: true}, + common.BytesToAddress([]byte{0x06}): &Bn256AddIstanbul{}, + common.BytesToAddress([]byte{0x07}): &Bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{0x08}): &Bn256PairingIstanbul{}, + common.BytesToAddress([]byte{0x09}): &Blake2F{}, + common.BytesToAddress([]byte{0x0a}): &KzgPointEvaluation{}, + common.BytesToAddress([]byte{0x0b}): &Bls12381G1Add{}, + common.BytesToAddress([]byte{0x0c}): &Bls12381G1MultiExp{}, + common.BytesToAddress([]byte{0x0d}): &Bls12381G2Add{}, + common.BytesToAddress([]byte{0x0e}): &Bls12381G2MultiExp{}, + common.BytesToAddress([]byte{0x0f}): &Bls12381Pairing{}, + common.BytesToAddress([]byte{0x10}): &Bls12381MapG1{}, + common.BytesToAddress([]byte{0x11}): &Bls12381MapG2{}, } var PrecompiledContractsBLS = PrecompiledContractsPrague @@ -218,7 +222,12 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p PrecompiledContract, evm *EVM, sender common.Address, callingContract common.Address, input []byte, suppliedGas uint64, value *big.Int, logger *tracing.Hooks, readOnly bool, isFromDelegateCall bool) (ret []byte, remainingGas uint64, err error) { + evm.depth++ + defer func() { evm.depth-- }() + if dp, ok := p.(DynamicGasPrecompiledContract); ok { + return dp.RunAndCalculateGas(evm, sender, callingContract, input, suppliedGas, value, logger, readOnly, isFromDelegateCall) + } gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas @@ -227,23 +236,23 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract) } suppliedGas -= gasCost - output, err := p.Run(input) + output, err := p.Run(evm, sender, callingContract, input, value, readOnly, isFromDelegateCall) return output, suppliedGas, err } -// ecrecover implemented as a native contract. -type ecrecover struct{} +// Ecrecover implemented as a native contract. +type Ecrecover struct{} -func (c *ecrecover) RequiredGas(input []byte) uint64 { +func (c *Ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(input []byte) ([]byte, error) { - const ecRecoverInputLength = 128 +func (c *Ecrecover) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { + const EcrecoverInputLength = 128 - input = common.RightPadBytes(input, ecRecoverInputLength) + input = common.RightPadBytes(input, EcrecoverInputLength) // "input" is (hash, v, r, s), each 32 bytes - // but for ecrecover we want (r, s, v) + // but for Ecrecover we want (r, s, v) r := new(big.Int).SetBytes(input[64:96]) s := new(big.Int).SetBytes(input[96:128]) @@ -270,53 +279,53 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) { } // SHA256 implemented as a native contract. -type sha256hash struct{} +type Sha256hash struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs // required for anything significant is so high it's impossible to pay for. -func (c *sha256hash) RequiredGas(input []byte) uint64 { +func (c *Sha256hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas } -func (c *sha256hash) Run(input []byte) ([]byte, error) { +func (c *Sha256hash) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { h := sha256.Sum256(input) return h[:], nil } // RIPEMD160 implemented as a native contract. -type ripemd160hash struct{} +type Ripemd160hash struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs // required for anything significant is so high it's impossible to pay for. -func (c *ripemd160hash) RequiredGas(input []byte) uint64 { +func (c *Ripemd160hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas } -func (c *ripemd160hash) Run(input []byte) ([]byte, error) { +func (c *Ripemd160hash) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { ripemd := ripemd160.New() ripemd.Write(input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil } // data copy implemented as a native contract. -type dataCopy struct{} +type DataCopy struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs // required for anything significant is so high it's impossible to pay for. -func (c *dataCopy) RequiredGas(input []byte) uint64 { +func (c *DataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(in []byte) ([]byte, error) { +func (c *DataCopy) Run(_ *EVM, _ common.Address, _ common.Address, in []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { return common.CopyBytes(in), nil } -// bigModExp implements a native big integer exponential modular operation. -type bigModExp struct { - eip2565 bool +// BigModExp implements a native big integer exponential modular operation. +type BigModExp struct { + Eip2565 bool } var ( @@ -362,7 +371,7 @@ func modexpMultComplexity(x *big.Int) *big.Int { } // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bigModExp) RequiredGas(input []byte) uint64 { +func (c *BigModExp) RequiredGas(input []byte) uint64 { var ( baseLen = new(big.Int).SetBytes(getData(input, 0, 32)) expLen = new(big.Int).SetBytes(getData(input, 32, 32)) @@ -402,7 +411,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { } else { gas.Set(modLen) } - if c.eip2565 { + if c.Eip2565 { // EIP-2565 has three changes // 1. Different multComplexity (inlined here) // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565): @@ -441,7 +450,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(input []byte) ([]byte, error) { +func (c *BigModExp) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { var ( baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() @@ -512,16 +521,16 @@ func runBn256Add(input []byte) ([]byte, error) { return res.Marshal(), nil } -// bn256AddIstanbul implements a native elliptic curve point addition conforming to +// Bn256AddIstanbul implements a native elliptic curve point addition conforming to // Istanbul consensus rules. -type bn256AddIstanbul struct{} +type Bn256AddIstanbul struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { +func (c *Bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { +func (c *Bn256AddIstanbul) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { return runBn256Add(input) } @@ -534,7 +543,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256AddByzantium) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { return runBn256Add(input) } @@ -550,16 +559,16 @@ func runBn256ScalarMul(input []byte) ([]byte, error) { return res.Marshal(), nil } -// bn256ScalarMulIstanbul implements a native elliptic curve scalar +// Bn256ScalarMulIstanbul implements a native elliptic curve scalar // multiplication conforming to Istanbul consensus rules. -type bn256ScalarMulIstanbul struct{} +type Bn256ScalarMulIstanbul struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { +func (c *Bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { +func (c *Bn256ScalarMulIstanbul) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { return runBn256ScalarMul(input) } @@ -572,7 +581,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulByzantium) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { return runBn256ScalarMul(input) } @@ -618,16 +627,16 @@ func runBn256Pairing(input []byte) ([]byte, error) { return false32Byte, nil } -// bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve +// Bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve // conforming to Istanbul consensus rules. -type bn256PairingIstanbul struct{} +type Bn256PairingIstanbul struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { +func (c *Bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { +func (c *Bn256PairingIstanbul) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { return runBn256Pairing(input) } @@ -640,25 +649,25 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256PairingByzantium) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { return runBn256Pairing(input) } -type blake2F struct{} +type Blake2F struct{} -func (c *blake2F) RequiredGas(input []byte) uint64 { +func (c *Blake2F) RequiredGas(input []byte) uint64 { // If the input is malformed, we can't calculate the gas, return 0 and let the // actual call choke and fault. - if len(input) != blake2FInputLength { + if len(input) != Blake2FInputLength { return 0 } return uint64(binary.BigEndian.Uint32(input[0:4])) } const ( - blake2FInputLength = 213 - blake2FFinalBlockBytes = byte(1) - blake2FNonFinalBlockBytes = byte(0) + Blake2FInputLength = 213 + Blake2FFinalBlockBytes = byte(1) + Blake2FNonFinalBlockBytes = byte(0) ) var ( @@ -666,18 +675,18 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(input []byte) ([]byte, error) { +func (c *Blake2F) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Make sure the input is valid (correct length and final flag) - if len(input) != blake2FInputLength { + if len(input) != Blake2FInputLength { return nil, errBlake2FInvalidInputLength } - if input[212] != blake2FNonFinalBlockBytes && input[212] != blake2FFinalBlockBytes { + if input[212] != Blake2FNonFinalBlockBytes && input[212] != Blake2FFinalBlockBytes { return nil, errBlake2FInvalidFinalFlag } // Parse the input into the Blake2b call parameters var ( rounds = binary.BigEndian.Uint32(input[0:4]) - final = input[212] == blake2FFinalBlockBytes + final = input[212] == Blake2FFinalBlockBytes h [8]uint64 m [16]uint64 @@ -707,20 +716,20 @@ func (c *blake2F) Run(input []byte) ([]byte, error) { var ( errBLS12381InvalidInputLength = errors.New("invalid input length") - errBLS12381InvalidFieldElementTopBytes = errors.New("invalid field element top bytes") + errBLS12381InvalidFieldElementTOpBytes = errors.New("invalid field element top bytes") errBLS12381G1PointSubgroup = errors.New("g1 point is not on correct subgroup") errBLS12381G2PointSubgroup = errors.New("g2 point is not on correct subgroup") ) -// bls12381G1Add implements EIP-2537 G1Add precompile. -type bls12381G1Add struct{} +// Bls12381G1Add implements EIP-2537 G1Add precompile. +type Bls12381G1Add struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { +func (c *Bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { +func (c *Bls12381G1Add) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Implements EIP-2537 G1Add precompile. // > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). // > Output is an encoding of addition operation result - single G1 point (`128` bytes). @@ -748,11 +757,11 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { return encodePointG1(p0), nil } -// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile. -type bls12381G1MultiExp struct{} +// Bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile. +type Bls12381G1MultiExp struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { +func (c *Bls12381G1MultiExp) RequiredGas(input []byte) uint64 { // Calculate G1 point, scalar value pair length k := len(input) / 160 if k == 0 { @@ -770,7 +779,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 } -func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) { +func (c *Bls12381G1MultiExp) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Implements EIP-2537 G1MultiExp precompile. // G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). @@ -808,15 +817,15 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) { return encodePointG1(r), nil } -// bls12381G2Add implements EIP-2537 G2Add precompile. -type bls12381G2Add struct{} +// Bls12381G2Add implements EIP-2537 G2Add precompile. +type Bls12381G2Add struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { +func (c *Bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { +func (c *Bls12381G2Add) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Implements EIP-2537 G2Add precompile. // > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). // > Output is an encoding of addition operation result - single G2 point (`256` bytes). @@ -845,11 +854,11 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { return encodePointG2(r), nil } -// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile. -type bls12381G2MultiExp struct{} +// Bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile. +type Bls12381G2MultiExp struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { +func (c *Bls12381G2MultiExp) RequiredGas(input []byte) uint64 { // Calculate G2 point, scalar value pair length k := len(input) / 288 if k == 0 { @@ -867,7 +876,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 } -func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) { +func (c *Bls12381G2MultiExp) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Implements EIP-2537 G2MultiExp precompile logic // > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). @@ -905,15 +914,15 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) { return encodePointG2(r), nil } -// bls12381Pairing implements EIP-2537 Pairing precompile. -type bls12381Pairing struct{} +// Bls12381Pairing implements EIP-2537 Pairing precompile. +type Bls12381Pairing struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { +func (c *Bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas } -func (c *bls12381Pairing) Run(input []byte) ([]byte, error) { +func (c *Bls12381Pairing) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Implements EIP-2537 Pairing precompile logic. // > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: // > - `128` bytes of G1 point encoding @@ -1028,7 +1037,7 @@ func decodeBLS12381FieldElement(in []byte) (fp.Element, error) { // check top bytes for i := 0; i < 16; i++ { if in[i] != byte(0x00) { - return fp.Element{}, errBLS12381InvalidFieldElementTopBytes + return fp.Element{}, errBLS12381InvalidFieldElementTOpBytes } } var res [48]byte @@ -1057,15 +1066,15 @@ func encodePointG2(p *bls12381.G2Affine) []byte { return out } -// bls12381MapG1 implements EIP-2537 MapG1 precompile. -type bls12381MapG1 struct{} +// Bls12381MapG1 implements EIP-2537 MapG1 precompile. +type Bls12381MapG1 struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { +func (c *Bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { +func (c *Bls12381MapG1) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Implements EIP-2537 Map_To_G1 precompile. // > Field-to-curve call expects an `64` bytes input that is interpreted as an element of the base field. // > Output of this call is `128` bytes and is G1 point following respective encoding rules. @@ -1086,15 +1095,15 @@ func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { return encodePointG1(&r), nil } -// bls12381MapG2 implements EIP-2537 MapG2 precompile. -type bls12381MapG2 struct{} +// Bls12381MapG2 implements EIP-2537 MapG2 precompile. +type Bls12381MapG2 struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. -func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { +func (c *Bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { +func (c *Bls12381MapG2) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { // Implements EIP-2537 Map_FP2_TO_G2 precompile logic. // > Field-to-curve call expects an `128` bytes input that is interpreted as an element of the quadratic extension field. // > Output of this call is `256` bytes and is G2 point following respective encoding rules. @@ -1119,11 +1128,11 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { return encodePointG2(&r), nil } -// kzgPointEvaluation implements the EIP-4844 point evaluation precompile. -type kzgPointEvaluation struct{} +// KzgPointEvaluation implements the EIP-4844 point evaluation precompile. +type KzgPointEvaluation struct{} // RequiredGas estimates the gas required for running the point evaluation precompile. -func (b *kzgPointEvaluation) RequiredGas(input []byte) uint64 { +func (b *KzgPointEvaluation) RequiredGas(input []byte) uint64 { return params.BlobTxPointEvaluationPrecompileGas } @@ -1140,7 +1149,7 @@ var ( ) // Run executes the point evaluation precompile. -func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) { +func (b *KzgPointEvaluation) Run(_ *EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { if len(input) != blobVerifyInputLength { return nil, errBlobVerifyInvalidInputLength } diff --git a/core/vm/contracts_fuzz_test.go b/core/vm/contracts_fuzz_test.go index 1e5cc8007471..f722d78cae7e 100644 --- a/core/vm/contracts_fuzz_test.go +++ b/core/vm/contracts_fuzz_test.go @@ -14,12 +14,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" ) func FuzzPrecompiledContracts(f *testing.F) { @@ -36,7 +37,7 @@ func FuzzPrecompiledContracts(f *testing.F) { return } inWant := string(input) - RunPrecompiledContract(p, input, gas, nil) + vm.RunPrecompiledContract(p, nil, common.Address{}, common.Address{}, input, gas, nil, nil, false, false) if inHave := string(input); inWant != inHave { t.Errorf("Precompiled %v modified input data", a) } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index b627f2ada506..70297e23bf41 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "bytes" @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" ) // precompiledTest defines the input/output pairs for precompiled contract tests. @@ -45,48 +46,48 @@ type precompiledFailureTest struct { // allPrecompiles does not map to the actual set of precompiles, as it also contains // repriced versions of precompiles at certain slots -var allPrecompiles = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, - common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, - common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, - - common.BytesToAddress([]byte{0x0f, 0x0a}): &bls12381G1Add{}, - common.BytesToAddress([]byte{0x0f, 0x0b}): &bls12381G1MultiExp{}, - common.BytesToAddress([]byte{0x0f, 0x0c}): &bls12381G2Add{}, - common.BytesToAddress([]byte{0x0f, 0x0d}): &bls12381G2MultiExp{}, - common.BytesToAddress([]byte{0x0f, 0x0e}): &bls12381Pairing{}, - common.BytesToAddress([]byte{0x0f, 0x0f}): &bls12381MapG1{}, - common.BytesToAddress([]byte{0x0f, 0x10}): &bls12381MapG2{}, +var allPrecompiles = map[common.Address]vm.PrecompiledContract{ + common.BytesToAddress([]byte{1}): &vm.Ecrecover{}, + common.BytesToAddress([]byte{2}): &vm.Sha256hash{}, + common.BytesToAddress([]byte{3}): &vm.Ripemd160hash{}, + common.BytesToAddress([]byte{4}): &vm.DataCopy{}, + common.BytesToAddress([]byte{5}): &vm.BigModExp{Eip2565: false}, + common.BytesToAddress([]byte{0xf5}): &vm.BigModExp{Eip2565: true}, + common.BytesToAddress([]byte{6}): &vm.Bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &vm.Bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &vm.Bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &vm.Blake2F{}, + common.BytesToAddress([]byte{0x0a}): &vm.KzgPointEvaluation{}, + + common.BytesToAddress([]byte{0x0f, 0x0a}): &vm.Bls12381G1Add{}, + common.BytesToAddress([]byte{0x0f, 0x0b}): &vm.Bls12381G1MultiExp{}, + common.BytesToAddress([]byte{0x0f, 0x0c}): &vm.Bls12381G2Add{}, + common.BytesToAddress([]byte{0x0f, 0x0d}): &vm.Bls12381G2MultiExp{}, + common.BytesToAddress([]byte{0x0f, 0x0e}): &vm.Bls12381Pairing{}, + common.BytesToAddress([]byte{0x0f, 0x0f}): &vm.Bls12381MapG1{}, + common.BytesToAddress([]byte{0x0f, 0x10}): &vm.Bls12381MapG2{}, } // EIP-152 test vectors var blake2FMalformedInputTests = []precompiledFailureTest{ { Input: "", - ExpectedError: errBlake2FInvalidInputLength.Error(), + ExpectedError: "invalid input length", Name: "vector 0: empty input", }, { Input: "00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - ExpectedError: errBlake2FInvalidInputLength.Error(), + ExpectedError: "invalid input length", Name: "vector 1: less than 213 bytes input", }, { Input: "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - ExpectedError: errBlake2FInvalidInputLength.Error(), + ExpectedError: "invalid input length", Name: "vector 2: more than 213 bytes input", }, { Input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002", - ExpectedError: errBlake2FInvalidFinalFlag.Error(), + ExpectedError: "invalid final flag", Name: "vector 3: malformed final block indicator flag", }, } @@ -96,7 +97,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil { + if res, _, err := vm.RunPrecompiledContract(p, nil, common.Address{}, common.Address{}, in, gas, nil, nil, false, false); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) @@ -118,7 +119,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { gas := p.RequiredGas(in) - 1 t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas, nil) + _, _, err := vm.RunPrecompiledContract(p, nil, common.Address{}, common.Address{}, in, gas, nil, nil, false, false) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -135,7 +136,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas, nil) + _, _, err := vm.RunPrecompiledContract(p, nil, common.Address{}, common.Address{}, in, gas, nil, nil, false, false) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -167,7 +168,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = RunPrecompiledContract(p, data, reqGas, nil) + res, _, err = vm.RunPrecompiledContract(p, nil, common.Address{}, common.Address{}, data, reqGas, nil, nil, false, false) } bench.StopTimer() elapsed := uint64(time.Since(start)) @@ -191,7 +192,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { }) } -// Benchmarks the sample inputs from the ECRECOVER precompile. +// Benchmarks the sample inputs from the vm.Ecrecover precompile. func BenchmarkPrecompiledEcrecover(bench *testing.B) { t := precompiledTest{ Input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", @@ -270,7 +271,7 @@ func TestPrecompileBlake2FMalformedInput(t *testing.T) { } } -func TestPrecompiledEcrecover(t *testing.T) { testJson("ecRecover", "01", t) } +func TestPrecompiledEcrecover(t *testing.T) { testJson("vm.Ecrecover", "01", t) } func testJson(name, addr string, t *testing.T) { tests, err := loadJson(name) diff --git a/core/vm/eips.go b/core/vm/eips.go index 515999bd19aa..265050b36351 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -90,7 +90,7 @@ func enable1884(jt *JumpTable) { func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - scope.Stack.push(balance) + scope.Stack.Push(balance) return nil, nil } @@ -109,7 +109,7 @@ func enable1344(jt *JumpTable) { // opChainID implements CHAINID opcode func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID) - scope.Stack.push(chainId) + scope.Stack.Push(chainId) return nil, nil } @@ -183,36 +183,36 @@ func enable3198(jt *JumpTable) { // - Adds TSTORE that writes to transient storage func enable1153(jt *JumpTable) { jt[TLOAD] = &operation{ - execute: opTload, + execute: OpTload, constantGas: params.WarmStorageReadCostEIP2929, minStack: minStack(1, 1), maxStack: maxStack(1, 1), } jt[TSTORE] = &operation{ - execute: opTstore, + 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() +// 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) { +// 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() + loc := scope.Stack.Pop() + val := scope.Stack.Pop() interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) return nil, nil } @@ -220,7 +220,7 @@ func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // opBaseFee implements BASEFEE opcode func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee) - scope.Stack.push(baseFee) + scope.Stack.Push(baseFee) return nil, nil } @@ -237,7 +237,7 @@ func enable3855(jt *JumpTable) { // opPush0 implements the PUSH0 opcode func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int)) + scope.Stack.Push(new(uint256.Int)) return nil, nil } @@ -252,21 +252,21 @@ func enable3860(jt *JumpTable) { // https://eips.ethereum.org/EIPS/eip-5656 func enable5656(jt *JumpTable) { jt[MCOPY] = &operation{ - execute: opMcopy, + execute: OpMcopy, constantGas: GasFastestStep, - dynamicGas: gasMcopy, + dynamicGas: GasMcopy, minStack: minStack(3, 0), maxStack: maxStack(3, 0), - memorySize: memoryMcopy, + memorySize: MemoryMcopy, } } -// opMcopy implements the MCOPY opcode (https://eips.ethereum.org/EIPS/eip-5656) -func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +// OpMcopy implements the MCOPY opcode (https://eips.ethereum.org/EIPS/eip-5656) +func OpMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - dst = scope.Stack.pop() - src = scope.Stack.pop() - length = scope.Stack.pop() + dst = scope.Stack.Pop() + src = scope.Stack.Pop() + length = scope.Stack.Pop() ) // These values are checked for overflow during memory expansion calculation // (the memorySize function on the opcode). @@ -274,9 +274,9 @@ func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by return nil, nil } -// opBlobHash implements the BLOBHASH opcode -func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - index := scope.Stack.peek() +// OpBlobHash implements the BLOBHASH opcode +func OpBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + index := scope.Stack.Peek() if index.LtUint64(uint64(len(interpreter.evm.TxContext.BlobHashes))) { blobHash := interpreter.evm.TxContext.BlobHashes[index.Uint64()] index.SetBytes32(blobHash[:]) @@ -289,14 +289,14 @@ func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ // opBlobBaseFee implements BLOBBASEFEE opcode func opBlobBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { blobBaseFee, _ := uint256.FromBig(interpreter.evm.Context.BlobBaseFee) - scope.Stack.push(blobBaseFee) + scope.Stack.Push(blobBaseFee) return nil, nil } // enable4844 applies EIP-4844 (BLOBHASH opcode) func enable4844(jt *JumpTable) { jt[BLOBHASH] = &operation{ - execute: opBlobHash, + execute: OpBlobHash, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), @@ -327,10 +327,10 @@ func enable6780(jt *JumpTable) { func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( stack = scope.Stack - a = stack.pop() - memOffset = stack.pop() - codeOffset = stack.pop() - length = stack.pop() + a = stack.Pop() + memOffset = stack.Pop() + codeOffset = stack.Pop() + length = stack.Pop() ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { @@ -365,7 +365,7 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext ) *pc += 1 if *pc < codeLen { - scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) + scope.Stack.Push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall && *pc%31 == 0 { // touch next chunk if PUSH1 is at the boundary. if so, *pc has @@ -378,19 +378,19 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } } } else { - scope.Stack.push(integer.Clear()) + scope.Stack.Push(integer.Clear()) } return nil, nil } -func makePushEIP4762(size uint64, pushByteSize int) executionFunc { +func makePushEIP4762(size uint64, pushByteSize int) ExecutionFunc { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( codeLen = len(scope.Contract.Code) start = min(codeLen, int(*pc+1)) end = min(codeLen, start+pushByteSize) ) - scope.Stack.push(new(uint256.Int).SetBytes( + scope.Stack.Push(new(uint256.Int).SetBytes( common.RightPadBytes( scope.Contract.Code[start:end], pushByteSize, diff --git a/core/vm/eof.go b/core/vm/eof.go index a5406283d5a4..be733c13b393 100644 --- a/core/vm/eof.go +++ b/core/vm/eof.go @@ -42,7 +42,7 @@ const ( maxInputItems = 127 maxOutputItems = 128 - maxStackHeight = 1023 + MaxStackHeight = 1023 maxContainerSections = 256 ) @@ -66,39 +66,39 @@ func isEOFVersion1(code []byte) bool { // Container is an EOF container object. type Container struct { - types []*functionMetadata - codeSections [][]byte - subContainers []*Container - subContainerCodes [][]byte - data []byte - dataSize int // might be more than len(data) + Types []*FunctionMetadata + CodeSections [][]byte + SubContainers []*Container + SubContainerCodes [][]byte + Data []byte + DataSize int // might be more than len(data) } -// functionMetadata is an EOF function signature. -type functionMetadata struct { - inputs uint8 - outputs uint8 - maxStackHeight uint16 +// FunctionMetadata is an EOF function signature. +type FunctionMetadata struct { + Inputs uint8 + Outputs uint8 + MaxStackHeight uint16 } -// stackDelta returns the #outputs - #inputs -func (meta *functionMetadata) stackDelta() int { - return int(meta.outputs) - int(meta.inputs) +// stackDelta returns the #Outputs - #Inputs +func (meta *FunctionMetadata) stackDelta() int { + return int(meta.Outputs) - int(meta.Inputs) } -// checkInputs checks the current minimum stack (stackMin) against the required inputs +// checkInputs checks the current minimum stack (stackMin) against the required Inputs // of the metadata, and returns an error if the stack is too shallow. -func (meta *functionMetadata) checkInputs(stackMin int) error { - if int(meta.inputs) > stackMin { - return ErrStackUnderflow{stackLen: stackMin, required: int(meta.inputs)} +func (meta *FunctionMetadata) checkInputs(stackMin int) error { + if int(meta.Inputs) > stackMin { + return ErrStackUnderflow{StackLen: stackMin, Required: int(meta.Inputs)} } return nil } // checkStackMax checks the if current maximum stack combined with the // function max stack will result in a stack overflow, and if so returns an error. -func (meta *functionMetadata) checkStackMax(stackMax int) error { - newMaxStack := stackMax + int(meta.maxStackHeight) - int(meta.inputs) +func (meta *FunctionMetadata) checkStackMax(stackMax int) error { + newMaxStack := stackMax + int(meta.MaxStackHeight) - int(meta.Inputs) if newMaxStack > int(params.StackLimit) { return ErrStackOverflow{stackLen: newMaxStack, limit: int(params.StackLimit)} } @@ -114,37 +114,37 @@ func (c *Container) MarshalBinary() []byte { // Write section headers. b = append(b, kindTypes) - b = binary.BigEndian.AppendUint16(b, uint16(len(c.types)*4)) + b = binary.BigEndian.AppendUint16(b, uint16(len(c.Types)*4)) b = append(b, kindCode) - b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSections))) - for _, codeSection := range c.codeSections { + b = binary.BigEndian.AppendUint16(b, uint16(len(c.CodeSections))) + for _, codeSection := range c.CodeSections { b = binary.BigEndian.AppendUint16(b, uint16(len(codeSection))) } var encodedContainer [][]byte - if len(c.subContainers) != 0 { + if len(c.SubContainers) != 0 { b = append(b, kindContainer) - b = binary.BigEndian.AppendUint16(b, uint16(len(c.subContainers))) - for _, section := range c.subContainers { + b = binary.BigEndian.AppendUint16(b, uint16(len(c.SubContainers))) + for _, section := range c.SubContainers { encoded := section.MarshalBinary() b = binary.BigEndian.AppendUint16(b, uint16(len(encoded))) encodedContainer = append(encodedContainer, encoded) } } b = append(b, kindData) - b = binary.BigEndian.AppendUint16(b, uint16(c.dataSize)) + b = binary.BigEndian.AppendUint16(b, uint16(c.DataSize)) b = append(b, 0) // terminator // Write section contents. - for _, ty := range c.types { - b = append(b, []byte{ty.inputs, ty.outputs, byte(ty.maxStackHeight >> 8), byte(ty.maxStackHeight & 0x00ff)}...) + for _, ty := range c.Types { + b = append(b, []byte{ty.Inputs, ty.Outputs, byte(ty.MaxStackHeight >> 8), byte(ty.MaxStackHeight & 0x00ff)}...) } - for _, code := range c.codeSections { + for _, code := range c.CodeSections { b = append(b, code...) } for _, section := range encodedContainer { b = append(b, section...) } - b = append(b, c.data...) + b = append(b, c.Data...) return b } @@ -231,7 +231,7 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) if kind != kindData { return fmt.Errorf("%w: found section %x instead", errMissingDataHeader, kind) } - c.dataSize = dataSize + c.DataSize = dataSize // Check for terminator. offsetTerminator := offset + 3 @@ -257,28 +257,28 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) // Parse types section. idx := offsetTerminator + 1 - var types = make([]*functionMetadata, 0, typesSize/4) + var types = make([]*FunctionMetadata, 0, typesSize/4) for i := 0; i < typesSize/4; i++ { - sig := &functionMetadata{ - inputs: b[idx+i*4], - outputs: b[idx+i*4+1], - maxStackHeight: binary.BigEndian.Uint16(b[idx+i*4+2:]), + sig := &FunctionMetadata{ + Inputs: b[idx+i*4], + Outputs: b[idx+i*4+1], + MaxStackHeight: binary.BigEndian.Uint16(b[idx+i*4+2:]), } - if sig.inputs > maxInputItems { - return fmt.Errorf("%w for section %d: have %d", errTooManyInputs, i, sig.inputs) + if sig.Inputs > maxInputItems { + return fmt.Errorf("%w for section %d: have %d", errTooManyInputs, i, sig.Inputs) } - if sig.outputs > maxOutputItems { - return fmt.Errorf("%w for section %d: have %d", errTooManyOutputs, i, sig.outputs) + if sig.Outputs > maxOutputItems { + return fmt.Errorf("%w for section %d: have %d", errTooManyOutputs, i, sig.Outputs) } - if sig.maxStackHeight > maxStackHeight { - return fmt.Errorf("%w for section %d: have %d", errTooLargeMaxStackHeight, i, sig.maxStackHeight) + if sig.MaxStackHeight > MaxStackHeight { + return fmt.Errorf("%w for section %d: have %d", errTooLargeMaxStackHeight, i, sig.MaxStackHeight) } types = append(types, sig) } - if types[0].inputs != 0 || types[0].outputs != 0x80 { - return fmt.Errorf("%w: have %d, %d", errInvalidSection0Type, types[0].inputs, types[0].outputs) + if types[0].Inputs != 0 || types[0].Outputs != 0x80 { + return fmt.Errorf("%w: have %d, %d", errInvalidSection0Type, types[0].Inputs, types[0].Outputs) } - c.types = types + c.Types = types // Parse code sections. idx += typesSize @@ -290,7 +290,7 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) codeSections[i] = b[idx : idx+size] idx += size } - c.codeSections = codeSections + c.CodeSections = codeSections // Parse the optional container sizes. if len(containerSizes) != 0 { if len(containerSizes) > maxContainerSections { @@ -315,8 +315,8 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) idx += size } - c.subContainers = subContainers - c.subContainerCodes = subContainerCodes + c.SubContainers = subContainers + c.SubContainerCodes = subContainerCodes } //Parse data section. @@ -327,7 +327,7 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) if topLevel && len(b) != idx+dataSize { return errTruncatedTopLevelContainer } - c.data = b[idx:end] + c.Data = b[idx:end] return nil } @@ -355,10 +355,10 @@ func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error { // should not mean 2 and 3 should be visited twice var ( index = toVisit[0] - code = c.codeSections[index] + code = c.CodeSections[index] ) if _, ok := visited[index]; !ok { - res, err := validateCode(code, index, c, jt, refBy == refByEOFCreate) + res, err := ValidateCode(code, index, c, jt, refBy == refByEOFCreate) if err != nil { return err } @@ -387,10 +387,10 @@ func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error { toVisit = toVisit[1:] } // Make sure every code section is visited at least once. - if len(visited) != len(c.codeSections) { - return errUnreachableCode + if len(visited) != len(c.CodeSections) { + return ErrUnreachableCode } - for idx, container := range c.subContainers { + for idx, container := range c.SubContainers { reference, ok := subContainerVisited[idx] if !ok { return errOrphanedSubcontainer @@ -469,33 +469,33 @@ func (c *Container) String() string { fmt.Sprintf(" - EOFMagic: %02x", eofMagic), fmt.Sprintf(" - EOFVersion: %02x", eof1Version), fmt.Sprintf(" - KindType: %02x", kindTypes), - fmt.Sprintf(" - TypesSize: %04x", len(c.types)*4), + fmt.Sprintf(" - TypesSize: %04x", len(c.Types)*4), fmt.Sprintf(" - KindCode: %02x", kindCode), fmt.Sprintf(" - KindData: %02x", kindData), - fmt.Sprintf(" - DataSize: %04x", len(c.data)), - fmt.Sprintf(" - Number of code sections: %d", len(c.codeSections)), + fmt.Sprintf(" - DataSize: %04x", len(c.Data)), + fmt.Sprintf(" - Number of code sections: %d", len(c.CodeSections)), } - for i, code := range c.codeSections { + for i, code := range c.CodeSections { output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, len(code))) } - output = append(output, fmt.Sprintf(" - Number of subcontainers: %d", len(c.subContainers))) - if len(c.subContainers) > 0 { - for i, section := range c.subContainers { + output = append(output, fmt.Sprintf(" - Number of subcontainers: %d", len(c.SubContainers))) + if len(c.SubContainers) > 0 { + for i, section := range c.SubContainers { output = append(output, fmt.Sprintf(" - subcontainer %d length: %04x\n", i, len(section.MarshalBinary()))) } } output = append(output, "Body") - for i, typ := range c.types { + for i, typ := range c.Types { output = append(output, fmt.Sprintf(" - Type %v: %x", i, - []byte{typ.inputs, typ.outputs, byte(typ.maxStackHeight >> 8), byte(typ.maxStackHeight & 0x00ff)})) + []byte{typ.Inputs, typ.Outputs, byte(typ.MaxStackHeight >> 8), byte(typ.MaxStackHeight & 0x00ff)})) } - for i, code := range c.codeSections { + for i, code := range c.CodeSections { output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, code)) } - for i, section := range c.subContainers { + for i, section := range c.SubContainers { output = append(output, fmt.Sprintf(" - Subcontainer %d: %x", i, section.MarshalBinary())) } - output = append(output, fmt.Sprintf(" - Data: %#x", c.data)) + output = append(output, fmt.Sprintf(" - Data: %#x", c.Data)) return strings.Join(output, "\n") } diff --git a/core/vm/eof_control_flow.go b/core/vm/eof_control_flow.go index c0a44599067d..9a9bfaaebb28 100644 --- a/core/vm/eof_control_flow.go +++ b/core/vm/eof_control_flow.go @@ -22,9 +22,9 @@ import ( "github.com/ethereum/go-ethereum/params" ) -func validateControlFlow(code []byte, section int, metadata []*functionMetadata, jt *JumpTable) (int, error) { +func validateControlFlow(code []byte, section int, metadata []*FunctionMetadata, jt *JumpTable) (int, error) { var ( - maxStackHeight = int(metadata[section].inputs) + maxStackHeight = int(metadata[section].Inputs) visitCount = 0 next = make([]int, 0, 1) ) @@ -54,14 +54,14 @@ func validateControlFlow(code []byte, section int, metadata []*functionMetadata, return true, int(stackBoundsMin[pos]), int(maxi - 1) } // set the initial stack bounds - setBounds(0, int(metadata[section].inputs), int(metadata[section].inputs)) + setBounds(0, int(metadata[section].Inputs), int(metadata[section].Inputs)) qualifiedExit := false for pos := 0; pos < len(code); pos++ { op := OpCode(code[pos]) ok, currentStackMin, currentStackMax := getStackMaxMin(pos) if !ok { - return 0, errUnreachableCode + return 0, ErrUnreachableCode } switch op { @@ -84,14 +84,14 @@ func validateControlFlow(code []byte, section int, metadata []*functionMetadata, In other words: RETF must unambiguously return all items remaining on the stack. */ if currentStackMax != currentStackMin { - return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", errInvalidOutputs, currentStackMax, currentStackMin, pos) + return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", ErrInvalidOutputs, currentStackMax, currentStackMin, pos) } - numOutputs := int(metadata[section].outputs) + numOutputs := int(metadata[section].Outputs) if numOutputs >= maxOutputItems { return 0, fmt.Errorf("%w: at pos %d", errInvalidNonReturningFlag, pos) } if numOutputs != currentStackMin { - return 0, fmt.Errorf("%w: have %d, want %d, at pos %d", errInvalidOutputs, numOutputs, currentStackMin, pos) + return 0, fmt.Errorf("%w: have %d, want %d, at pos %d", ErrInvalidOutputs, numOutputs, currentStackMin, pos) } qualifiedExit = true case JUMPF: @@ -102,40 +102,40 @@ func validateControlFlow(code []byte, section int, metadata []*functionMetadata, return 0, fmt.Errorf("%w: at pos %d", err, pos) } - if newSection.outputs == 0x80 { + if newSection.Outputs == 0x80 { if err := newSection.checkInputs(currentStackMin); err != nil { return 0, fmt.Errorf("%w: at pos %d", err, pos) } } else { if currentStackMax != currentStackMin { - return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", errInvalidOutputs, currentStackMax, currentStackMin, pos) + return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", ErrInvalidOutputs, currentStackMax, currentStackMin, pos) } - wantStack := int(metadata[section].outputs) - newSection.stackDelta() + wantStack := int(metadata[section].Outputs) - newSection.stackDelta() if currentStackMax != wantStack { - return 0, fmt.Errorf("%w: at pos %d", errInvalidOutputs, pos) + return 0, fmt.Errorf("%w: at pos %d", ErrInvalidOutputs, pos) } } - qualifiedExit = qualifiedExit || newSection.outputs < maxOutputItems + qualifiedExit = qualifiedExit || newSection.Outputs < maxOutputItems case DUPN: arg := int(code[pos+1]) + 1 if want, have := arg, currentStackMin; want > have { - return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos) + return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{StackLen: have, Required: want}, pos) } case SWAPN: arg := int(code[pos+1]) + 1 if want, have := arg+1, currentStackMin; want > have { - return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos) + return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{StackLen: have, Required: want}, pos) } case EXCHANGE: arg := int(code[pos+1]) n := arg>>4 + 1 m := arg&0x0f + 1 if want, have := n+m+1, currentStackMin; want > have { - return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos) + return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{StackLen: have, Required: want}, pos) } default: if want, have := jt[op].minStack, currentStackMin; want > have { - return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos) + return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{StackLen: have, Required: want}, pos) } } if !terminals[op] && op != CALLF { @@ -156,7 +156,7 @@ func validateControlFlow(code []byte, section int, metadata []*functionMetadata, return 0, errInvalidBackwardJump } if nextMax != currentStackMax || nextMin != currentStackMin { - return 0, errInvalidMaxStackHeight + return 0, ErrInvalidMaxStackHeight } } else { ok, nextMin, nextMax := getStackMaxMin(nextPos + 1) @@ -190,7 +190,7 @@ func validateControlFlow(code []byte, section int, metadata []*functionMetadata, for _, instr := range next { nextPC := instr + 1 if nextPC >= len(code) { - return 0, fmt.Errorf("%w: end with %s, pos %d", errInvalidCodeTermination, op, pos) + return 0, fmt.Errorf("%w: end with %s, pos %d", ErrInvalidCodeTermination, op, pos) } if nextPC > pos { // target reached via forward jump or seq flow @@ -222,14 +222,14 @@ func validateControlFlow(code []byte, section int, metadata []*functionMetadata, pos = next[0] } } - if qualifiedExit != (metadata[section].outputs < maxOutputItems) { + if qualifiedExit != (metadata[section].Outputs < maxOutputItems) { return 0, fmt.Errorf("%w no RETF or qualified JUMPF", errInvalidNonReturningFlag) } if maxStackHeight >= int(params.StackLimit) { return 0, ErrStackOverflow{maxStackHeight, int(params.StackLimit)} } - if maxStackHeight != int(metadata[section].maxStackHeight) { - return 0, fmt.Errorf("%w in code section %d: have %d, want %d", errInvalidMaxStackHeight, section, maxStackHeight, metadata[section].maxStackHeight) + if maxStackHeight != int(metadata[section].MaxStackHeight) { + return 0, fmt.Errorf("%w in code section %d: have %d, want %d", ErrInvalidMaxStackHeight, section, maxStackHeight, metadata[section].MaxStackHeight) } return visitCount, nil } diff --git a/core/vm/eof_test.go b/core/vm/eof_test.go index 0a9cf638ceba..a206c8ffec19 100644 --- a/core/vm/eof_test.go +++ b/core/vm/eof_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "encoding/hex" @@ -22,48 +22,49 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" ) func TestEOFMarshaling(t *testing.T) { for i, test := range []struct { - want Container + want vm.Container err error }{ { - want: Container{ - types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - codeSections: [][]byte{common.Hex2Bytes("604200")}, - data: []byte{0x01, 0x02, 0x03}, - dataSize: 3, + want: vm.Container{ + Types: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + CodeSections: [][]byte{common.Hex2Bytes("604200")}, + Data: []byte{0x01, 0x02, 0x03}, + DataSize: 3, }, }, { - want: Container{ - types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - codeSections: [][]byte{common.Hex2Bytes("604200")}, - data: []byte{0x01, 0x02, 0x03}, - dataSize: 3, + want: vm.Container{ + Types: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + CodeSections: [][]byte{common.Hex2Bytes("604200")}, + Data: []byte{0x01, 0x02, 0x03}, + DataSize: 3, }, }, { - want: Container{ - types: []*functionMetadata{ - {inputs: 0, outputs: 0x80, maxStackHeight: 1}, - {inputs: 2, outputs: 3, maxStackHeight: 4}, - {inputs: 1, outputs: 1, maxStackHeight: 1}, + want: vm.Container{ + Types: []*vm.FunctionMetadata{ + {Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}, + {Inputs: 2, Outputs: 3, MaxStackHeight: 4}, + {Inputs: 1, Outputs: 1, MaxStackHeight: 1}, }, - codeSections: [][]byte{ + CodeSections: [][]byte{ common.Hex2Bytes("604200"), common.Hex2Bytes("6042604200"), common.Hex2Bytes("00"), }, - data: []byte{}, + Data: []byte{}, }, }, } { var ( b = test.want.MarshalBinary() - got Container + got vm.Container ) t.Logf("b: %#x", b) if err := got.UnmarshalBinary(b, true); err != nil && err != test.err { @@ -76,20 +77,20 @@ func TestEOFMarshaling(t *testing.T) { } func TestEOFSubcontainer(t *testing.T) { - var subcontainer = new(Container) + var subcontainer = new(vm.Container) if err := subcontainer.UnmarshalBinary(common.Hex2Bytes("ef000101000402000100010400000000800000fe"), true); err != nil { t.Fatal(err) } - container := Container{ - types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - codeSections: [][]byte{common.Hex2Bytes("604200")}, - subContainers: []*Container{subcontainer}, - data: []byte{0x01, 0x02, 0x03}, - dataSize: 3, + container := vm.Container{ + Types: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + CodeSections: [][]byte{common.Hex2Bytes("604200")}, + SubContainers: []*vm.Container{subcontainer}, + Data: []byte{0x01, 0x02, 0x03}, + DataSize: 3, } var ( b = container.MarshalBinary() - got Container + got vm.Container ) if err := got.UnmarshalBinary(b, true); err != nil { t.Fatal(err) @@ -109,7 +110,7 @@ func TestMarshaling(t *testing.T) { if err != nil { t.Fatalf("test %d: error decoding: %v", i, err) } - var got Container + var got vm.Container if err := got.UnmarshalBinary(s, true); err != nil { t.Fatalf("test %d: got error %v", i, err) } diff --git a/core/vm/eof_validation.go b/core/vm/eof_validation.go index 514f9fb58cf9..070f23ad880f 100644 --- a/core/vm/eof_validation.go +++ b/core/vm/eof_validation.go @@ -27,22 +27,22 @@ import ( var ( errInvalidMagic = errors.New("invalid magic") errUndefinedInstruction = errors.New("undefined instruction") - errTruncatedImmediate = errors.New("truncated immediate") + ErrTruncatedImmediate = errors.New("truncated immediate") errInvalidSectionArgument = errors.New("invalid section argument") errInvalidCallArgument = errors.New("callf into non-returning section") errInvalidDataloadNArgument = errors.New("invalid dataloadN argument") - errInvalidJumpDest = errors.New("invalid jump destination") + ErrInvalidJumpDest = errors.New("invalid jump destination") errInvalidBackwardJump = errors.New("invalid backward jump") - errInvalidOutputs = errors.New("invalid number of outputs") - errInvalidMaxStackHeight = errors.New("invalid max stack height") - errInvalidCodeTermination = errors.New("invalid code termination") + ErrInvalidOutputs = errors.New("invalid number of outputs") + ErrInvalidMaxStackHeight = errors.New("invalid max stack height") + ErrInvalidCodeTermination = errors.New("invalid code termination") errEOFCreateWithTruncatedSection = errors.New("eofcreate with truncated section") errOrphanedSubcontainer = errors.New("subcontainer not referenced at all") errIncompatibleContainerKind = errors.New("incompatible container kind") - errStopAndReturnContract = errors.New("Stop/Return and Returncontract in the same code section") + errStOpAndReturnContract = errors.New("Stop/Return and Returncontract in the same code section") errStopInInitCode = errors.New("initcode contains a RETURN or STOP opcode") errTruncatedTopLevelContainer = errors.New("truncated top level container") - errUnreachableCode = errors.New("unreachable code") + ErrUnreachableCode = errors.New("unreachable code") errInvalidNonReturningFlag = errors.New("invalid non-returning flag, bad RETF") errInvalidVersion = errors.New("invalid version") errMissingTypeHeader = errors.New("missing type header") @@ -73,7 +73,7 @@ type validationResult struct { } // validateCode validates the code parameter against the EOF v1 validity requirements. -func validateCode(code []byte, section int, container *Container, jt *JumpTable, isInitCode bool) (*validationResult, error) { +func ValidateCode(code []byte, section int, container *Container, jt *JumpTable, isInitCode bool) (*validationResult, error) { var ( i = 0 // Tracks the number of actual instructions in the code (e.g. @@ -81,7 +81,7 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, // if each instruction is reachable. count = 0 op OpCode - analysis bitvec + analysis Bitvec visitedCode map[int]struct{} visitedSubcontainers map[int]int hasReturnContract bool @@ -101,7 +101,7 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, } size := int(immediates[op]) if size != 0 && len(code) <= i+size { - return nil, fmt.Errorf("%w: op %s, pos %d", errTruncatedImmediate, op, i) + return nil, fmt.Errorf("%w: op %s, pos %d", ErrTruncatedImmediate, op, i) } switch op { case RJUMP, RJUMPI: @@ -112,7 +112,7 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, maxSize := int(code[i+1]) length := maxSize + 1 if len(code) <= i+length { - return nil, fmt.Errorf("%w: jump table truncated, op %s, pos %d", errTruncatedImmediate, op, i) + return nil, fmt.Errorf("%w: jump table truncated, op %s, pos %d", ErrTruncatedImmediate, op, i) } offset := i + 2 for j := 0; j < length; j++ { @@ -123,10 +123,10 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, i += 2 * maxSize case CALLF: arg, _ := parseUint16(code[i+1:]) - if arg >= len(container.types) { - return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.types), i) + if arg >= len(container.Types) { + return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.Types), i) } - if container.types[arg].outputs == 0x80 { + if container.Types[arg].Outputs == 0x80 { return nil, fmt.Errorf("%w: section %v", errInvalidCallArgument, arg) } if visitedCode == nil { @@ -135,11 +135,11 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, visitedCode[arg] = struct{}{} case JUMPF: arg, _ := parseUint16(code[i+1:]) - if arg >= len(container.types) { - return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.types), i) + if arg >= len(container.Types) { + return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.Types), i) } - if container.types[arg].outputs != 0x80 && container.types[arg].outputs > container.types[section].outputs { - return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidOutputs, arg, len(container.types), i) + if container.Types[arg].Outputs != 0x80 && container.Types[arg].Outputs > container.Types[section].Outputs { + return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", ErrInvalidOutputs, arg, len(container.Types), i) } if visitedCode == nil { visitedCode = make(map[int]struct{}) @@ -148,16 +148,16 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, case DATALOADN: arg, _ := parseUint16(code[i+1:]) // TODO why are we checking this? We should just pad - if arg+32 > len(container.data) { - return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, len(container.data), i) + if arg+32 > len(container.Data) { + return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, len(container.Data), i) } case RETURNCONTRACT: if !isInitCode { return nil, errIncompatibleContainerKind } arg := int(code[i+1]) - if arg >= len(container.subContainers) { - return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i) + if arg >= len(container.SubContainers) { + return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", ErrUnreachableCode, arg, len(container.SubContainers), i) } if visitedSubcontainers == nil { visitedSubcontainers = make(map[int]int) @@ -167,17 +167,17 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, return nil, fmt.Errorf("section already referenced, arg :%d", arg) } if hasStop { - return nil, errStopAndReturnContract + return nil, errStOpAndReturnContract } hasReturnContract = true visitedSubcontainers[arg] = refByReturnContract case EOFCREATE: arg := int(code[i+1]) - if arg >= len(container.subContainers) { - return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i) + if arg >= len(container.SubContainers) { + return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", ErrUnreachableCode, arg, len(container.SubContainers), i) } - if ct := container.subContainers[arg]; len(ct.data) != ct.dataSize { - return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, len(ct.data), ct.dataSize, i) + if ct := container.SubContainers[arg]; len(ct.Data) != ct.DataSize { + return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, len(ct.Data), ct.DataSize, i) } if visitedSubcontainers == nil { visitedSubcontainers = make(map[int]int) @@ -192,7 +192,7 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, return nil, errStopInInitCode } if hasReturnContract { - return nil, errStopAndReturnContract + return nil, errStOpAndReturnContract } hasStop = true } @@ -201,13 +201,13 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, // Code sections may not "fall through" and require proper termination. // Therefore, the last instruction must be considered terminal or RJUMP. if !terminals[op] && op != RJUMP { - return nil, fmt.Errorf("%w: end with %s, pos %d", errInvalidCodeTermination, op, i) + return nil, fmt.Errorf("%w: end with %s, pos %d", ErrInvalidCodeTermination, op, i) } - if paths, err := validateControlFlow(code, section, container.types, jt); err != nil { + if paths, err := validateControlFlow(code, section, container.Types, jt); err != nil { return nil, err } else if paths != count { // TODO(matt): return actual position of unreachable code - return nil, errUnreachableCode + return nil, ErrUnreachableCode } return &validationResult{ visitedCode: visitedCode, @@ -218,7 +218,7 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, } // checkDest parses a relative offset at code[0:2] and checks if it is a valid jump destination. -func checkDest(code []byte, analysis *bitvec, imm, from, length int) error { +func checkDest(code []byte, analysis *Bitvec, imm, from, length int) error { if len(code) < imm+2 { return io.ErrUnexpectedEOF } @@ -228,10 +228,10 @@ func checkDest(code []byte, analysis *bitvec, imm, from, length int) error { offset := parseInt16(code[imm:]) dest := from + offset if dest < 0 || dest >= length { - return fmt.Errorf("%w: out-of-bounds offset: offset %d, dest %d, pos %d", errInvalidJumpDest, offset, dest, imm) + return fmt.Errorf("%w: out-of-bounds offset: offset %d, dest %d, pos %d", ErrInvalidJumpDest, offset, dest, imm) } if !analysis.codeSegment(uint64(dest)) { - return fmt.Errorf("%w: offset into immediate: offset %d, dest %d, pos %d", errInvalidJumpDest, offset, dest, imm) + return fmt.Errorf("%w: offset into immediate: offset %d, dest %d, pos %d", ErrInvalidJumpDest, offset, dest, imm) } return nil } diff --git a/core/vm/eof_validation_test.go b/core/vm/eof_validation_test.go index f7b0e78f2695..a1a84d529473 100644 --- a/core/vm/eof_validation_test.go +++ b/core/vm/eof_validation_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "encoding/binary" @@ -22,6 +22,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" ) @@ -29,229 +30,229 @@ func TestValidateCode(t *testing.T) { for i, test := range []struct { code []byte section int - metadata []*functionMetadata + metadata []*vm.FunctionMetadata err error }{ { code: []byte{ - byte(CALLER), - byte(POP), - byte(STOP), + byte(vm.CALLER), + byte(vm.POP), + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, }, { code: []byte{ - byte(CALLF), 0x00, 0x00, - byte(RETF), + byte(vm.CALLF), 0x00, 0x00, + byte(vm.RETF), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 0}}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0, MaxStackHeight: 0}}, }, { code: []byte{ - byte(ADDRESS), - byte(CALLF), 0x00, 0x00, - byte(POP), - byte(RETF), + byte(vm.ADDRESS), + byte(vm.CALLF), 0x00, 0x00, + byte(vm.POP), + byte(vm.RETF), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 1}}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0, MaxStackHeight: 1}}, }, { code: []byte{ - byte(CALLER), - byte(POP), + byte(vm.CALLER), + byte(vm.POP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - err: errInvalidCodeTermination, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + err: vm.ErrInvalidCodeTermination, }, { code: []byte{ - byte(RJUMP), + byte(vm.RJUMP), byte(0x00), byte(0x01), - byte(CALLER), - byte(STOP), + byte(vm.CALLER), + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 0}}, - err: errUnreachableCode, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 0}}, + err: vm.ErrUnreachableCode, }, { code: []byte{ - byte(PUSH1), + byte(vm.PUSH1), byte(0x42), - byte(ADD), - byte(STOP), + byte(vm.ADD), + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - err: ErrStackUnderflow{stackLen: 1, required: 2}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + err: vm.ErrStackUnderflow{StackLen: 1, Required: 2}, }, { code: []byte{ - byte(PUSH1), + byte(vm.PUSH1), byte(0x42), - byte(POP), - byte(STOP), + byte(vm.POP), + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 2}}, - err: errInvalidMaxStackHeight, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 2}}, + err: vm.ErrInvalidMaxStackHeight, }, { code: []byte{ - byte(PUSH0), - byte(RJUMPI), + byte(vm.PUSH0), + byte(vm.RJUMPI), byte(0x00), byte(0x01), - byte(PUSH1), + byte(vm.PUSH1), byte(0x42), // jumps to here - byte(POP), - byte(STOP), + byte(vm.POP), + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - err: errInvalidJumpDest, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + err: vm.ErrInvalidJumpDest, }, { code: []byte{ - byte(PUSH0), - byte(RJUMPV), + byte(vm.PUSH0), + byte(vm.RJUMPV), byte(0x01), byte(0x00), byte(0x01), byte(0x00), byte(0x02), - byte(PUSH1), - byte(0x42), // jumps to here - byte(POP), // and here - byte(STOP), + byte(vm.PUSH1), + byte(0x42), // jumps to here + byte(vm.POP), // and here + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - err: errInvalidJumpDest, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + err: vm.ErrInvalidJumpDest, }, { code: []byte{ - byte(PUSH0), - byte(RJUMPV), + byte(vm.PUSH0), + byte(vm.RJUMPV), byte(0x00), - byte(STOP), + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - err: errTruncatedImmediate, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + err: vm.ErrTruncatedImmediate, }, { code: []byte{ - byte(RJUMP), 0x00, 0x03, - byte(JUMPDEST), // this code is unreachable to forward jumps alone - byte(JUMPDEST), - byte(RETURN), - byte(PUSH1), 20, - byte(PUSH1), 39, - byte(PUSH1), 0x00, - byte(DATACOPY), - byte(PUSH1), 20, - byte(PUSH1), 0x00, - byte(RJUMP), 0xff, 0xef, + byte(vm.RJUMP), 0x00, 0x03, + byte(vm.JUMPDEST), // this code is unreachable to forward jumps alone + byte(vm.JUMPDEST), + byte(vm.RETURN), + byte(vm.PUSH1), 20, + byte(vm.PUSH1), 39, + byte(vm.PUSH1), 0x00, + byte(vm.DATACOPY), + byte(vm.PUSH1), 20, + byte(vm.PUSH1), 0x00, + byte(vm.RJUMP), 0xff, 0xef, }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}}, - err: errUnreachableCode, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 3}}, + err: vm.ErrUnreachableCode, }, { code: []byte{ - byte(PUSH1), 1, - byte(RJUMPI), 0x00, 0x03, - byte(JUMPDEST), - byte(JUMPDEST), - byte(STOP), - byte(PUSH1), 20, - byte(PUSH1), 39, - byte(PUSH1), 0x00, - byte(DATACOPY), - byte(PUSH1), 20, - byte(PUSH1), 0x00, - byte(RETURN), + byte(vm.PUSH1), 1, + byte(vm.RJUMPI), 0x00, 0x03, + byte(vm.JUMPDEST), + byte(vm.JUMPDEST), + byte(vm.STOP), + byte(vm.PUSH1), 20, + byte(vm.PUSH1), 39, + byte(vm.PUSH1), 0x00, + byte(vm.DATACOPY), + byte(vm.PUSH1), 20, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 3}}, }, { code: []byte{ - byte(PUSH1), 1, - byte(RJUMPV), 0x01, 0x00, 0x03, 0xff, 0xf8, - byte(JUMPDEST), - byte(JUMPDEST), - byte(STOP), - byte(PUSH1), 20, - byte(PUSH1), 39, - byte(PUSH1), 0x00, - byte(DATACOPY), - byte(PUSH1), 20, - byte(PUSH1), 0x00, - byte(RETURN), + byte(vm.PUSH1), 1, + byte(vm.RJUMPV), 0x01, 0x00, 0x03, 0xff, 0xf8, + byte(vm.JUMPDEST), + byte(vm.JUMPDEST), + byte(vm.STOP), + byte(vm.PUSH1), 20, + byte(vm.PUSH1), 39, + byte(vm.PUSH1), 0x00, + byte(vm.DATACOPY), + byte(vm.PUSH1), 20, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 3}}, }, { code: []byte{ - byte(STOP), - byte(STOP), - byte(INVALID), + byte(vm.STOP), + byte(vm.STOP), + byte(vm.INVALID), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 0}}, - err: errUnreachableCode, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 0}}, + err: vm.ErrUnreachableCode, }, { code: []byte{ - byte(RETF), + byte(vm.RETF), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 1, maxStackHeight: 0}}, - err: errInvalidOutputs, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 1, MaxStackHeight: 0}}, + err: vm.ErrInvalidOutputs, }, { code: []byte{ - byte(RETF), + byte(vm.RETF), }, section: 0, - metadata: []*functionMetadata{{inputs: 3, outputs: 3, maxStackHeight: 3}}, + metadata: []*vm.FunctionMetadata{{Inputs: 3, Outputs: 3, MaxStackHeight: 3}}, }, { code: []byte{ - byte(CALLF), 0x00, 0x01, - byte(POP), - byte(STOP), + byte(vm.CALLF), 0x00, 0x01, + byte(vm.POP), + byte(vm.STOP), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}, {inputs: 0, outputs: 1, maxStackHeight: 0}}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}, {Inputs: 0, Outputs: 1, MaxStackHeight: 0}}, }, { code: []byte{ - byte(ORIGIN), - byte(ORIGIN), - byte(CALLF), 0x00, 0x01, - byte(POP), - byte(RETF), + byte(vm.ORIGIN), + byte(vm.ORIGIN), + byte(vm.CALLF), 0x00, 0x01, + byte(vm.POP), + byte(vm.RETF), }, section: 0, - metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 2}, {inputs: 2, outputs: 1, maxStackHeight: 2}}, + metadata: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0, MaxStackHeight: 2}, {Inputs: 2, Outputs: 1, MaxStackHeight: 2}}, }, } { - container := &Container{ - types: test.metadata, - data: make([]byte, 0), - subContainers: make([]*Container, 0), + container := &vm.Container{ + Types: test.metadata, + Data: make([]byte, 0), + SubContainers: make([]*vm.Container, 0), } - _, err := validateCode(test.code, test.section, container, &eofInstructionSet, false) + _, err := vm.ValidateCode(test.code, test.section, container, &vm.EofInstructionSet, false) if !errors.Is(err, test.err) { t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err) } @@ -262,22 +263,22 @@ func TestValidateCode(t *testing.T) { // For this we do a bunch of RJUMPIs that jump backwards (in a potential infinite loop). func BenchmarkRJUMPI(b *testing.B) { snippet := []byte{ - byte(PUSH0), - byte(RJUMPI), 0xFF, 0xFC, + byte(vm.PUSH0), + byte(vm.RJUMPI), 0xFF, 0xFC, } code := []byte{} for i := 0; i < params.MaxCodeSize/len(snippet)-1; i++ { code = append(code, snippet...) } - code = append(code, byte(STOP)) - container := &Container{ - types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - data: make([]byte, 0), - subContainers: make([]*Container, 0), + code = append(code, byte(vm.STOP)) + container := &vm.Container{ + Types: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + Data: make([]byte, 0), + SubContainers: make([]*vm.Container, 0), } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := validateCode(code, 0, container, &eofInstructionSet, false) + _, err := vm.ValidateCode(code, 0, container, &vm.EofInstructionSet, false) if err != nil { b.Fatal(err) } @@ -288,8 +289,8 @@ func BenchmarkRJUMPI(b *testing.B) { // for this we set up as many RJUMPV opcodes with a full jumptable (containing 0s) as possible. func BenchmarkRJUMPV(b *testing.B) { snippet := []byte{ - byte(PUSH0), - byte(RJUMPV), + byte(vm.PUSH0), + byte(vm.RJUMPV), 0xff, // count 0x00, 0x00, } @@ -300,16 +301,16 @@ func BenchmarkRJUMPV(b *testing.B) { for i := 0; i < 24576/len(snippet)-1; i++ { code = append(code, snippet...) } - code = append(code, byte(PUSH0)) - code = append(code, byte(STOP)) - container := &Container{ - types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - data: make([]byte, 0), - subContainers: make([]*Container, 0), + code = append(code, byte(vm.PUSH0)) + code = append(code, byte(vm.STOP)) + container := &vm.Container{ + Types: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + Data: make([]byte, 0), + SubContainers: make([]*vm.Container, 0), } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := validateCode(code, 0, container, &pragueInstructionSet, false) + _, err := vm.ValidateCode(code, 0, container, &vm.PragueInstructionSet, false) if err != nil { b.Fatal(err) } @@ -322,28 +323,28 @@ func BenchmarkRJUMPV(b *testing.B) { // - or code to again call into 1024 code sections. // We can't have all code sections calling each other, otherwise we would exceed 48KB. func BenchmarkEOFValidation(b *testing.B) { - var container Container + var container vm.Container var code []byte maxSections := 1024 for i := 0; i < maxSections; i++ { - code = append(code, byte(CALLF)) + code = append(code, byte(vm.CALLF)) code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1) } // First container - container.codeSections = append(container.codeSections, append(code, byte(STOP))) - container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0}) + container.CodeSections = append(container.CodeSections, append(code, byte(vm.STOP))) + container.Types = append(container.Types, &vm.FunctionMetadata{Inputs: 0, Outputs: 0x80, MaxStackHeight: 0}) inner := []byte{ - byte(RETF), + byte(vm.RETF), } for i := 0; i < 1023; i++ { - container.codeSections = append(container.codeSections, inner) - container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0}) + container.CodeSections = append(container.CodeSections, inner) + container.Types = append(container.Types, &vm.FunctionMetadata{Inputs: 0, Outputs: 0, MaxStackHeight: 0}) } for i := 0; i < 12; i++ { - container.codeSections[i+1] = append(code, byte(RETF)) + container.CodeSections[i+1] = append(code, byte(vm.RETF)) } bin := container.MarshalBinary() @@ -351,13 +352,13 @@ func BenchmarkEOFValidation(b *testing.B) { b.Fatal("Exceeds 48Kb") } - var container2 Container + var container2 vm.Container b.ResetTimer() for i := 0; i < b.N; i++ { if err := container2.UnmarshalBinary(bin, true); err != nil { b.Fatal(err) } - if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil { + if err := container2.ValidateCode(&vm.PragueInstructionSet, false); err != nil { b.Fatal(err) } } @@ -368,37 +369,37 @@ func BenchmarkEOFValidation(b *testing.B) { // - contain calls to some other code sections. // We can't have all code sections calling each other, otherwise we would exceed 48KB. func BenchmarkEOFValidation2(b *testing.B) { - var container Container + var container vm.Container var code []byte maxSections := 1024 for i := 0; i < maxSections; i++ { - code = append(code, byte(CALLF)) + code = append(code, byte(vm.CALLF)) code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1) } - code = append(code, byte(STOP)) + code = append(code, byte(vm.STOP)) // First container - container.codeSections = append(container.codeSections, code) - container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0}) + container.CodeSections = append(container.CodeSections, code) + container.Types = append(container.Types, &vm.FunctionMetadata{Inputs: 0, Outputs: 0x80, MaxStackHeight: 0}) inner := []byte{ - byte(CALLF), 0x03, 0xE8, - byte(CALLF), 0x03, 0xE9, - byte(CALLF), 0x03, 0xF0, - byte(CALLF), 0x03, 0xF1, - byte(CALLF), 0x03, 0xF2, - byte(CALLF), 0x03, 0xF3, - byte(CALLF), 0x03, 0xF4, - byte(CALLF), 0x03, 0xF5, - byte(CALLF), 0x03, 0xF6, - byte(CALLF), 0x03, 0xF7, - byte(CALLF), 0x03, 0xF8, - byte(CALLF), 0x03, 0xF, - byte(RETF), + byte(vm.CALLF), 0x03, 0xE8, + byte(vm.CALLF), 0x03, 0xE9, + byte(vm.CALLF), 0x03, 0xF0, + byte(vm.CALLF), 0x03, 0xF1, + byte(vm.CALLF), 0x03, 0xF2, + byte(vm.CALLF), 0x03, 0xF3, + byte(vm.CALLF), 0x03, 0xF4, + byte(vm.CALLF), 0x03, 0xF5, + byte(vm.CALLF), 0x03, 0xF6, + byte(vm.CALLF), 0x03, 0xF7, + byte(vm.CALLF), 0x03, 0xF8, + byte(vm.CALLF), 0x03, 0xF, + byte(vm.RETF), } for i := 0; i < 1023; i++ { - container.codeSections = append(container.codeSections, inner) - container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0}) + container.CodeSections = append(container.CodeSections, inner) + container.Types = append(container.Types, &vm.FunctionMetadata{Inputs: 0, Outputs: 0, MaxStackHeight: 0}) } bin := container.MarshalBinary() @@ -406,13 +407,13 @@ func BenchmarkEOFValidation2(b *testing.B) { b.Fatal("Exceeds 48Kb") } - var container2 Container + var container2 vm.Container b.ResetTimer() for i := 0; i < b.N; i++ { if err := container2.UnmarshalBinary(bin, true); err != nil { b.Fatal(err) } - if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil { + if err := container2.ValidateCode(&vm.PragueInstructionSet, false); err != nil { b.Fatal(err) } } @@ -424,11 +425,11 @@ func BenchmarkEOFValidation2(b *testing.B) { // - contain calls to other code sections // We can't have all code sections calling each other, otherwise we would exceed 48KB. func BenchmarkEOFValidation3(b *testing.B) { - var container Container + var container vm.Container var code []byte snippet := []byte{ - byte(PUSH0), - byte(RJUMPV), + byte(vm.PUSH0), + byte(vm.RJUMPV), 0xff, // count 0x00, 0x00, } @@ -439,22 +440,22 @@ func BenchmarkEOFValidation3(b *testing.B) { // First container, calls into all other containers maxSections := 1024 for i := 0; i < maxSections; i++ { - code = append(code, byte(CALLF)) + code = append(code, byte(vm.CALLF)) code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1) } - code = append(code, byte(STOP)) - container.codeSections = append(container.codeSections, code) - container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 1}) + code = append(code, byte(vm.STOP)) + container.CodeSections = append(container.CodeSections, code) + container.Types = append(container.Types, &vm.FunctionMetadata{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}) // Other containers for i := 0; i < 1023; i++ { - container.codeSections = append(container.codeSections, []byte{byte(RJUMP), 0x00, 0x00, byte(RETF)}) - container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0}) + container.CodeSections = append(container.CodeSections, []byte{byte(vm.RJUMP), 0x00, 0x00, byte(vm.RETF)}) + container.Types = append(container.Types, &vm.FunctionMetadata{Inputs: 0, Outputs: 0, MaxStackHeight: 0}) } // Other containers for i := 0; i < 68; i++ { - container.codeSections[i+1] = append(snippet, byte(RETF)) - container.types[i+1] = &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 1} + container.CodeSections[i+1] = append(snippet, byte(vm.RETF)) + container.Types[i+1] = &vm.FunctionMetadata{Inputs: 0, Outputs: 0, MaxStackHeight: 1} } bin := container.MarshalBinary() if len(bin) > 48*1024 { @@ -464,11 +465,11 @@ func BenchmarkEOFValidation3(b *testing.B) { b.ReportMetric(float64(len(bin)), "bytes") for i := 0; i < b.N; i++ { for k := 0; k < 40; k++ { - var container2 Container + var container2 vm.Container if err := container2.UnmarshalBinary(bin, true); err != nil { b.Fatal(err) } - if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil { + if err := container2.ValidateCode(&vm.PragueInstructionSet, false); err != nil { b.Fatal(err) } } @@ -477,24 +478,24 @@ func BenchmarkEOFValidation3(b *testing.B) { func BenchmarkRJUMPI_2(b *testing.B) { code := []byte{ - byte(PUSH0), - byte(RJUMPI), 0xFF, 0xFC, + byte(vm.PUSH0), + byte(vm.RJUMPI), 0xFF, 0xFC, } for i := 0; i < params.MaxCodeSize/4-1; i++ { - code = append(code, byte(PUSH0)) + code = append(code, byte(vm.PUSH0)) x := -4 * i - code = append(code, byte(RJUMPI)) + code = append(code, byte(vm.RJUMPI)) code = binary.BigEndian.AppendUint16(code, uint16(x)) } - code = append(code, byte(STOP)) - container := &Container{ - types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - data: make([]byte, 0), - subContainers: make([]*Container, 0), + code = append(code, byte(vm.STOP)) + container := &vm.Container{ + Types: []*vm.FunctionMetadata{{Inputs: 0, Outputs: 0x80, MaxStackHeight: 1}}, + Data: make([]byte, 0), + SubContainers: make([]*vm.Container, 0), } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := validateCode(code, 0, container, &pragueInstructionSet, false) + _, err := vm.ValidateCode(code, 0, container, &vm.PragueInstructionSet, false) if err != nil { b.Fatal(err) } @@ -503,15 +504,15 @@ func BenchmarkRJUMPI_2(b *testing.B) { func FuzzUnmarshalBinary(f *testing.F) { f.Fuzz(func(_ *testing.T, input []byte) { - var container Container + var container vm.Container container.UnmarshalBinary(input, true) }) } func FuzzValidate(f *testing.F) { f.Fuzz(func(_ *testing.T, code []byte, maxStack uint16) { - var container Container - container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: maxStack}) - validateCode(code, 0, &container, &pragueInstructionSet, true) + var container vm.Container + container.Types = append(container.Types, &vm.FunctionMetadata{Inputs: 0, Outputs: 0x80, MaxStackHeight: maxStack}) + vm.ValidateCode(code, 0, &container, &vm.PragueInstructionSet, true) }) } diff --git a/core/vm/errors.go b/core/vm/errors.go index e33c9fcb853c..4de0d037093d 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -47,12 +47,12 @@ var ( // ErrStackUnderflow wraps an evm error when the items on the stack less // than the minimal requirement. type ErrStackUnderflow struct { - stackLen int - required int + StackLen int + Required int } func (e ErrStackUnderflow) Error() string { - return fmt.Sprintf("stack underflow (%d <=> %d)", e.stackLen, e.required) + return fmt.Sprintf("stack underflow (%d <=> %d)", e.StackLen, e.Required) } func (e ErrStackUnderflow) Unwrap() error { diff --git a/core/vm/evm.go b/core/vm/evm.go index 442441e9aea8..76f3268d4c8a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -22,7 +22,6 @@ import ( "sync/atomic" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -71,11 +70,11 @@ type BlockContext struct { // All fields can change between transactions. type TxContext struct { // Message information - Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set) - BlobHashes []common.Hash // Provides information for BLOBHASH - BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set - AccessEvents *state.AccessEvents // Capture all state accesses for this tx + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set) + BlobHashes []common.Hash // Provides information for BLOBHASH + BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set + AccessEvents *AccessEvents // Capture all state accesses for this tx } // EVM is the Ethereum Virtual Machine base object and provides @@ -133,6 +132,10 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon return evm } +func (evm *EVM) GetInterpreter() *EVMInterpreter { + return evm.interpreter +} + // SetPrecompiles sets the precompiled contracts for the EVM. // This method is only used through RPC calls. // It is not thread-safe. @@ -144,7 +147,7 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) { // This is not threadsafe and should only be done very cautiously. func (evm *EVM) SetTxContext(txCtx TxContext) { if evm.chainRules.IsEIP4762 { - txCtx.AccessEvents = state.NewAccessEvents(evm.StateDB.PointCache()) + txCtx.AccessEvents = NewAccessEvents(evm.StateDB.PointCache()) } evm.TxContext = txCtx } @@ -212,7 +215,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) if isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), caller.Address(), input, gas, value.ToBig(), evm.Config.Tracer, evm.interpreter.readOnly, false) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -279,7 +282,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), caller.Address(), input, gas, value.ToBig(), evm.Config.Tracer, evm.interpreter.readOnly, true) } else { addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. @@ -327,7 +330,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + // NOTE: caller must, at all times be a contract. It should never happen + // that caller is something other than a Contract. + parent := caller.(*Contract) + ret, gas, err = RunPrecompiledContract(p, evm, parent.CallerAddress, caller.Address(), input, gas, nil, evm.Config.Tracer, evm.interpreter.readOnly, true) } else { addrCopy := addr // Initialise a new contract and make initialise the delegate values @@ -378,7 +384,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount) if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), caller.Address(), input, gas, nil, evm.Config.Tracer, true, false) } else { // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' @@ -596,6 +602,39 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash { return evm.StateDB.GetCodeHash(addr) } +func (evm *EVM) CreateWithAddress(caller ContractRef, code []byte, gas uint64, value *big.Int, address common.Address) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + return evm.create(caller, &codeAndHash{code: code}, gas, uint256.MustFromBig(value), address, CREATE) +} + +func (evm *EVM) GetDeploymentCode(caller ContractRef, code []byte, gas uint64, value *big.Int, address common.Address) ([]byte, uint64, error) { + contract := NewContract(caller, AccountRef(address), uint256.MustFromBig(value), gas) + contract.SetCodeOptionalHash(&address, &codeAndHash{code: code}) + ret, err := evm.interpreter.Run(contract, nil, false) + // Check whether the max code size has been exceeded, assign err if the case. + if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { + err = ErrMaxCodeSizeExceeded + } + // Reject code starting with 0xEF if EIP-3541 is enabled. + if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon { + err = ErrInvalidCode + } + if err == nil { + createDataGas := uint64(len(ret)) * params.CreateDataGas + if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { + err = ErrCodeStoreOutOfGas + } + } + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally, + // when we're in homestead this also counts for code storage gas errors. + if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { + if err != ErrExecutionReverted { + contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution) + } + } + return ret, contract.Gas, err +} + // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 55855727b527..c98318f42a79 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -25,9 +25,9 @@ import ( "github.com/ethereum/go-ethereum/params" ) -// memoryGasCost calculates the quadratic gas for memory expansion. It does so +// MemoryGasCost calculates the quadratic gas for memory expansion. It does so // only for the memory region that is expanded, not the total memory. -func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { +func MemoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { if newMemSize == 0 { return 0, nil } @@ -39,7 +39,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { if newMemSize > 0x1FFFFFFFE0 { return 0, ErrGasUintOverflow } - newMemSizeWords := toWordSize(newMemSize) + newMemSizeWords := ToWordSize(newMemSize) newMemSize = newMemSizeWords * 32 if newMemSize > uint64(mem.Len()) { @@ -67,7 +67,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { func memoryCopierGas(stackpos int) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // Gas for expanding the memory - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -77,7 +77,7 @@ func memoryCopierGas(stackpos int) gasFunc { return 0, ErrGasUintOverflow } - if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow { + if words, overflow = math.SafeMul(ToWordSize(words), params.CopyGas); overflow { return 0, ErrGasUintOverflow } @@ -91,7 +91,7 @@ func memoryCopierGas(stackpos int) gasFunc { var ( gasCallDataCopy = memoryCopierGas(2) gasCodeCopy = memoryCopierGas(2) - gasMcopy = memoryCopierGas(2) + GasMcopy = memoryCopierGas(2) gasExtCodeCopy = memoryCopierGas(3) gasReturnDataCopy = memoryCopierGas(2) ) @@ -230,7 +230,7 @@ func makeGasLog(n uint64) gasFunc { return 0, ErrGasUintOverflow } - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -254,7 +254,7 @@ func makeGasLog(n uint64) gasFunc { } func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -262,7 +262,7 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor if overflow { return 0, ErrGasUintOverflow } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { + if wordGas, overflow = math.SafeMul(ToWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { @@ -275,7 +275,7 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor // static cost have a dynamic cost which is solely based on the memory // expansion func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return memoryGasCost(mem, memorySize) + return MemoryGasCost(mem, memorySize) } var ( @@ -288,7 +288,7 @@ var ( ) func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -296,7 +296,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if overflow { return 0, ErrGasUintOverflow } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { + if wordGas, overflow = math.SafeMul(ToWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { @@ -306,7 +306,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS } func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -325,7 +325,7 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m return gas, nil } func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -345,7 +345,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, } func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) + expByteLen := uint64((stack.data[stack.Len()-2].BitLen() + 7) / 8) var ( gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas @@ -358,7 +358,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem } func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) + expByteLen := uint64((stack.data[stack.Len()-2].BitLen() + 7) / 8) var ( gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas @@ -386,7 +386,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if transfersValue && !evm.chainRules.IsEIP4762 { gas += params.CallValueTransferGas } - memoryGas, err := memoryGasCost(mem, memorySize) + memoryGas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -414,7 +414,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize } func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - memoryGas, err := memoryGasCost(mem, memorySize) + memoryGas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -449,7 +449,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory } func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } @@ -465,7 +465,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me } func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) + gas, err := MemoryGasCost(mem, memorySize) if err != nil { return 0, err } diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index be8688526193..0d72f1ba12ac 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "bytes" @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -42,9 +43,9 @@ func TestMemoryGasCost(t *testing.T) { {0x1fffffffe1, 0, true}, } for i, tt := range tests { - v, err := memoryGasCost(&Memory{}, tt.size) - if (err == ErrGasUintOverflow) != tt.overflow { - t.Errorf("test %d: overflow mismatch: have %v, want %v", i, err == ErrGasUintOverflow, tt.overflow) + v, err := vm.MemoryGasCost(&vm.Memory{}, tt.size) + if (err == vm.ErrGasUintOverflow) != tt.overflow { + t.Errorf("test %d: overflow mismatch: have %v, want %v", i, err == vm.ErrGasUintOverflow, tt.overflow) } if v != tt.cost { t.Errorf("test %d: gas cost mismatch: have %v, want %v", i, v, tt.cost) @@ -77,7 +78,7 @@ var eip2200Tests = []struct { {1, math.MaxUint64, "0x60016000556001600055", 1612, 0, nil}, // 1 -> 1 -> 1 {0, math.MaxUint64, "0x600160005560006000556001600055", 40818, 19200, nil}, // 0 -> 1 -> 0 -> 1 {1, math.MaxUint64, "0x600060005560016000556000600055", 10818, 19200, nil}, // 1 -> 0 -> 1 -> 0 - {1, 2306, "0x6001600055", 2306, 0, ErrOutOfGas}, // 1 -> 1 (2300 sentry + 2xPUSH) + {1, 2306, "0x6001600055", 2306, 0, vm.ErrOutOfGas}, // 1 -> 1 (2300 sentry + 2xPUSH) {1, 2307, "0x6001600055", 806, 0, nil}, // 1 -> 1 (2301 sentry + 2xPUSH) } @@ -91,13 +92,13 @@ func TestEIP2200(t *testing.T) { statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original})) statedb.Finalise(true) // Push the state into the "original" slot - vmctx := BlockContext{ - CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, - Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, + vmctx := vm.BlockContext{ + CanTransfer: func(vm.StateDB, common.Address, *uint256.Int) bool { return true }, + Transfer: func(vm.StateDB, common.Address, common.Address, *uint256.Int) {}, } - evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) + evm := vm.NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, vm.Config{ExtraEips: []int{2200}}) - _, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int)) + _, gas, err := evm.Call(vm.AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int)) if !errors.Is(err, tt.failure) { t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) } @@ -141,19 +142,19 @@ func TestCreateGas(t *testing.T) { statedb.CreateAccount(address) statedb.SetCode(address, hexutil.MustDecode(tt.code)) statedb.Finalise(true) - vmctx := BlockContext{ - CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, - Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, + vmctx := vm.BlockContext{ + CanTransfer: func(vm.StateDB, common.Address, *uint256.Int) bool { return true }, + Transfer: func(vm.StateDB, common.Address, common.Address, *uint256.Int) {}, BlockNumber: big.NewInt(0), } - config := Config{} + config := vm.Config{} if tt.eip3860 { config.ExtraEips = []int{3860} } - evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, config) + evm := vm.NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, config) var startGas = uint64(testGas) - ret, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int)) + ret, gas, err := evm.Call(vm.AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int)) if err != nil { return false } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index c9eea335070d..4f9e83a7fbd1 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -27,68 +27,68 @@ import ( "github.com/holiman/uint256" ) -func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.Add(&x, y) return nil, nil } -func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.Sub(&x, y) return nil, nil } -func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.Mul(&x, y) return nil, nil } -func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.Div(&x, y) return nil, nil } -func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.SDiv(&x, y) return nil, nil } -func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.Mod(&x, y) return nil, nil } -func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.SMod(&x, y) return nil, nil } -func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - base, exponent := scope.Stack.pop(), scope.Stack.peek() +func OpExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + base, exponent := scope.Stack.Pop(), scope.Stack.Peek() exponent.Exp(&base, exponent) return nil, nil } -func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - back, num := scope.Stack.pop(), scope.Stack.peek() +func OpSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + back, num := scope.Stack.Pop(), scope.Stack.Peek() num.ExtendSign(num, &back) return nil, nil } func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() + x := scope.Stack.Peek() x.Not(x) return nil, nil } -func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() if x.Lt(y) { y.SetOne() } else { @@ -97,8 +97,8 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, return nil, nil } -func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() if x.Gt(y) { y.SetOne() } else { @@ -107,8 +107,8 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, return nil, nil } -func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() if x.Slt(y) { y.SetOne() } else { @@ -117,8 +117,8 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte return nil, nil } -func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() if x.Sgt(y) { y.SetOne() } else { @@ -127,8 +127,8 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte return nil, nil } -func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() if x.Eq(y) { y.SetOne() } else { @@ -137,8 +137,8 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, return nil, nil } -func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() +func OpIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.Peek() if x.IsZero() { x.SetOne() } else { @@ -147,48 +147,48 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b return nil, nil } -func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.And(&x, y) return nil, nil } -func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.Or(&x, y) return nil, nil } -func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() +func OpXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.Pop(), scope.Stack.Peek() y.Xor(&x, y) return nil, nil } -func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - th, val := scope.Stack.pop(), scope.Stack.peek() +func OpByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + th, val := scope.Stack.Pop(), scope.Stack.Peek() val.Byte(&th) return nil, nil } -func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() +func OpAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y, z := scope.Stack.Pop(), scope.Stack.Pop(), scope.Stack.Peek() z.AddMod(&x, &y, z) return nil, nil } -func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() +func OpMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y, z := scope.Stack.Pop(), scope.Stack.Pop(), scope.Stack.Peek() z.MulMod(&x, &y, z) return nil, nil } -// opSHL implements Shift Left +// OpSHL implements Shift Left // The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the left by arg1 number of bits. -func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func OpSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value := scope.Stack.Pop(), scope.Stack.Peek() if shift.LtUint64(256) { value.Lsh(value, uint(shift.Uint64())) } else { @@ -197,12 +197,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte return nil, nil } -// opSHR implements Logical Shift Right +// OpSHR implements Logical Shift Right // The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. -func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func OpSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value := scope.Stack.Pop(), scope.Stack.Peek() if shift.LtUint64(256) { value.Rsh(value, uint(shift.Uint64())) } else { @@ -211,11 +211,11 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte return nil, nil } -// opSAR implements Arithmetic Shift Right +// OpSAR implements Arithmetic Shift Right // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. -func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - shift, value := scope.Stack.pop(), scope.Stack.peek() +func OpSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + shift, value := scope.Stack.Pop(), scope.Stack.Peek() if shift.GtUint64(256) { if value.Sign() >= 0 { value.Clear() @@ -230,8 +230,8 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte return nil, nil } -func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - offset, size := scope.Stack.pop(), scope.Stack.peek() +func OpKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + offset, size := scope.Stack.Pop(), scope.Stack.Peek() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) if interpreter.hasher == nil { @@ -250,35 +250,35 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( return nil, nil } -func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes())) +func OpAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.Push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes())) return nil, nil } func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() + slot := scope.Stack.Peek() address := common.Address(slot.Bytes20()) slot.Set(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } -func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) +func OpOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) return nil, nil } func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes())) + scope.Stack.Push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes())) return nil, nil } func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(scope.Contract.value) + scope.Stack.Push(scope.Contract.value) return nil, nil } func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() + x := scope.Stack.Peek() if offset, overflow := x.Uint64WithOverflow(); !overflow { data := getData(scope.Contract.Input, offset, 32) x.SetBytes(data) @@ -289,15 +289,15 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input)))) + scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input)))) return nil, nil } func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = scope.Stack.pop() - dataOffset = scope.Stack.pop() - length = scope.Stack.pop() + memOffset = scope.Stack.Pop() + dataOffset = scope.Stack.Pop() + length = scope.Stack.Pop() ) dataOffset64, overflow := dataOffset.Uint64WithOverflow() if overflow { @@ -312,15 +312,15 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) + scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) return nil, nil } func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = scope.Stack.pop() - dataOffset = scope.Stack.pop() - length = scope.Stack.pop() + memOffset = scope.Stack.Pop() + dataOffset = scope.Stack.Pop() + length = scope.Stack.Pop() ) offset64, overflow := dataOffset.Uint64WithOverflow() @@ -339,21 +339,21 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() + slot := scope.Stack.Peek() slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) return nil, nil } func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code)))) + scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code)))) return nil, nil } func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = scope.Stack.pop() - codeOffset = scope.Stack.pop() - length = scope.Stack.pop() + memOffset = scope.Stack.Pop() + codeOffset = scope.Stack.Pop() + length = scope.Stack.Pop() ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { @@ -368,10 +368,10 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( stack = scope.Stack - a = stack.pop() - memOffset = stack.pop() - codeOffset = stack.pop() - length = stack.pop() + a = stack.Pop() + memOffset = stack.Pop() + codeOffset = stack.Pop() + length = stack.Pop() ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { @@ -412,7 +412,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) // 6. Caller tries to get the code hash for an account which is marked as deleted, this // account should be regarded as a non-existent account and zero should be returned. func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() + slot := scope.Stack.Peek() address := common.Address(slot.Bytes20()) if interpreter.evm.StateDB.Empty(address) { slot.Clear() @@ -424,12 +424,12 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.GasPrice) - scope.Stack.push(v) + scope.Stack.Push(v) return nil, nil } func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - num := scope.Stack.peek() + num := scope.Stack.Peek() num64, overflow := num.Uint64WithOverflow() if overflow { num.Clear() @@ -459,64 +459,64 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) + scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) return nil, nil } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time)) + scope.Stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time)) return nil, nil } func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber) - scope.Stack.push(v) + scope.Stack.Push(v) return nil, nil } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty) - scope.Stack.push(v) + scope.Stack.Push(v) return nil, nil } -func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func OpRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v := new(uint256.Int).SetBytes(interpreter.evm.Context.Random.Bytes()) - scope.Stack.push(v) + scope.Stack.Push(v) return nil, nil } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) + scope.Stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) return nil, nil } func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.pop() + scope.Stack.Pop() return nil, nil } func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v := scope.Stack.peek() + v := scope.Stack.Peek() offset := v.Uint64() v.SetBytes(scope.Memory.GetPtr(offset, 32)) return nil, nil } -func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - mStart, val := scope.Stack.pop(), scope.Stack.pop() +func OpMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + mStart, val := scope.Stack.Pop(), scope.Stack.Pop() scope.Memory.Set32(mStart.Uint64(), &val) return nil, nil } -func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - off, val := scope.Stack.pop(), scope.Stack.pop() +func OpMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + off, val := scope.Stack.Pop(), scope.Stack.Pop() scope.Memory.store[off.Uint64()] = byte(val.Uint64()) return nil, nil } func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - loc := scope.Stack.peek() + loc := scope.Stack.Peek() hash := common.Hash(loc.Bytes32()) val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash) loc.SetBytes(val.Bytes()) @@ -527,8 +527,8 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b if interpreter.readOnly { return nil, ErrWriteProtection } - loc := scope.Stack.pop() - val := scope.Stack.pop() + loc := scope.Stack.Pop() + val := scope.Stack.Pop() interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) return nil, nil } @@ -537,7 +537,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if interpreter.evm.abort.Load() { return nil, errStopToken } - pos := scope.Stack.pop() + pos := scope.Stack.Pop() if !scope.Contract.validJumpdest(&pos) { return nil, ErrInvalidJump } @@ -549,7 +549,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by if interpreter.evm.abort.Load() { return nil, errStopToken } - pos, cond := scope.Stack.pop(), scope.Stack.pop() + pos, cond := scope.Stack.Pop(), scope.Stack.Pop() if !cond.IsZero() { if !scope.Contract.validJumpdest(&pos) { return nil, ErrInvalidJump @@ -564,17 +564,17 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(*pc)) + scope.Stack.Push(new(uint256.Int).SetUint64(*pc)) return nil, nil } func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len()))) + scope.Stack.Push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len()))) return nil, nil } func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas)) + scope.Stack.Push(new(uint256.Int).SetUint64(scope.Contract.Gas)) return nil, nil } @@ -663,8 +663,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b return nil, ErrWriteProtection } var ( - value = scope.Stack.pop() - offset, size = scope.Stack.pop(), scope.Stack.pop() + value = scope.Stack.Pop() + offset, size = scope.Stack.Pop(), scope.Stack.Pop() input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) gas = scope.Contract.Gas ) @@ -689,7 +689,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } else { stackvalue.SetBytes(addr.Bytes()) } - scope.Stack.push(&stackvalue) + scope.Stack.Push(&stackvalue) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) @@ -706,9 +706,9 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, ErrWriteProtection } var ( - endowment = scope.Stack.pop() - offset, size = scope.Stack.pop(), scope.Stack.pop() - salt = scope.Stack.pop() + endowment = scope.Stack.Pop() + offset, size = scope.Stack.Pop(), scope.Stack.Pop() + salt = scope.Stack.Pop() input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) gas = scope.Contract.Gas ) @@ -726,7 +726,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } else { stackvalue.SetBytes(addr.Bytes()) } - scope.Stack.push(&stackvalue) + scope.Stack.Push(&stackvalue) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { @@ -741,10 +741,10 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt stack := scope.Stack // Pop gas. The actual gas in interpreter.evm.callGasTemp. // We can use this as a temporary value - temp := stack.pop() + temp := stack.Pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() toAddr := common.Address(addr.Bytes20()) // Get the arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -762,7 +762,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } else { temp.SetOne() } - stack.push(&temp) + stack.Push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -777,10 +777,10 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack // We use it as a temporary value - temp := stack.pop() + temp := stack.Pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -795,7 +795,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } else { temp.SetOne() } - stack.push(&temp) + stack.Push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -810,10 +810,10 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext stack := scope.Stack // Pop gas. The actual gas is in interpreter.evm.callGasTemp. // We use it as a temporary value - temp := stack.pop() + temp := stack.Pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -824,7 +824,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } else { temp.SetOne() } - stack.push(&temp) + stack.Push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -839,10 +839,10 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack // We use it as a temporary value - temp := stack.pop() + temp := stack.Pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -853,7 +853,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } else { temp.SetOne() } - stack.push(&temp) + stack.Push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -865,14 +865,14 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - offset, size := scope.Stack.pop(), scope.Stack.pop() + offset, size := scope.Stack.Pop(), scope.Stack.Pop() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) return ret, errStopToken } func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - offset, size := scope.Stack.pop(), scope.Stack.pop() + offset, size := scope.Stack.Pop(), scope.Stack.Pop() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) interpreter.returnData = ret @@ -891,7 +891,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if interpreter.readOnly { return nil, ErrWriteProtection } - beneficiary := scope.Stack.pop() + beneficiary := scope.Stack.Pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) @@ -910,7 +910,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon if interpreter.readOnly { return nil, ErrWriteProtection } - beneficiary := scope.Stack.pop() + beneficiary := scope.Stack.Pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) @@ -929,16 +929,16 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon // following functions are used by the instruction jump table // make log instruction function -func makeLog(size int) executionFunc { +func makeLog(size int) ExecutionFunc { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } topics := make([]common.Hash, size) stack := scope.Stack - mStart, mSize := stack.pop(), stack.pop() + mStart, mSize := stack.Pop(), stack.Pop() for i := 0; i < size; i++ { - addr := stack.pop() + addr := stack.Pop() topics[i] = addr.Bytes32() } @@ -964,15 +964,15 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by ) *pc += 1 if *pc < codeLen { - scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) + scope.Stack.Push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) } else { - scope.Stack.push(integer.Clear()) + scope.Stack.Push(integer.Clear()) } return nil, nil } // make push instruction function -func makePush(size uint64, pushByteSize int) executionFunc { +func MakePush(size uint64, pushByteSize int) ExecutionFunc { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( codeLen = len(scope.Contract.Code) @@ -985,14 +985,14 @@ func makePush(size uint64, pushByteSize int) executionFunc { if missing := pushByteSize - (end - start); missing > 0 { a.Lsh(a, uint(8*missing)) } - scope.Stack.push(a) + scope.Stack.Push(a) *pc += size return nil, nil } } // make dup instruction function -func makeDup(size int64) executionFunc { +func makeDup(size int64) ExecutionFunc { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.dup(int(size)) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 08f2b2bfea1b..026eba0ee24d 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "bytes" @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -47,7 +48,7 @@ type twoOperandParams struct { var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" var commonParams []*twoOperandParams -var twoOpMethods map[string]executionFunc +var twoOpMethods map[string]vm.ExecutionFunc type contractRef struct { addr common.Address @@ -77,35 +78,35 @@ func init() { commonParams[i*len(params)+j] = &twoOperandParams{x, y} } } - twoOpMethods = map[string]executionFunc{ - "add": opAdd, - "sub": opSub, - "mul": opMul, - "div": opDiv, - "sdiv": opSdiv, - "mod": opMod, - "smod": opSmod, - "exp": opExp, - "signext": opSignExtend, - "lt": opLt, - "gt": opGt, - "slt": opSlt, - "sgt": opSgt, - "eq": opEq, - "and": opAnd, - "or": opOr, - "xor": opXor, - "byte": opByte, - "shl": opSHL, - "shr": opSHR, - "sar": opSAR, - } -} - -func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { + twoOpMethods = map[string]vm.ExecutionFunc{ + "add": vm.OpAdd, + "sub": vm.OpSub, + "mul": vm.OpMul, + "div": vm.OpDiv, + "sdiv": vm.OpSdiv, + "mod": vm.OpMod, + "smod": vm.OpSmod, + "exp": vm.OpExp, + "signext": vm.OpSignExtend, + "lt": vm.OpLt, + "gt": vm.OpGt, + "slt": vm.OpSlt, + "sgt": vm.OpSgt, + "eq": vm.OpEq, + "and": vm.OpAnd, + "or": vm.OpOr, + "xor": vm.OpXor, + "byte": vm.OpByte, + "shl": vm.OpSHL, + "shr": vm.OpSHR, + "sar": vm.OpSAR, + } +} + +func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn vm.ExecutionFunc, name string) { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() pc = uint64(0) ) @@ -113,13 +114,13 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) - stack.push(x) - stack.push(y) - opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) - if len(stack.data) != 1 { - t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) + stack.Push(x) + stack.Push(y) + opFn(&pc, evm.GetInterpreter(), &vm.ScopeContext{nil, stack, nil}) + if len(stack.Data()) != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.Data())) } - actual := stack.pop() + actual := stack.Pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual) @@ -138,7 +139,7 @@ func TestByteOp(t *testing.T) { {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "20", "00"}, {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "FFFFFFFFFFFFFFFF", "00"}, } - testTwoOperandOp(t, tests, opByte, "byte") + testTwoOperandOp(t, tests, vm.OpByte, "byte") } func TestSHL(t *testing.T) { @@ -155,7 +156,7 @@ func TestSHL(t *testing.T) { {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"}, } - testTwoOperandOp(t, tests, opSHL, "shl") + testTwoOperandOp(t, tests, vm.OpSHL, "shl") } func TestSHR(t *testing.T) { @@ -173,7 +174,7 @@ func TestSHR(t *testing.T) { {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, } - testTwoOperandOp(t, tests, opSHR, "shr") + testTwoOperandOp(t, tests, vm.OpSHR, "shr") } func TestSAR(t *testing.T) { @@ -197,13 +198,13 @@ func TestSAR(t *testing.T) { {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, } - testTwoOperandOp(t, tests, opSAR, "sar") + testTwoOperandOp(t, tests, vm.OpSAR, "sar") } func TestAddMod(t *testing.T) { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() pc = uint64(0) ) tests := []struct { @@ -226,11 +227,11 @@ func TestAddMod(t *testing.T) { y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y)) z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected)) - stack.push(z) - stack.push(y) - stack.push(x) - opAddmod(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) - actual := stack.pop() + stack.Push(z) + stack.Push(y) + stack.Push(x) + vm.OpAddmod(&pc, evm.GetInterpreter(), &vm.ScopeContext{nil, stack, nil}) + actual := stack.Pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) } @@ -243,20 +244,20 @@ func TestWriteExpectedValues(t *testing.T) { t.Skip("Enable this test to create json test cases.") // getResult is a convenience function to generate the expected values - getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { + getResult := func(args []*twoOperandParams, opFn vm.ExecutionFunc) []TwoOperandTestcase { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() pc = uint64(0) ) result := make([]TwoOperandTestcase, len(args)) for i, param := range args { x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) - stack.push(x) - stack.push(y) - opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) - actual := stack.pop() + stack.Push(x) + stack.Push(y) + opFn(&pc, evm.GetInterpreter(), &vm.ScopeContext{nil, stack, nil}) + actual := stack.Pop() result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} } return result @@ -287,11 +288,11 @@ func TestJsonTestcases(t *testing.T) { } } -func opBenchmark(bench *testing.B, op executionFunc, args ...string) { +func opBenchmark(bench *testing.B, op vm.ExecutionFunc, args ...string) { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - scope = &ScopeContext{nil, stack, nil} + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() + scope = &vm.ScopeContext{nil, stack, nil} ) // convert args intArgs := make([]*uint256.Int, len(args)) @@ -302,10 +303,10 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { bench.ResetTimer() for i := 0; i < bench.N; i++ { for _, arg := range intArgs { - stack.push(arg) + stack.Push(arg) } - op(&pc, evm.interpreter, scope) - stack.pop() + op(&pc, evm.GetInterpreter(), scope) + stack.Pop() } bench.StopTimer() @@ -321,169 +322,169 @@ func BenchmarkOpAdd64(b *testing.B) { x := "ffffffff" y := "fd37f3e2bba2c4f" - opBenchmark(b, opAdd, x, y) + opBenchmark(b, vm.OpAdd, x, y) } func BenchmarkOpAdd128(b *testing.B) { x := "ffffffffffffffff" y := "f5470b43c6549b016288e9a65629687" - opBenchmark(b, opAdd, x, y) + opBenchmark(b, vm.OpAdd, x, y) } func BenchmarkOpAdd256(b *testing.B) { x := "0802431afcbce1fc194c9eaa417b2fb67dc75a95db0bc7ec6b1c8af11df6a1da9" y := "a1f5aac137876480252e5dcac62c354ec0d42b76b0642b6181ed099849ea1d57" - opBenchmark(b, opAdd, x, y) + opBenchmark(b, vm.OpAdd, x, y) } func BenchmarkOpSub64(b *testing.B) { x := "51022b6317003a9d" y := "a20456c62e00753a" - opBenchmark(b, opSub, x, y) + opBenchmark(b, vm.OpSub, x, y) } func BenchmarkOpSub128(b *testing.B) { x := "4dde30faaacdc14d00327aac314e915d" y := "9bbc61f5559b829a0064f558629d22ba" - opBenchmark(b, opSub, x, y) + opBenchmark(b, vm.OpSub, x, y) } func BenchmarkOpSub256(b *testing.B) { x := "4bfcd8bb2ac462735b48a17580690283980aa2d679f091c64364594df113ea37" y := "97f9b1765588c4e6b69142eb00d20507301545acf3e1238c86c8b29be227d46e" - opBenchmark(b, opSub, x, y) + opBenchmark(b, vm.OpSub, x, y) } func BenchmarkOpMul(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opMul, x, y) + opBenchmark(b, vm.OpMul, x, y) } func BenchmarkOpDiv256(b *testing.B) { x := "ff3f9014f20db29ae04af2c2d265de17" y := "fe7fb0d1f59dfe9492ffbf73683fd1e870eec79504c60144cc7f5fc2bad1e611" - opBenchmark(b, opDiv, x, y) + opBenchmark(b, vm.OpDiv, x, y) } func BenchmarkOpDiv128(b *testing.B) { x := "fdedc7f10142ff97" y := "fbdfda0e2ce356173d1993d5f70a2b11" - opBenchmark(b, opDiv, x, y) + opBenchmark(b, vm.OpDiv, x, y) } func BenchmarkOpDiv64(b *testing.B) { x := "fcb34eb3" y := "f97180878e839129" - opBenchmark(b, opDiv, x, y) + opBenchmark(b, vm.OpDiv, x, y) } func BenchmarkOpSdiv(b *testing.B) { x := "ff3f9014f20db29ae04af2c2d265de17" y := "fe7fb0d1f59dfe9492ffbf73683fd1e870eec79504c60144cc7f5fc2bad1e611" - opBenchmark(b, opSdiv, x, y) + opBenchmark(b, vm.OpSdiv, x, y) } func BenchmarkOpMod(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opMod, x, y) + opBenchmark(b, vm.OpMod, x, y) } func BenchmarkOpSmod(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opSmod, x, y) + opBenchmark(b, vm.OpSmod, x, y) } func BenchmarkOpExp(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opExp, x, y) + opBenchmark(b, vm.OpExp, x, y) } func BenchmarkOpSignExtend(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opSignExtend, x, y) + opBenchmark(b, vm.OpSignExtend, x, y) } func BenchmarkOpLt(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opLt, x, y) + opBenchmark(b, vm.OpLt, x, y) } func BenchmarkOpGt(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opGt, x, y) + opBenchmark(b, vm.OpGt, x, y) } func BenchmarkOpSlt(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opSlt, x, y) + opBenchmark(b, vm.OpSlt, x, y) } func BenchmarkOpSgt(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opSgt, x, y) + opBenchmark(b, vm.OpSgt, x, y) } func BenchmarkOpEq(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opEq, x, y) + opBenchmark(b, vm.OpEq, x, y) } func BenchmarkOpEq2(b *testing.B) { x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" y := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201fffffffe" - opBenchmark(b, opEq, x, y) + opBenchmark(b, vm.OpEq, x, y) } func BenchmarkOpAnd(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opAnd, x, y) + opBenchmark(b, vm.OpAnd, x, y) } func BenchmarkOpOr(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opOr, x, y) + opBenchmark(b, vm.OpOr, x, y) } func BenchmarkOpXor(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opXor, x, y) + opBenchmark(b, vm.OpXor, x, y) } func BenchmarkOpByte(b *testing.B) { x := alphabetSoup y := alphabetSoup - opBenchmark(b, opByte, x, y) + opBenchmark(b, vm.OpByte, x, y) } func BenchmarkOpAddmod(b *testing.B) { @@ -491,7 +492,7 @@ func BenchmarkOpAddmod(b *testing.B) { y := alphabetSoup z := alphabetSoup - opBenchmark(b, opAddmod, x, y, z) + opBenchmark(b, vm.OpAddmod, x, y, z) } func BenchmarkOpMulmod(b *testing.B) { @@ -499,50 +500,50 @@ func BenchmarkOpMulmod(b *testing.B) { y := alphabetSoup z := alphabetSoup - opBenchmark(b, opMulmod, x, y, z) + opBenchmark(b, vm.OpMulmod, x, y, z) } func BenchmarkOpSHL(b *testing.B) { x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" y := "ff" - opBenchmark(b, opSHL, x, y) + opBenchmark(b, vm.OpSHL, x, y) } func BenchmarkOpSHR(b *testing.B) { x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" y := "ff" - opBenchmark(b, opSHR, x, y) + opBenchmark(b, vm.OpSHR, x, y) } func BenchmarkOpSAR(b *testing.B) { x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" y := "ff" - opBenchmark(b, opSAR, x, y) + opBenchmark(b, vm.OpSAR, x, y) } func BenchmarkOpIsZero(b *testing.B) { x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" - opBenchmark(b, opIszero, x) + opBenchmark(b, vm.OpIszero, x) } func TestOpMstore(t *testing.T) { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() + mem = vm.NewMemory() ) mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" - stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) - stack.push(new(uint256.Int)) - opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) + stack.Push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) + stack.Push(new(uint256.Int)) + vm.OpMstore(&pc, evm.GetInterpreter(), &vm.ScopeContext{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } - stack.push(new(uint256.Int).SetUint64(0x1)) - stack.push(new(uint256.Int)) - opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) + stack.Push(new(uint256.Int).SetUint64(0x1)) + stack.Push(new(uint256.Int)) + vm.OpMstore(&pc, evm.GetInterpreter(), &vm.ScopeContext{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } @@ -550,9 +551,9 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() + mem = vm.NewMemory() ) mem.Resize(64) pc := uint64(0) @@ -561,23 +562,23 @@ func BenchmarkOpMstore(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.push(value) - stack.push(memStart) - opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) + stack.Push(value) + stack.Push(memStart) + vm.OpMstore(&pc, evm.GetInterpreter(), &vm.ScopeContext{mem, stack, nil}) } } func TestOpTstore(t *testing.T) { var ( statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - evm = NewEVM(BlockContext{}, statedb, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() + evm = vm.NewEVM(vm.BlockContext{}, statedb, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() + mem = vm.NewMemory() caller = common.Address{} to = common.Address{1} contractRef = contractRef{caller} - contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0) - scopeContext = ScopeContext{mem, stack, contract} + contract = vm.NewContract(contractRef, vm.AccountRef(to), new(uint256.Int), 0) + scopeContext = vm.ScopeContext{mem, stack, contract} value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") ) @@ -587,22 +588,22 @@ func TestOpTstore(t *testing.T) { pc := uint64(0) // push the value to the stack - stack.push(new(uint256.Int).SetBytes(value)) + stack.Push(new(uint256.Int).SetBytes(value)) // push the location to the stack - stack.push(new(uint256.Int)) - opTstore(&pc, evm.interpreter, &scopeContext) + stack.Push(new(uint256.Int)) + vm.OpTstore(&pc, evm.GetInterpreter(), &scopeContext) // there should be no elements on the stack after TSTORE - if stack.len() != 0 { + if stack.Len() != 0 { t.Fatal("stack wrong size") } // push the location to the stack - stack.push(new(uint256.Int)) - opTload(&pc, evm.interpreter, &scopeContext) + stack.Push(new(uint256.Int)) + vm.OpTload(&pc, evm.GetInterpreter(), &scopeContext) // there should be one element on the stack after TLOAD - if stack.len() != 1 { + if stack.Len() != 1 { t.Fatal("stack wrong size") } - val := stack.peek() + val := stack.Peek() if !bytes.Equal(val.Bytes(), value) { t.Fatal("incorrect element read from transient storage") } @@ -610,9 +611,9 @@ func TestOpTstore(t *testing.T) { func BenchmarkOpKeccak256(bench *testing.B) { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() + mem = vm.NewMemory() ) mem.Resize(32) pc := uint64(0) @@ -620,9 +621,9 @@ func BenchmarkOpKeccak256(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.push(uint256.NewInt(32)) - stack.push(start) - opKeccak256(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) + stack.Push(uint256.NewInt(32)) + stack.Push(start) + vm.OpKeccak256(&pc, evm.GetInterpreter(), &vm.ScopeContext{mem, stack, nil}) } } @@ -684,11 +685,11 @@ func TestCreate2Addresses(t *testing.T) { codeHash := crypto.Keccak256(code) address := crypto.CreateAddress2(origin, salt, codeHash) /* - stack := newstack() + stack := vm.Newstack() // salt, but we don't need that for this test - stack.push(big.NewInt(int64(len(code)))) //size - stack.push(big.NewInt(0)) // memstart - stack.push(big.NewInt(0)) // value + stack.Push(big.NewInt(int64(len(code)))) //size + stack.Push(big.NewInt(0)) // memstart + stack.Push(big.NewInt(0)) // value gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0) fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) */ @@ -712,15 +713,15 @@ func TestRandom(t *testing.T) { {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, } { var ( - evm = NewEVM(BlockContext{Random: &tt.random}, nil, params.TestChainConfig, Config{}) - stack = newstack() + evm = vm.NewEVM(vm.BlockContext{Random: &tt.random}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() pc = uint64(0) ) - opRandom(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) - if len(stack.data) != 1 { - t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) + vm.OpRandom(&pc, evm.GetInterpreter(), &vm.ScopeContext{nil, stack, nil}) + if len(stack.Data()) != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.Data())) } - actual := stack.pop() + actual := stack.Pop() expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes())) if overflow { t.Errorf("Testcase %v: invalid overflow", tt.name) @@ -752,17 +753,17 @@ func TestBlobHash(t *testing.T) { {name: "out-of-bounds (nil)", idx: 25, expect: zero, hashes: nil}, } { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() pc = uint64(0) ) - evm.SetTxContext(TxContext{BlobHashes: tt.hashes}) - stack.push(uint256.NewInt(tt.idx)) - opBlobHash(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) - if len(stack.data) != 1 { - t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) + evm.SetTxContext(vm.TxContext{BlobHashes: tt.hashes}) + stack.Push(uint256.NewInt(tt.idx)) + vm.OpBlobHash(&pc, evm.GetInterpreter(), &vm.ScopeContext{nil, stack, nil}) + if len(stack.Data()) != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.Data())) } - actual := stack.pop() + actual := stack.Pop() expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes())) if overflow { t.Errorf("Testcase %v: invalid overflow", tt.name) @@ -855,13 +856,13 @@ func TestOpMCopy(t *testing.T) { }, } { var ( - evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() + evm = vm.NewEVM(vm.BlockContext{}, nil, params.TestChainConfig, vm.Config{}) + stack = vm.Newstack() pc = uint64(0) ) data := common.FromHex(strings.ReplaceAll(tc.pre, " ", "")) // Set pre - mem := NewMemory() + mem := vm.NewMemory() mem.Resize(uint64(len(data))) mem.Set(0, uint64(len(data)), data) // Push stack args @@ -869,38 +870,38 @@ func TestOpMCopy(t *testing.T) { src, _ := uint256.FromHex(tc.src) dst, _ := uint256.FromHex(tc.dst) - stack.push(len) - stack.push(src) - stack.push(dst) + stack.Push(len) + stack.Push(src) + stack.Push(dst) wantErr := (tc.wantGas == 0) // Calc mem expansion var memorySize uint64 - if memSize, overflow := memoryMcopy(stack); overflow { + if memSize, overflow := vm.MemoryMcopy(stack); overflow { if wantErr { continue } t.Errorf("overflow") } else { var overflow bool - if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { - t.Error(ErrGasUintOverflow) + if memorySize, overflow = math.SafeMul(vm.ToWordSize(memSize), 32); overflow { + t.Error(vm.ErrGasUintOverflow) } } // and the dynamic cost var haveGas uint64 - if dynamicCost, err := gasMcopy(evm, nil, stack, mem, memorySize); err != nil { + if dynamicCost, err := vm.GasMcopy(evm, nil, stack, mem, memorySize); err != nil { t.Error(err) } else { - haveGas = GasFastestStep + dynamicCost + haveGas = vm.GasFastestStep + dynamicCost } // Expand mem if memorySize > 0 { mem.Resize(memorySize) } // Do the copy - opMcopy(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) + vm.OpMcopy(&pc, evm.GetInterpreter(), &vm.ScopeContext{mem, stack, nil}) want := common.FromHex(strings.ReplaceAll(tc.want, " ", "")) - if have := mem.store; !bytes.Equal(want, have) { + if have := mem.Store(); !bytes.Equal(want, have) { t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have) } wantGas := tc.wantGas @@ -915,12 +916,12 @@ func TestOpMCopy(t *testing.T) { func TestPush(t *testing.T) { code := common.FromHex("0011223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121") - push32 := makePush(32, 32) + push32 := vm.MakePush(32, 32) - scope := &ScopeContext{ + scope := &vm.ScopeContext{ Memory: nil, - Stack: newstack(), - Contract: &Contract{ + Stack: vm.Newstack(), + Contract: &vm.Contract{ Code: code, }, } @@ -975,7 +976,7 @@ func TestPush(t *testing.T) { pc := new(uint64) *pc = uint64(i) push32(pc, nil, scope) - res := scope.Stack.pop() + res := scope.Stack.Pop() if have := res.Hex(); have != want { t.Fatalf("case %d, have %v want %v", i, have, want) } diff --git a/core/vm/interface.go b/core/vm/interface.go index 0d7862a66e11..c32f8c7b3463 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,7 +20,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -99,10 +98,24 @@ type StateDB interface { Witness() *stateless.Witness - AccessEvents() *state.AccessEvents + AccessEvents() *AccessEvents // Finalise must be invoked at the end of a transaction Finalise(bool) + + // new methods + Error() error + SetBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) + SetStorage(common.Address, map[common.Hash]common.Hash) + Commit(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (common.Hash, error) + SetTxContext(thash common.Hash, ti int) + Copy() StateDB + IntermediateRoot(deleteEmptyObjects bool) common.Hash + GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log + TxIndex() int + Preimages() map[common.Hash][]byte + Logs() []*types.Log + SetEVM(evm *EVM) } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index a0038d1aa833..29cb8b00e924 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -110,7 +110,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // TODO replace with proper instruction set when fork is specified table = &verkleInstructionSet case evm.chainRules.IsPrague: - table = &pragueInstructionSet + table = &PragueInstructionSet case evm.chainRules.IsCancun: table = &cancunInstructionSet case evm.chainRules.IsShanghai: @@ -139,7 +139,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { var extraEips []int if len(evm.Config.ExtraEips) > 0 { // Deep-copy jumptable to prevent modification of opcodes in other tables - table = copyJumpTable(table) + table = CopyJumpTable(table) } for _, eip := range evm.Config.ExtraEips { if err := EnableEIP(eip, table); err != nil { @@ -183,7 +183,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( var ( op OpCode // current opcode mem = NewMemory() // bound memory - stack = newstack() // local stack + stack = Newstack() // local stack callContext = &ScopeContext{ Memory: mem, Stack: stack, @@ -246,8 +246,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( operation := in.table[op] cost = operation.constantGas // For tracing // Validate stack - if sLen := stack.len(); sLen < operation.minStack { - return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} + if sLen := stack.Len(); sLen < operation.minStack { + return nil, &ErrStackUnderflow{StackLen: sLen, Required: operation.minStack} } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } @@ -272,7 +272,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } // memory is expanded in words of 32 bytes. Gas // is also calculated in words. - if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + if memorySize, overflow = math.SafeMul(ToWordSize(memSize), 32); overflow { return nil, ErrGasUintOverflow } } diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index cacad8f81339..358c5f34b6c1 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "math" @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -37,8 +38,8 @@ var loopInterruptTests = []string{ func TestLoopInterrupt(t *testing.T) { address := common.BytesToAddress([]byte("contract")) - vmctx := BlockContext{ - Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, + vmctx := vm.BlockContext{ + Transfer: func(vm.StateDB, common.Address, common.Address, *uint256.Int) {}, } for i, tt := range loopInterruptTests { @@ -47,13 +48,13 @@ func TestLoopInterrupt(t *testing.T) { statedb.SetCode(address, common.Hex2Bytes(tt)) statedb.Finalise(true) - evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{}) + evm := vm.NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, vm.Config{}) errChannel := make(chan error) timeout := make(chan bool) - go func(evm *EVM) { - _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(uint256.Int)) + go func(evm *vm.EVM) { + _, _, err := evm.Call(vm.AccountRef(common.Address{}), address, nil, math.MaxUint64, new(uint256.Int)) errChannel <- err }(evm) diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 6610fa7f9aba..64ca7df5581d 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -23,7 +23,7 @@ import ( ) type ( - executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) + ExecutionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 // memorySizeFunc returns the required size, and whether the operation overflowed a uint64 memorySizeFunc func(*Stack) (size uint64, overflow bool) @@ -31,7 +31,7 @@ type ( type operation struct { // execute is the operation function - execute executionFunc + execute ExecutionFunc constantGas uint64 dynamicGas gasFunc // minStack tells how many stack items are required @@ -47,6 +47,14 @@ type operation struct { undefined bool } +func (o *operation) GetConstantGas() uint64 { + return o.constantGas +} + +func (o *operation) SetConstantGas(cg uint64) { + o.constantGas = cg +} + var ( frontierInstructionSet = newFrontierInstructionSet() homesteadInstructionSet = newHomesteadInstructionSet() @@ -57,12 +65,12 @@ var ( istanbulInstructionSet = newIstanbulInstructionSet() berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() - mergeInstructionSet = newMergeInstructionSet() + mergeInstructionSet = NewMergeInstructionSet() shanghaiInstructionSet = newShanghaiInstructionSet() cancunInstructionSet = newCancunInstructionSet() verkleInstructionSet = newVerkleInstructionSet() - pragueInstructionSet = newPragueInstructionSet() - eofInstructionSet = newEOFInstructionSetForTesting() + PragueInstructionSet = newPragueInstructionSet() + EofInstructionSet = newEOFInstructionSetForTesting() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -119,17 +127,17 @@ func newCancunInstructionSet() JumpTable { } func newShanghaiInstructionSet() JumpTable { - instructionSet := newMergeInstructionSet() + instructionSet := NewMergeInstructionSet() enable3855(&instructionSet) // PUSH0 instruction enable3860(&instructionSet) // Limit and meter initcode return validate(instructionSet) } -func newMergeInstructionSet() JumpTable { +func NewMergeInstructionSet() JumpTable { instructionSet := newLondonInstructionSet() instructionSet[PREVRANDAO] = &operation{ - execute: opRandom, + execute: OpRandom, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), @@ -171,19 +179,19 @@ func newIstanbulInstructionSet() JumpTable { func newConstantinopleInstructionSet() JumpTable { instructionSet := newByzantiumInstructionSet() instructionSet[SHL] = &operation{ - execute: opSHL, + execute: OpSHL, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), } instructionSet[SHR] = &operation{ - execute: opSHR, + execute: OpSHR, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), } instructionSet[SAR] = &operation{ - execute: opSAR, + execute: OpSAR, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), @@ -287,121 +295,121 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(0, 0), }, ADD: { - execute: opAdd, + execute: OpAdd, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, MUL: { - execute: opMul, + execute: OpMul, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SUB: { - execute: opSub, + execute: OpSub, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, DIV: { - execute: opDiv, + execute: OpDiv, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SDIV: { - execute: opSdiv, + execute: OpSdiv, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, MOD: { - execute: opMod, + execute: OpMod, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SMOD: { - execute: opSmod, + execute: OpSmod, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, ADDMOD: { - execute: opAddmod, + execute: OpAddmod, constantGas: GasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), }, MULMOD: { - execute: opMulmod, + execute: OpMulmod, constantGas: GasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), }, EXP: { - execute: opExp, + execute: OpExp, dynamicGas: gasExpFrontier, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SIGNEXTEND: { - execute: opSignExtend, + execute: OpSignExtend, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, LT: { - execute: opLt, + execute: OpLt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, GT: { - execute: opGt, + execute: OpGt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SLT: { - execute: opSlt, + execute: OpSlt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SGT: { - execute: opSgt, + execute: OpSgt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, EQ: { - execute: opEq, + execute: OpEq, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, ISZERO: { - execute: opIszero, + execute: OpIszero, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, AND: { - execute: opAnd, + execute: OpAnd, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, XOR: { - execute: opXor, + execute: OpXor, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, OR: { - execute: opOr, + execute: OpOr, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), @@ -413,13 +421,13 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(1, 1), }, BYTE: { - execute: opByte, + execute: OpByte, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, KECCAK256: { - execute: opKeccak256, + execute: OpKeccak256, constantGas: params.Keccak256Gas, dynamicGas: gasKeccak256, minStack: minStack(2, 1), @@ -427,7 +435,7 @@ func newFrontierInstructionSet() JumpTable { memorySize: memoryKeccak256, }, ADDRESS: { - execute: opAddress, + execute: OpAddress, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), @@ -439,7 +447,7 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(1, 1), }, ORIGIN: { - execute: opOrigin, + execute: OpOrigin, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), @@ -561,7 +569,7 @@ func newFrontierInstructionSet() JumpTable { memorySize: memoryMLoad, }, MSTORE: { - execute: opMstore, + execute: OpMstore, constantGas: GasFastestStep, dynamicGas: gasMStore, minStack: minStack(2, 0), @@ -569,7 +577,7 @@ func newFrontierInstructionSet() JumpTable { memorySize: memoryMStore, }, MSTORE8: { - execute: opMstore8, + execute: OpMstore8, constantGas: GasFastestStep, dynamicGas: gasMStore8, memorySize: memoryMStore8, @@ -631,187 +639,187 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(0, 1), }, PUSH2: { - execute: makePush(2, 2), + execute: MakePush(2, 2), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH3: { - execute: makePush(3, 3), + execute: MakePush(3, 3), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH4: { - execute: makePush(4, 4), + execute: MakePush(4, 4), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH5: { - execute: makePush(5, 5), + execute: MakePush(5, 5), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH6: { - execute: makePush(6, 6), + execute: MakePush(6, 6), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH7: { - execute: makePush(7, 7), + execute: MakePush(7, 7), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH8: { - execute: makePush(8, 8), + execute: MakePush(8, 8), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH9: { - execute: makePush(9, 9), + execute: MakePush(9, 9), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH10: { - execute: makePush(10, 10), + execute: MakePush(10, 10), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH11: { - execute: makePush(11, 11), + execute: MakePush(11, 11), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH12: { - execute: makePush(12, 12), + execute: MakePush(12, 12), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH13: { - execute: makePush(13, 13), + execute: MakePush(13, 13), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH14: { - execute: makePush(14, 14), + execute: MakePush(14, 14), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH15: { - execute: makePush(15, 15), + execute: MakePush(15, 15), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH16: { - execute: makePush(16, 16), + execute: MakePush(16, 16), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH17: { - execute: makePush(17, 17), + execute: MakePush(17, 17), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH18: { - execute: makePush(18, 18), + execute: MakePush(18, 18), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH19: { - execute: makePush(19, 19), + execute: MakePush(19, 19), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH20: { - execute: makePush(20, 20), + execute: MakePush(20, 20), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH21: { - execute: makePush(21, 21), + execute: MakePush(21, 21), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH22: { - execute: makePush(22, 22), + execute: MakePush(22, 22), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH23: { - execute: makePush(23, 23), + execute: MakePush(23, 23), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH24: { - execute: makePush(24, 24), + execute: MakePush(24, 24), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH25: { - execute: makePush(25, 25), + execute: MakePush(25, 25), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH26: { - execute: makePush(26, 26), + execute: MakePush(26, 26), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH27: { - execute: makePush(27, 27), + execute: MakePush(27, 27), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH28: { - execute: makePush(28, 28), + execute: MakePush(28, 28), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH29: { - execute: makePush(29, 29), + execute: MakePush(29, 29), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH30: { - execute: makePush(30, 30), + execute: MakePush(30, 30), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH31: { - execute: makePush(31, 31), + execute: MakePush(31, 31), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH32: { - execute: makePush(32, 32), + execute: MakePush(32, 32), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), @@ -1097,7 +1105,7 @@ func newFrontierInstructionSet() JumpTable { return validate(tbl) } -func copyJumpTable(source *JumpTable) *JumpTable { +func CopyJumpTable(source *JumpTable) *JumpTable { dest := *source for i, op := range source { if op != nil { diff --git a/core/vm/jump_table_export.go b/core/vm/jump_table_export.go index b8fa6049bb41..7794e5657f29 100644 --- a/core/vm/jump_table_export.go +++ b/core/vm/jump_table_export.go @@ -37,7 +37,7 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) { case rules.IsShanghai: return newShanghaiInstructionSet(), nil case rules.IsMerge: - return newMergeInstructionSet(), nil + return NewMergeInstructionSet(), nil case rules.IsLondon: return newLondonInstructionSet(), nil case rules.IsBerlin: diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go index a4f9759ed2b7..9376f20b208a 100644 --- a/core/vm/jump_table_test.go +++ b/core/vm/jump_table_test.go @@ -14,22 +14,23 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package vm_test import ( "testing" + "github.com/ethereum/go-ethereum/core/vm" "github.com/stretchr/testify/require" ) // TestJumpTableCopy tests that deep copy is necessary to prevent modify shared jump table func TestJumpTableCopy(t *testing.T) { - tbl := newMergeInstructionSet() - require.Equal(t, uint64(0), tbl[SLOAD].constantGas) + tbl := vm.NewMergeInstructionSet() + require.Equal(t, uint64(0), tbl[vm.SLOAD].GetConstantGas()) // a deep copy won't modify the shared jump table - deepCopy := copyJumpTable(&tbl) - deepCopy[SLOAD].constantGas = 100 - require.Equal(t, uint64(100), deepCopy[SLOAD].constantGas) - require.Equal(t, uint64(0), tbl[SLOAD].constantGas) + deepCopy := vm.CopyJumpTable(&tbl) + deepCopy[vm.SLOAD].SetConstantGas(100) + require.Equal(t, uint64(100), deepCopy[vm.SLOAD].GetConstantGas()) + require.Equal(t, uint64(0), tbl[vm.SLOAD].GetConstantGas()) } diff --git a/core/vm/memory.go b/core/vm/memory.go index 5e11e83748fb..8fe575b9aa78 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -39,6 +39,10 @@ func NewMemory() *Memory { return memoryPool.Get().(*Memory) } +func (m *Memory) Store() []byte { + return m.store +} + // Free returns the memory to the pool. func (m *Memory) Free() { // To reduce peak allocation, return only smaller memory instances to the pool. diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 28746042cf3b..33b3141faf2f 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -48,7 +48,7 @@ func memoryMStore(stack *Stack) (uint64, bool) { return calcMemSize64WithUint(stack.Back(0), 32) } -func memoryMcopy(stack *Stack) (uint64, bool) { +func MemoryMcopy(stack *Stack) (uint64, bool) { mStart := stack.Back(0) // stack[0]: dest if stack.Back(1).Gt(mStart) { mStart = stack.Back(1) // stack[1]: source diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index ff3875868f56..ef2b0f1b5c04 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -34,7 +34,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { } // Gas sentry honoured, do the actual gas calculation based on the stored value var ( - y, x = stack.Back(1), stack.peek() + y, x = stack.Back(1), stack.Peek() slot = common.Hash(x.Bytes32()) current = evm.StateDB.GetState(contract.Address(), slot) cost = uint64(0) @@ -97,7 +97,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { // charge 2100 gas and add the pair to accessed_storage_keys. // If the pair is already in accessed_storage_keys, charge 100 gas. func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - loc := stack.peek() + loc := stack.Peek() slot := common.Hash(loc.Bytes32()) // Check slot presence in the access list if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { @@ -120,7 +120,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo if err != nil { return 0, err } - addr := common.Address(stack.peek().Bytes20()) + addr := common.Address(stack.Peek().Bytes20()) // Check slot presence in the access list if !evm.StateDB.AddressInAccessList(addr) { evm.StateDB.AddAddressToAccessList(addr) @@ -142,7 +142,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo // - extcodesize, // - (ext) balance func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - addr := common.Address(stack.peek().Bytes20()) + addr := common.Address(stack.Peek().Bytes20()) // Check slot presence in the access list if !evm.StateDB.AddressInAccessList(addr) { // If the caller cannot afford the cost, this change will be rolled back @@ -225,7 +225,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( gas uint64 - address = common.Address(stack.peek().Bytes20()) + address = common.Address(stack.Peek().Bytes20()) ) if !evm.StateDB.AddressInAccessList(address) { // If the caller cannot afford the cost, this change will be rolled back diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 751761a91159..9d4316538e52 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -25,7 +25,7 @@ import ( ) func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true) + gas := evm.AccessEvents.SlotGas(contract.Address(), stack.Peek().Bytes32(), true) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -33,7 +33,7 @@ func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo } func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false) + gas := evm.AccessEvents.SlotGas(contract.Address(), stack.Peek().Bytes32(), false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -44,7 +44,7 @@ func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem if contract.IsSystemCall { return 0, nil } - address := stack.peek().Bytes20() + address := stack.Peek().Bytes20() gas := evm.AccessEvents.BasicDataGas(address, false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 @@ -53,7 +53,7 @@ func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem } func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - address := stack.peek().Bytes20() + address := stack.Peek().Bytes20() if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } @@ -71,7 +71,7 @@ func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if contract.IsSystemCall { return 0, nil } - address := stack.peek().Bytes20() + address := stack.Peek().Bytes20() if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } @@ -110,7 +110,7 @@ var ( ) func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - beneficiaryAddr := common.Address(stack.peek().Bytes20()) + beneficiaryAddr := common.Address(stack.Peek().Bytes20()) if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile { return 0, nil } @@ -161,7 +161,7 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo if contract.IsSystemCall { return gas, nil } - addr := common.Address(stack.peek().Bytes20()) + addr := common.Address(stack.Peek().Bytes20()) wgas := evm.AccessEvents.BasicDataGas(addr, false) if wgas == 0 { wgas = params.WarmStorageReadCostEIP2929 diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 2243e14b65a9..ae815b54a270 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -49,12 +49,12 @@ type Config struct { BlobFeeCap *big.Int Random *common.Hash - State *state.StateDB + State vm.StateDB GetHashFn func(n uint64) common.Hash } // sets defaults on the config -func setDefaults(cfg *Config) { +func SetDefaults(cfg *Config) { if cfg.ChainConfig == nil { var ( shanghaiTime = uint64(0) @@ -116,11 +116,11 @@ func setDefaults(cfg *Config) { // // Execute sets up an in-memory, temporary, environment for the execution of // the given code. It makes sure that it's restored to its original state afterwards. -func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { +func Execute(code, input []byte, cfg *Config) ([]byte, vm.StateDB, error) { if cfg == nil { cfg = new(Config) } - setDefaults(cfg) + SetDefaults(cfg) if cfg.State == nil { cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) @@ -160,7 +160,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { if cfg == nil { cfg = new(Config) } - setDefaults(cfg) + SetDefaults(cfg) if cfg.State == nil { cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) @@ -196,7 +196,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // Call, unlike Execute, requires a config and also requires the State field to // be set. func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) { - setDefaults(cfg) + SetDefaults(cfg) var ( vmenv = NewEnv(cfg) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index bde230b6da12..5ccafa6248ec 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -45,7 +45,7 @@ import ( func TestDefaults(t *testing.T) { cfg := new(Config) - setDefaults(cfg) + SetDefaults(cfg) if cfg.Difficulty == nil { t.Error("expected difficulty to be non nil") @@ -397,7 +397,7 @@ func TestBlockhash(t *testing.T) { // state, this should not be used, since it does not reset the state between runs. func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode string, b *testing.B) { cfg := new(Config) - setDefaults(cfg) + SetDefaults(cfg) cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) cfg.GasLimit = gas if len(tracerCode) > 0 { diff --git a/core/vm/stack.go b/core/vm/stack.go index 879dc9aa6d82..23d970a13cab 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -35,7 +35,7 @@ type Stack struct { data []uint256.Int } -func newstack() *Stack { +func Newstack() *Stack { return stackPool.Get().(*Stack) } @@ -49,79 +49,79 @@ func (st *Stack) Data() []uint256.Int { return st.data } -func (st *Stack) push(d *uint256.Int) { +func (st *Stack) Push(d *uint256.Int) { // NOTE push limit (1024) is checked in baseCheck st.data = append(st.data, *d) } -func (st *Stack) pop() (ret uint256.Int) { +func (st *Stack) Pop() (ret uint256.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return } -func (st *Stack) len() int { +func (st *Stack) Len() int { return len(st.data) } func (st *Stack) swap1() { - st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2] + st.data[st.Len()-2], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-2] } func (st *Stack) swap2() { - st.data[st.len()-3], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-3] + st.data[st.Len()-3], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-3] } func (st *Stack) swap3() { - st.data[st.len()-4], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-4] + st.data[st.Len()-4], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-4] } func (st *Stack) swap4() { - st.data[st.len()-5], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-5] + st.data[st.Len()-5], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-5] } func (st *Stack) swap5() { - st.data[st.len()-6], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-6] + st.data[st.Len()-6], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-6] } func (st *Stack) swap6() { - st.data[st.len()-7], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-7] + st.data[st.Len()-7], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-7] } func (st *Stack) swap7() { - st.data[st.len()-8], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-8] + st.data[st.Len()-8], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-8] } func (st *Stack) swap8() { - st.data[st.len()-9], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-9] + st.data[st.Len()-9], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-9] } func (st *Stack) swap9() { - st.data[st.len()-10], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-10] + st.data[st.Len()-10], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-10] } func (st *Stack) swap10() { - st.data[st.len()-11], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-11] + st.data[st.Len()-11], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-11] } func (st *Stack) swap11() { - st.data[st.len()-12], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-12] + st.data[st.Len()-12], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-12] } func (st *Stack) swap12() { - st.data[st.len()-13], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-13] + st.data[st.Len()-13], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-13] } func (st *Stack) swap13() { - st.data[st.len()-14], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-14] + st.data[st.Len()-14], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-14] } func (st *Stack) swap14() { - st.data[st.len()-15], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-15] + st.data[st.Len()-15], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-15] } func (st *Stack) swap15() { - st.data[st.len()-16], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-16] + st.data[st.Len()-16], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-16] } func (st *Stack) swap16() { - st.data[st.len()-17], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-17] + st.data[st.Len()-17], st.data[st.Len()-1] = st.data[st.Len()-1], st.data[st.Len()-17] } func (st *Stack) dup(n int) { - st.push(&st.data[st.len()-n]) + st.Push(&st.data[st.Len()-n]) } -func (st *Stack) peek() *uint256.Int { - return &st.data[st.len()-1] +func (st *Stack) Peek() *uint256.Int { + return &st.data[st.Len()-1] } // Back returns the n'th item in stack func (st *Stack) Back(n int) *uint256.Int { - return &st.data[st.len()-n-1] + return &st.data[st.Len()-n-1] } diff --git a/eth/api_backend.go b/eth/api_backend.go index 66621190ddcf..3ca9d5ce298b 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -30,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -182,11 +181,11 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r return nil, errors.New("invalid arguments; neither block nor hash specified") } -func (b *EthAPIBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) { +func (b *EthAPIBackend) Pending() (*types.Block, types.Receipts, vm.StateDB) { return b.eth.miner.Pending() } -func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { +func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (vm.StateDB, *types.Header, error) { // Pending state is only known by the miner if number == rpc.PendingBlockNumber { block, _, state := b.eth.miner.Pending() @@ -210,7 +209,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B return stateDb, header, nil } -func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { +func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (vm.StateDB, *types.Header, error) { if blockNr, ok := blockNrOrHash.Number(); ok { return b.StateAndHeaderByNumber(ctx, blockNr) } @@ -242,7 +241,7 @@ func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash, number ui return rawdb.ReadLogs(b.eth.chainDb, hash, number), nil } -func (b *EthAPIBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { +func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state vm.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { if vmConfig == nil { vmConfig = b.eth.blockchain.GetVMConfig() } @@ -415,10 +414,10 @@ func (b *EthAPIBackend) CurrentHeader() *types.Header { return b.eth.blockchain.CurrentHeader() } -func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) { +func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base vm.StateDB, readOnly bool, preferDisk bool) (vm.StateDB, tracers.StateReleaseFunc, error) { return b.eth.stateAtBlock(ctx, block, reexec, base, readOnly, preferDisk) } -func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { +func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, tracers.StateReleaseFunc, error) { return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) } diff --git a/eth/api_debug.go b/eth/api_debug.go index d5e4dda1401c..eb9d04ef51bd 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" @@ -229,13 +230,13 @@ func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockNrOrHash rpc.Block return storageRangeAt(statedb, block.Root(), contractAddress, keyStart, maxResult) } -func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Address, start []byte, maxResult int) (StorageRangeResult, error) { +func storageRangeAt(statedb vm.StateDB, root common.Hash, address common.Address, start []byte, maxResult int) (StorageRangeResult, error) { storageRoot := statedb.GetStorageRoot(address) if storageRoot == types.EmptyRootHash || storageRoot == (common.Hash{}) { return StorageRangeResult{}, nil // empty storage } id := trie.StorageTrieID(root, crypto.Keccak256Hash(address.Bytes()), storageRoot) - tr, err := trie.NewStateTrie(id, statedb.Database().TrieDB()) + tr, err := trie.NewStateTrie(id, statedb.(*state.StateDB).Database().TrieDB()) if err != nil { return StorageRangeResult{}, err } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index a6c4718cf4a2..0b52d09d1d31 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/internal/ethapi/override" @@ -42,7 +41,7 @@ type Options struct { Config *params.ChainConfig // Chain configuration for hard fork selection Chain core.ChainContext // Chain context to access past block hashes Header *types.Header // Header defining the block context to execute in - State *state.StateDB // Pre-state on top of which to estimate the gas + State vm.StateDB // Pre-state on top of which to estimate the gas BlockOverrides *override.BlockOverrides // Block overrides to apply during the estimation ErrorRatio float64 // Allowed overestimation ratio for faster estimation termination @@ -197,9 +196,16 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin // returns true if the transaction fails for a reason that might be related to // not enough gas. A non-nil error means execution failed due to reasons unrelated // to the gas limit. -func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit uint64) (bool, *core.ExecutionResult, error) { +func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit uint64) (failed bool, execResult *core.ExecutionResult, err error) { // Configure the call for this specific execution (and revert the change after) defer func(gas uint64) { call.GasLimit = gas }(call.GasLimit) + defer func() { + if r := recover(); r != nil { + failed = true + execResult = nil + err = nil + } + }() call.GasLimit = gasLimit // Execute the call and separate execution faults caused by a lack of gas or @@ -234,6 +240,7 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio evmContext.BlobBaseFee = new(big.Int) } evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) + dirtyState.SetEVM(evm) // Monitor the outer context and interrupt the EVM upon cancellation. To avoid // a dangling goroutine until the outer estimation finishes, create an internal diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 4fd3df742853..2bbceefad6de 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -25,8 +25,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -54,7 +54,7 @@ type OracleBackend interface { HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) - Pending() (*types.Block, types.Receipts, *state.StateDB) + Pending() (*types.Block, types.Receipts, vm.StateDB) ChainConfig() *params.ChainConfig SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 0a32c278cbf4..f785b5d01366 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -102,7 +101,7 @@ func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types. return b.chain.GetReceiptsByHash(hash), nil } -func (b *testBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) { +func (b *testBackend) Pending() (*types.Block, types.Receipts, vm.StateDB) { if b.pending { block := b.chain.GetBlockByNumber(testHead + 1) state, _ := b.chain.StateAt(block.Root()) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 99ed28d96afc..45601517f52e 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -38,7 +38,7 @@ import ( // for releasing state. var noopReleaser = tracers.StateReleaseFunc(func() {}) -func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (statedb *state.StateDB, release tracers.StateReleaseFunc, err error) { +func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec uint64, base vm.StateDB, readOnly bool, preferDisk bool) (statedb vm.StateDB, release tracers.StateReleaseFunc, err error) { var ( current *types.Block database state.Database @@ -76,7 +76,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u } } // The optional base statedb is given, mark the start point as parent block - statedb, database, tdb, report = base, base.Database(), base.Database().TrieDB(), false + statedb, database, tdb, report = base, base.(*state.StateDB).Database(), base.(*state.StateDB).Database().TrieDB(), false current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) } else { // Otherwise, try to reexec blocks until we find a state or reach our limit @@ -210,7 +210,7 @@ func (eth *Ethereum) pathState(block *types.Block) (*state.StateDB, func(), erro // - preferDisk: This arg can be used by the caller to signal that even though the 'base' is // provided, it would be preferable to start from a fresh state, if we have it // on disk. -func (eth *Ethereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (statedb *state.StateDB, release tracers.StateReleaseFunc, err error) { +func (eth *Ethereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base vm.StateDB, readOnly bool, preferDisk bool) (statedb vm.StateDB, release tracers.StateReleaseFunc, err error) { if eth.blockchain.TrieDB().Scheme() == rawdb.HashScheme { return eth.hashState(ctx, block, reexec, base, readOnly, preferDisk) } @@ -218,7 +218,7 @@ func (eth *Ethereum) stateAtBlock(ctx context.Context, block *types.Block, reexe } // stateAtTransaction returns the execution environment of a certain transaction. -func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { +func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, tracers.StateReleaseFunc, error) { // Short circuit if it's genesis block. if block.NumberU64() == 0 { return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis") diff --git a/eth/tracers/api.go b/eth/tracers/api.go index d13aee555f46..134aad45fd68 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -25,6 +25,7 @@ import ( "math/big" "os" "runtime" + "strings" "sync" "time" @@ -87,8 +88,8 @@ type Backend interface { ChainConfig() *params.ChainConfig Engine() consensus.Engine ChainDb() ethdb.Database - StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error) - StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) + StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base vm.StateDB, readOnly bool, preferDisk bool) (vm.StateDB, StateReleaseFunc, error) + StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, StateReleaseFunc, error) } // API is the collection of tracing APIs exposed over the private debugging endpoint. @@ -176,8 +177,8 @@ type StdTraceConfig struct { TxHash common.Hash } -// txTraceResult is the result of a single transaction trace. -type txTraceResult struct { +// TxTraceResult is the result of a single transaction trace. +type TxTraceResult struct { TxHash common.Hash `json:"txHash"` // transaction hash Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer Error string `json:"error,omitempty"` // Trace failure produced by the tracer @@ -186,10 +187,10 @@ type txTraceResult struct { // blockTraceTask represents a single block trace task when an entire chain is // being traced. type blockTraceTask struct { - statedb *state.StateDB // Intermediate state prepped for tracing + statedb vm.StateDB // Intermediate state prepped for tracing block *types.Block // Block to trace the transactions from release StateReleaseFunc // The function to release the held resource for this task - results []*txTraceResult // Trace results produced by the task + results []*TxTraceResult // Trace results produced by the task } // blockTraceResult represents the results of tracing a single block when an entire @@ -197,14 +198,14 @@ type blockTraceTask struct { type blockTraceResult struct { Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace Hash common.Hash `json:"hash"` // Block hash corresponding to this trace - Traces []*txTraceResult `json:"traces"` // Trace results produced by the task + Traces []*TxTraceResult `json:"traces"` // Trace results produced by the task } // txTraceTask represents a single transaction trace task when an entire block // is being traced. type txTraceTask struct { - statedb *state.StateDB // Intermediate state prepped for tracing - index int // Transaction offset in the block + statedb vm.StateDB // Intermediate state prepped for tracing + index int // Transaction offset in the block } // TraceChain returns the structured logs created during the execution of EVM @@ -281,11 +282,11 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed } res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config) if err != nil { - task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()} + task.results[i] = &TxTraceResult{TxHash: tx.Hash(), Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) break } - task.results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res} + task.results[i] = &TxTraceResult{TxHash: tx.Hash(), Result: res} } // Tracing state is used up, queue it for de-referencing. Note the // state is the parent state of trace block, use block.number-1 as @@ -309,7 +310,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed number uint64 traced uint64 failed error - statedb *state.StateDB + statedb vm.StateDB release StateReleaseFunc ) // Ensure everything is properly cleaned up on any exit path @@ -369,7 +370,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // if the relevant state is available in disk. var preferDisk bool if statedb != nil { - s1, s2, s3 := statedb.Database().TrieDB().Size() + s1, s2, s3 := statedb.(*state.StateDB).Database().TrieDB().Size() preferDisk = s1+s2+s3 > defaultTracechainMemLimit } statedb, release, err = api.backend.StateAtBlock(ctx, block, reexec, statedb, false, preferDisk) @@ -397,7 +398,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // Send the block over to the concurrent tracers (if not in the fast-forward phase) txs := next.Transactions() select { - case taskCh <- &blockTraceTask{statedb: statedb.Copy(), block: next, release: release, results: make([]*txTraceResult, len(txs))}: + case taskCh <- &blockTraceTask{statedb: statedb.Copy(), block: next, release: release, results: make([]*TxTraceResult, len(txs))}: case <-closed: tracker.releaseState(number, release) return @@ -442,7 +443,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // TraceBlockByNumber returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*TxTraceResult, error) { block, err := api.blockByNumber(ctx, number) if err != nil { return nil, err @@ -452,7 +453,7 @@ func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, // TraceBlockByHash returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*TxTraceResult, error) { block, err := api.blockByHash(ctx, hash) if err != nil { return nil, err @@ -462,7 +463,7 @@ func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config * // TraceBlock returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*TxTraceResult, error) { block := new(types.Block) if err := rlp.DecodeBytes(blob, block); err != nil { return nil, fmt.Errorf("could not decode block: %v", err) @@ -472,7 +473,7 @@ func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *Trac // TraceBlockFromFile returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*TxTraceResult, error) { blob, err := os.ReadFile(file) if err != nil { return nil, fmt.Errorf("could not read file: %v", err) @@ -483,7 +484,7 @@ func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *Tra // TraceBadBlock returns the structured logs created during the execution of // EVM against a block pulled from the pool of bad ones and returns them as a JSON // object. -func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*TxTraceResult, error) { block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash) if block == nil { return nil, fmt.Errorf("bad block %#x not found", hash) @@ -580,7 +581,7 @@ func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Has // traceBlock configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item // per transaction, dependent on the requested tracer. -func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*TxTraceResult, error) { if block.NumberU64() == 0 { return nil, errors.New("genesis is not traceable") } @@ -621,7 +622,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac txs = block.Transactions() blockHash = block.Hash() signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) - results = make([]*txTraceResult, len(txs)) + results = make([]*TxTraceResult, len(txs)) ) for i, tx := range txs { // Generate the next state snapshot fast without tracing @@ -634,9 +635,10 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac } res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config) if err != nil { - return nil, err + results[i] = &TxTraceResult{TxHash: tx.Hash(), Error: err.Error()} + } else { + results[i] = &TxTraceResult{TxHash: tx.Hash(), Result: res} } - results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res} } return results, nil } @@ -644,13 +646,13 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac // traceBlockParallel is for tracers that have a high overhead (read JS tracers). One thread // runs along and executes txes without tracing enabled to generate their prestate. // Worker threads take the tasks and the prestate and trace them. -func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, statedb *state.StateDB, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, statedb vm.StateDB, config *TraceConfig) ([]*TxTraceResult, error) { // Execute all the transaction contained within the block concurrently var ( txs = block.Transactions() blockHash = block.Hash() signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) - results = make([]*txTraceResult, len(txs)) + results = make([]*TxTraceResult, len(txs)) pend sync.WaitGroup ) threads := runtime.NumCPU() @@ -678,10 +680,10 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config) if err != nil { - results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()} + results[task.index] = &TxTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()} continue } - results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Result: res} + results[task.index] = &TxTraceResult{TxHash: txs[task.index].Hash(), Result: res} } }() } @@ -857,7 +859,13 @@ func containsTx(block *types.Block, hash common.Hash) bool { // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { +func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (value interface{}, returnErr error) { + defer func() { + if r := recover(); r != nil { + value = nil + returnErr = fmt.Errorf("panic occurred: %v, could not trace tx: %s", r, hash.Hex()) + } + }() found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) if err != nil { return nil, ethapi.NewTxIndexingError() @@ -904,12 +912,18 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * // after executing the specified block. However, if a transaction index is provided, // the trace will be conducted on the state after executing the specified transaction // within the specified block. -func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { +func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (value interface{}, returnErr error) { + defer func() { + if r := recover(); r != nil { + value = nil + returnErr = fmt.Errorf("panic occurred: %v, could not trace tx: %s", r, args.ToTransaction(types.LegacyTxType).Hash().Hex()) + } + }() // Try to retrieve the specified block var ( err error block *types.Block - statedb *state.StateDB + statedb vm.StateDB release StateReleaseFunc ) if hash, ok := blockNrOrHash.Hash(); ok { @@ -983,13 +997,19 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb vm.StateDB, config *TraceConfig) (value interface{}, returnErr error) { var ( tracer *Tracer err error timeout = defaultTraceTimeout usedGas uint64 ) + defer func() { + if r := recover(); r != nil { + value = nil + returnErr = fmt.Errorf("panic occurred: %v, could not trace tx: %s", r, tx.Hash()) + } + }() if config == nil { config = &TraceConfig{} } @@ -1031,6 +1051,10 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm) if err != nil { + // Due to how our mempool works, a transaction with insufficient funds can be included in a block. For tracing purposes, we should ignore this. + if strings.Contains(err.Error(), core.ErrInsufficientFunds.Error()) { + return json.RawMessage(`{}`), nil + } return nil, fmt.Errorf("tracing failed: %w", err) } return tracer.GetResult() diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 95e7739be06b..6ef268f065f7 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -36,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -142,7 +141,7 @@ func (b *testBackend) teardown() { b.chain.Stop() } -func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error) { +func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base vm.StateDB, readOnly bool, preferDisk bool) (vm.StateDB, StateReleaseFunc, error) { statedb, err := b.chain.StateAt(block.Root()) if err != nil { return nil, nil, errStateNotFound @@ -158,7 +157,7 @@ func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reex return statedb, release, nil } -func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) { +func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, StateReleaseFunc, error) { parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { return nil, vm.BlockContext{}, nil, nil, errBlockNotFound @@ -548,6 +547,70 @@ func TestTraceTransaction(t *testing.T) { } } +func TestTracePanicTransaction(t *testing.T) { + t.Parallel() + // Initialize test accounts + accounts := newAccounts(2) + genesis := &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + }, + } + target := common.Hash{} + signer := types.HomesteadSigner{} + backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) + b.AddTx(tx) + target = tx.Hash() + }) + defer backend.chain.Stop() + // Create API with a backend that uses a panic-inducing StateDB + panicBackend := &panicBackend{backend} + api := NewAPI(panicBackend) + result, err := api.TraceTransaction(context.Background(), target, nil) + // Verify panic was caught and handled + if err == nil { + t.Fatal("Expected error from panic recovery, got nil") + } + if result != nil { + t.Errorf("Expected nil result after panic, got %v", result) + } +} + +type panicBackend struct { + Backend +} + +// StateAtTransaction overrides the backend's StateAtTransaction to use a panic-inducing StateDB +func (b *panicBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, StateReleaseFunc, error) { + tx, vmctx, _, release, err := b.Backend.StateAtTransaction(ctx, block, txIndex, reexec) + if err != nil { + return nil, vm.BlockContext{}, nil, nil, err + } + // Return a StateDB that panics on ApplyTransactionWithEVM + return tx, vmctx, &panicStateDB{}, release, nil +} + +// panicStateDB is a mock StateDB that panics during ApplyTransactionWithEVM +type panicStateDB struct { + vm.StateDB +} + +// ApplyTransactionWithEVM is overridden to panic +func (s *panicStateDB) ApplyTransactionWithEVM(message *core.Message, config *params.ChainConfig, gasPool *core.GasPool, statedb vm.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) ([]byte, error) { + panic("intentional panic for testing") +} + func TestTraceBlock(t *testing.T) { t.Parallel() diff --git a/export/ethapi.go b/export/ethapi.go new file mode 100644 index 000000000000..ff8c052408c9 --- /dev/null +++ b/export/ethapi.go @@ -0,0 +1,19 @@ +package export + +import ( + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/internal/ethapi/override" +) + +type RPCTransaction = ethapi.RPCTransaction +type SignTransactionResult = ethapi.SignTransactionResult +type TransactionArgs = ethapi.TransactionArgs +type StateOverride = override.StateOverride +type BlockOverrides = override.BlockOverrides + +var NewRPCTransaction = ethapi.NewRPCTransaction +var AccessList = ethapi.AccessList +var DoEstimateGas = ethapi.DoEstimateGas +var DoEstimateGasAfterCalls = ethapi.DoEstimateGasAfterCalls +var DoCall = ethapi.DoCall +var NewRPCPendingTransaction = ethapi.NewRPCPendingTransaction diff --git a/graphql/graphql.go b/graphql/graphql.go index 7af1adbb4a8e..1e50f52d5ab6 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -31,8 +31,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/rlp" @@ -85,7 +85,7 @@ type Account struct { } // getState fetches the StateDB object for an account. -func (a *Account) getState(ctx context.Context) (*state.StateDB, error) { +func (a *Account) getState(ctx context.Context) (vm.StateDB, error) { state, _, err := a.r.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash) return state, err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c0b37c516b09..d9800d81d7db 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -387,7 +387,7 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address, var storageTrie state.Trie if storageRoot != types.EmptyRootHash && storageRoot != (common.Hash{}) { id := trie.StorageTrieID(header.Root, crypto.Keccak256Hash(address.Bytes()), storageRoot) - st, err := trie.NewStateTrie(id, statedb.Database().TrieDB()) + st, err := trie.NewStateTrie(id, statedb.(*state.StateDB).Database().TrieDB()) if err != nil { return nil, err } @@ -418,7 +418,7 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address, } } // Create the accountProof. - tr, err := trie.NewStateTrie(trie.StateTrieID(header.Root), statedb.Database().TrieDB()) + tr, err := trie.NewStateTrie(trie.StateTrieID(header.Root), statedb.(*state.StateDB).Database().TrieDB()) if err != nil { return nil, err } @@ -657,7 +657,7 @@ func (context *ChainContext) Config() *params.ChainConfig { return context.b.ChainConfig() } -func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { +func doCall(ctx context.Context, b Backend, args TransactionArgs, state vm.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) if blockOverrides != nil { blockOverrides.Apply(&blockCtx) @@ -688,7 +688,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S return applyMessage(ctx, b, args, state, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles, true) } -func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts, skipChecks bool) (*core.ExecutionResult, error) { +func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state vm.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts, skipChecks bool) (*core.ExecutionResult, error) { // Get a new instance of the EVM. if err := args.CallDefaults(gp.Gas(), blockContext.BaseFee, b.ChainConfig().ChainID); err != nil { return nil, err @@ -702,7 +702,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { blockContext.BlobBaseFee = new(big.Int) } - evm := b.GetEVM(ctx, state, header, vmConfig, blockContext) + evm := b.GetEVM(ctx, msg, state, header, vmConfig, blockContext) if precompiles != nil { evm.SetPrecompiles(precompiles) } @@ -850,6 +850,44 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr return hexutil.Uint64(estimate), nil } +func DoEstimateGasAfterCalls(ctx context.Context, b Backend, args TransactionArgs, calls []TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *override.StateOverride, timeout time.Duration, gasCap uint64) (hexutil.Uint64, error) { + state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if state == nil || err != nil { + return 0, err + } + for _, call := range calls { + _, err = doCall(ctx, b, call, state, header, overrides, nil, timeout, gasCap) + if err != nil { + return 0, err + } + overrides = nil + } + // Construct the gas estimator option from the user input + opts := &gasestimator.Options{ + Config: b.ChainConfig(), + Chain: NewChainContext(ctx, b), + Header: header, + State: state, + ErrorRatio: estimateGasErrorRatio, + } + if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil { + return 0, err + } + call := args.ToMessage(header.BaseFee, true, true) + if err != nil { + return 0, err + } + // Run the gas estimation andwrap any revertals into a custom return + estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap) + if err != nil { + if len(revert) > 0 { + return 0, newRevertError(revert) + } + return 0, err + } + return hexutil.Uint64(estimate), nil +} + // EstimateGas returns the lowest possible gas limit that allows the transaction to run // successfully at block `blockNrOrHash`, or the latest block if `blockNrOrHash` is unspecified. It // returns error if the transaction would revert or if there are unexpected failures. The returned @@ -918,7 +956,7 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param } if fullTx { formatTx = func(idx int, tx *types.Transaction) interface{} { - return newRPCTransactionFromBlockIndex(block, uint64(idx), config) + return NewRPCTransactionFromBlockIndex(block, uint64(idx), config) } } txs := block.Transactions() @@ -967,9 +1005,9 @@ type RPCTransaction struct { YParity *hexutil.Uint64 `json:"yParity,omitempty"` } -// newRPCTransaction returns a transaction that will serialize to the RPC +// NewRPCTransaction returns a transaction that will serialize to the RPC // representation, with the given location metadata set (if available). -func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, blockTime uint64, index uint64, baseFee *big.Int, config *params.ChainConfig) *RPCTransaction { +func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, blockTime uint64, index uint64, baseFee *big.Int, config *params.ChainConfig) *RPCTransaction { signer := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) from, _ := types.Sender(signer, tx) v, r, s := tx.RawSignatureValues() @@ -1083,16 +1121,16 @@ func NewRPCPendingTransaction(tx *types.Transaction, current *types.Header, conf blockNumber = current.Number.Uint64() blockTime = current.Time } - return newRPCTransaction(tx, common.Hash{}, blockNumber, blockTime, 0, baseFee, config) + return NewRPCTransaction(tx, common.Hash{}, blockNumber, blockTime, 0, baseFee, config) } -// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *params.ChainConfig) *RPCTransaction { +// NewRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. +func NewRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *params.ChainConfig) *RPCTransaction { txs := b.Transactions() if index >= uint64(len(txs)) { return nil } - return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time(), index, b.BaseFee(), config) + return NewRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time(), index, b.BaseFee(), config) } // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. @@ -1187,7 +1225,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true} - evm := b.GetEVM(ctx, statedb, header, &config, nil) + evm := b.GetEVM(ctx, msg, statedb, header, &config, nil) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). @@ -1244,7 +1282,7 @@ func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, b // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) + return NewRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) } return nil } @@ -1252,7 +1290,7 @@ func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Conte // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) + return NewRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) } return nil } @@ -1310,7 +1348,7 @@ func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common if err != nil { return nil, err } - return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, api.b.ChainConfig()), nil + return NewRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, api.b.ChainConfig()), nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. @@ -1464,7 +1502,7 @@ func (api *TransactionAPI) SendTransaction(ctx context.Context, args Transaction } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, api.b, false); err != nil { + if err := args.SetDefaults(ctx, api.b, false); err != nil { return common.Hash{}, err } // Assemble the transaction and sign with the wallet @@ -1484,7 +1522,7 @@ func (api *TransactionAPI) FillTransaction(ctx context.Context, args Transaction args.blobSidecarAllowed = true // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, api.b, false); err != nil { + if err := args.SetDefaults(ctx, api.b, false); err != nil { return nil, err } // Assemble the transaction and obtain rlp @@ -1552,7 +1590,7 @@ func (api *TransactionAPI) SignTransaction(ctx context.Context, args Transaction if args.Nonce == nil { return nil, errors.New("nonce not specified") } - if err := args.setDefaults(ctx, api.b, false); err != nil { + if err := args.SetDefaults(ctx, api.b, false); err != nil { return nil, err } // Before actually sign the transaction, ensure the transaction fee is reasonable. @@ -1611,7 +1649,7 @@ func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, if sendArgs.Nonce == nil { return common.Hash{}, errors.New("missing transaction nonce in transaction spec") } - if err := sendArgs.setDefaults(ctx, api.b, false); err != nil { + if err := sendArgs.SetDefaults(ctx, api.b, false); err != nil { return common.Hash{}, err } matchTx := sendArgs.ToTransaction(types.LegacyTxType) diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index f00022e3de4f..c19c49b5703d 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -47,7 +47,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -83,7 +82,7 @@ func testTransactionMarshal(t *testing.T, tests []txData, config *params.ChainCo } // rpcTransaction - rpcTx := newRPCTransaction(tx, common.Hash{}, 0, 0, 0, nil, config) + rpcTx := NewRPCTransaction(tx, common.Hash{}, 0, 0, 0, nil, config) if data, err := json.Marshal(rpcTx); err != nil { t.Fatalf("test %d: marshalling failed; %v", i, err) } else if err = tx2.UnmarshalJSON(data); err != nil { @@ -536,7 +535,7 @@ func (b testBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc. func (b testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { return b.chain.GetBlock(hash, uint64(number.Int64())).Body(), nil } -func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { +func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (vm.StateDB, *types.Header, error) { if number == rpc.PendingBlockNumber { panic("pending state not implemented") } @@ -550,13 +549,13 @@ func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.Bloc stateDb, err := b.chain.StateAt(header.Root) return stateDb, header, err } -func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { +func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (vm.StateDB, *types.Header, error) { if blockNr, ok := blockNrOrHash.Number(); ok { return b.StateAndHeaderByNumber(ctx, blockNr) } panic("only implemented for number") } -func (b testBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) { panic("implement me") } +func (b testBackend) Pending() (*types.Block, types.Receipts, vm.StateDB) { panic("implement me") } func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { header, err := b.HeaderByHash(ctx, hash) if header == nil || err != nil { @@ -565,7 +564,7 @@ func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.R receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), header.Time, b.chain.Config()) return receipts, nil } -func (b testBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM { +func (b testBackend) GetEVM(ctx context.Context, msg *core.Message, state vm.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM { if vmConfig == nil { vmConfig = b.chain.GetVMConfig() } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 3d8f7aa700b7..65666eda4166 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" @@ -64,11 +63,11 @@ type Backend interface { BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) - StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) - StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) - Pending() (*types.Block, types.Receipts, *state.StateDB) + StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (vm.StateDB, *types.Header, error) + StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (vm.StateDB, *types.Header, error) + Pending() (*types.Block, types.Receipts, vm.StateDB) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) - GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM + GetEVM(ctx context.Context, msg *core.Message, state vm.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription diff --git a/internal/ethapi/override/override.go b/internal/ethapi/override/override.go index f6a8a94ffdaf..93fb0d9bcced 100644 --- a/internal/ethapi/override/override.go +++ b/internal/ethapi/override/override.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -53,7 +52,7 @@ func (diff *StateOverride) has(address common.Address) bool { } // Apply overrides the fields of specified accounts into the given state. -func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.PrecompiledContracts) error { +func (diff *StateOverride) Apply(statedb vm.StateDB, precompiles vm.PrecompiledContracts) error { if diff == nil { return nil } diff --git a/internal/ethapi/override/override_test.go b/internal/ethapi/override/override_test.go index 02a17c133170..21c9730602db 100644 --- a/internal/ethapi/override/override_test.go +++ b/internal/ethapi/override/override_test.go @@ -18,6 +18,7 @@ package override import ( "maps" + "math/big" "testing" "github.com/ethereum/go-ethereum/common" @@ -33,7 +34,9 @@ type precompileContract struct{} func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 } -func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil } +func (p *precompileContract) Run(_ *vm.EVM, _ common.Address, _ common.Address, input []byte, _ *big.Int, _ bool, _ bool) ([]byte, error) { + return nil, nil +} func TestStateOverrideMovePrecompile(t *testing.T) { db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index c461b1f0a1cb..6c65caf737b7 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -85,7 +85,7 @@ type simOpts struct { // it is not safe for concurrent use. type simulator struct { b Backend - state *state.StateDB + state vm.StateDB base *types.Header chainConfig *params.ChainConfig gp *core.GasPool diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 7a7d63c535ac..30e45d15d0a8 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -94,8 +94,8 @@ func (args *TransactionArgs) data() []byte { return nil } -// setDefaults fills in default values for unspecified tx fields. -func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error { +// SetDefaults fills in default values for unspecified tx fields. +func (args *TransactionArgs) SetDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error { if err := args.setBlobTxSidecar(ctx); err != nil { return err } @@ -413,7 +413,7 @@ func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. -// Assumes that fields are not nil, i.e. setDefaults or CallDefaults has been called. +// Assumes that fields are not nil, i.e. SetDefaults or CallDefaults has been called. func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoACheck bool) *core.Message { var ( gasPrice *big.Int @@ -467,7 +467,7 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoA } // ToTransaction converts the arguments to a transaction. -// This assumes that setDefaults has been called. +// This assumes that SetDefaults has been called. func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { usedType := types.LegacyTxType switch { diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index e017804861f7..03ef6b58e598 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" @@ -357,20 +356,20 @@ func (b *backendMock) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc func (b *backendMock) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { return nil, nil } -func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { +func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (vm.StateDB, *types.Header, error) { return nil, nil, nil } -func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { +func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (vm.StateDB, *types.Header, error) { return nil, nil, nil } -func (b *backendMock) Pending() (*types.Block, types.Receipts, *state.StateDB) { return nil, nil, nil } +func (b *backendMock) Pending() (*types.Block, types.Receipts, vm.StateDB) { return nil, nil, nil } func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { return nil, nil } func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { return nil, nil } -func (b *backendMock) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { +func (b *backendMock) GetEVM(ctx context.Context, msg *core.Message, state vm.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { return nil } func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil } diff --git a/miner/miner.go b/miner/miner.go index 595ef8081c8a..bf6666d5932d 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -96,7 +96,7 @@ func (miner *Miner) Pending() (*types.Block, types.Receipts, *state.StateDB) { if pending == nil { return nil, nil, nil } - return pending.block, pending.receipts, pending.stateDB.Copy() + return pending.block, pending.receipts, pending.stateDB.Copy().(*state.StateDB) } // SetExtra sets the content used to initialize the block extra field. diff --git a/node/database.go b/node/database.go index e3ccb9106678..f54bebf268b4 100644 --- a/node/database.go +++ b/node/database.go @@ -26,9 +26,9 @@ import ( "github.com/ethereum/go-ethereum/log" ) -// openOptions contains the options to apply when opening a database. +// OpenOptions contains the options to apply when opening a database. // OBS: If AncientsDirectory is empty, it indicates that no freezer is to be used. -type openOptions struct { +type OpenOptions struct { Type string // "leveldb" | "pebble" Directory string // the datadir AncientsDirectory string // the ancients-dir @@ -38,12 +38,12 @@ type openOptions struct { ReadOnly bool } -// openDatabase opens both a disk-based key-value database such as leveldb or pebble, but also +// OpenDatabase opens both a disk-based key-value database such as leveldb or pebble, but also // integrates it with a freezer database -- if the AncientDir option has been // set on the provided OpenOptions. // The passed o.AncientDir indicates the path of root ancient directory where // the chain freezer can be opened. -func openDatabase(o openOptions) (ethdb.Database, error) { +func OpenDatabase(o OpenOptions) (ethdb.Database, error) { kvdb, err := openKeyValueDatabase(o) if err != nil { return nil, err @@ -65,7 +65,7 @@ func openDatabase(o openOptions) (ethdb.Database, error) { // +---------------------------------------- // db is non-existent | pebble default | specified type // db is existent | from db | specified type (if compatible) -func openKeyValueDatabase(o openOptions) (ethdb.Database, error) { +func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) { // Reject any unsupported database type if len(o.Type) != 0 && o.Type != rawdb.DBLeveldb && o.Type != rawdb.DBPebble { return nil, fmt.Errorf("unknown db.engine %v", o.Type) diff --git a/node/node.go b/node/node.go index ec7382e72516..3df614e8e8bd 100644 --- a/node/node.go +++ b/node/node.go @@ -711,7 +711,7 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r if n.config.DataDir == "" { db = rawdb.NewMemoryDatabase() } else { - db, err = openDatabase(openOptions{ + db, err = OpenDatabase(OpenOptions{ Type: n.config.DBEngine, Directory: n.ResolvePath(name), Namespace: namespace, @@ -742,7 +742,7 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient if n.config.DataDir == "" { db, err = rawdb.NewDatabaseWithFreezer(memorydb.New(), "", namespace, readonly) } else { - db, err = openDatabase(openOptions{ + db, err = OpenDatabase(OpenOptions{ Type: n.config.DBEngine, Directory: n.ResolvePath(name), AncientsDirectory: n.ResolveAncient(name, ancient), diff --git a/rpc/server.go b/rpc/server.go index 42b59f8f6f7f..33564249820c 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -50,6 +50,7 @@ type Server struct { mutex sync.Mutex codecs map[ServerCodec]struct{} + denyList map[string]struct{} run atomic.Bool batchItemLimit int batchResponseLimit int @@ -62,6 +63,7 @@ func NewServer() *Server { idgen: randomIDGenerator(), codecs: make(map[ServerCodec]struct{}), httpBodyLimit: defaultBodyLimit, + denyList: make(map[string]struct{}), } server.run.Store(true) // Register the default service providing meta information about the RPC service such @@ -97,6 +99,12 @@ func (s *Server) RegisterName(name string, receiver interface{}) error { return s.services.registerName(name, receiver) } +// RegisterDenyList add given method name to the deny list so that RPC requests that matches +// any of the methods in deny list will got rejected directly. +func (s *Server) RegisterDenyList(methodName string) { + s.denyList[methodName] = struct{}{} +} + // ServeCodec reads incoming requests from codec, calls the appropriate callback and writes // the response back using the given codec. It will block until the codec is closed or the // server is stopped. In either case the codec is closed. @@ -159,6 +167,15 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { } return } + // check deny list + for _, req := range reqs { + method := req.Method + if _, found := s.denyList[method]; found { + resp := errorMessage(&methodNotFoundError{method: method}) + codec.writeJSON(ctx, resp, true) + return + } + } if batch { h.handleBatch(reqs) } else { diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 77bf945e40e7..c7691b89e002 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -48,17 +48,17 @@ import ( // A BlockTest checks handling of entire blocks. type BlockTest struct { - json btJSON + Json BtJSON } // UnmarshalJSON implements json.Unmarshaler interface. func (t *BlockTest) UnmarshalJSON(in []byte) error { - return json.Unmarshal(in, &t.json) + return json.Unmarshal(in, &t.Json) } -type btJSON struct { - Blocks []btBlock `json:"blocks"` - Genesis btHeader `json:"genesisBlockHeader"` +type BtJSON struct { + Blocks []BtBlock `json:"blocks"` + Genesis BtHeader `json:"genesisBlockHeader"` Pre types.GenesisAlloc `json:"pre"` Post types.GenesisAlloc `json:"postState"` BestBlock common.UnprefixedHash `json:"lastblockhash"` @@ -66,16 +66,16 @@ type btJSON struct { SealEngine string `json:"sealEngine"` } -type btBlock struct { - BlockHeader *btHeader +type BtBlock struct { + BlockHeader *BtHeader ExpectException string Rlp string - UncleHeaders []*btHeader + UncleHeaders []*BtHeader } //go:generate go run github.com/fjl/gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go -type btHeader struct { +type BtHeader struct { Bloom types.Bloom Coinbase common.Address MixHash common.Hash @@ -99,7 +99,7 @@ type btHeader struct { ParentBeaconBlockRoot *common.Hash } -type btHeaderMarshaling struct { +type BtHeaderMarshaling struct { ExtraData hexutil.Bytes Number *math.HexOrDecimal256 Difficulty *math.HexOrDecimal256 @@ -112,9 +112,9 @@ type btHeaderMarshaling struct { } func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) { - config, ok := Forks[t.json.Network] + config, ok := Forks[t.Json.Network] if !ok { - return UnsupportedForkError{t.json.Network} + return UnsupportedForkError{t.Json.Network} } // import pre accounts & construct test genesis block & state root var ( @@ -142,11 +142,11 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t } triedb.Close() // close the db to prevent memory leak - if gblock.Hash() != t.json.Genesis.Hash { - return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6]) + if gblock.Hash() != t.Json.Genesis.Hash { + return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.Json.Genesis.Hash[:6]) } - if gblock.Root() != t.json.Genesis.StateRoot { - return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) + if gblock.Root() != t.Json.Genesis.StateRoot { + return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.Json.Genesis.StateRoot[:6]) } // Wrap the original engine within the beacon-engine engine := beacon.New(ethash.NewFaker()) @@ -175,8 +175,8 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t defer postCheck(result, chain) } cmlast := chain.CurrentBlock().Hash() - if common.Hash(t.json.BestBlock) != cmlast { - return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast) + if common.Hash(t.Json.BestBlock) != cmlast { + return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.Json.BestBlock, cmlast) } newDB, err := chain.State() if err != nil { @@ -197,19 +197,19 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis { return &core.Genesis{ Config: config, - Nonce: t.json.Genesis.Nonce.Uint64(), - Timestamp: t.json.Genesis.Timestamp, - ParentHash: t.json.Genesis.ParentHash, - ExtraData: t.json.Genesis.ExtraData, - GasLimit: t.json.Genesis.GasLimit, - GasUsed: t.json.Genesis.GasUsed, - Difficulty: t.json.Genesis.Difficulty, - Mixhash: t.json.Genesis.MixHash, - Coinbase: t.json.Genesis.Coinbase, - Alloc: t.json.Pre, - BaseFee: t.json.Genesis.BaseFeePerGas, - BlobGasUsed: t.json.Genesis.BlobGasUsed, - ExcessBlobGas: t.json.Genesis.ExcessBlobGas, + Nonce: t.Json.Genesis.Nonce.Uint64(), + Timestamp: t.Json.Genesis.Timestamp, + ParentHash: t.Json.Genesis.ParentHash, + ExtraData: t.Json.Genesis.ExtraData, + GasLimit: t.Json.Genesis.GasLimit, + GasUsed: t.Json.Genesis.GasUsed, + Difficulty: t.Json.Genesis.Difficulty, + Mixhash: t.Json.Genesis.MixHash, + Coinbase: t.Json.Genesis.Coinbase, + Alloc: t.Json.Pre, + BaseFee: t.Json.Genesis.BaseFeePerGas, + BlobGasUsed: t.Json.Genesis.BlobGasUsed, + ExcessBlobGas: t.Json.Genesis.ExcessBlobGas, } } @@ -226,11 +226,11 @@ See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II expected we are expected to ignore it and continue processing and then validate the post state. */ -func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) { - validBlocks := make([]btBlock, 0) +func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]BtBlock, error) { + validBlocks := make([]BtBlock, 0) // insert the test blocks, which will execute all transactions - for bi, b := range t.json.Blocks { - cb, err := b.decode() + for bi, b := range t.Json.Blocks { + cb, err := b.Decode() if err != nil { if b.BlockHeader == nil { log.Info("Block decoding failed", "index", bi, "err", err) @@ -267,7 +267,7 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) return validBlocks, nil } -func validateHeader(h *btHeader, h2 *types.Header) error { +func validateHeader(h *BtHeader, h2 *types.Header) error { if h.Bloom != h2.Bloom { return fmt.Errorf("bloom: want: %x have: %x", h.Bloom, h2.Bloom) } @@ -333,7 +333,7 @@ func validateHeader(h *btHeader, h2 *types.Header) error { func (t *BlockTest) validatePostState(statedb *state.StateDB) error { // validate post state accounts in test file against what we have in state db - for addr, acct := range t.json.Post { + for addr, acct := range t.Json.Post { // address is indirectly verified by the other fields, as it's the db key code2 := statedb.GetCode(addr) balance2 := statedb.GetBalance(addr).ToBig() @@ -357,9 +357,9 @@ func (t *BlockTest) validatePostState(statedb *state.StateDB) error { return nil } -func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error { +func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []BtBlock) error { // to get constant lookup when verifying block headers by hash (some tests have many blocks) - bmap := make(map[common.Hash]btBlock, len(t.json.Blocks)) + bmap := make(map[common.Hash]BtBlock, len(t.Json.Blocks)) for _, b := range validBlocks { bmap[b.BlockHeader.Hash] = b } @@ -376,7 +376,7 @@ func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []b return nil } -func (bb *btBlock) decode() (*types.Block, error) { +func (bb *BtBlock) Decode() (*types.Block, error) { data, err := hexutil.Decode(bb.Rlp) if err != nil { return nil, err diff --git a/tests/fuzzers/bls12381/precompile_fuzzer.go b/tests/fuzzers/bls12381/precompile_fuzzer.go index 33b2ca798874..b7cc19863d90 100644 --- a/tests/fuzzers/bls12381/precompile_fuzzer.go +++ b/tests/fuzzers/bls12381/precompile_fuzzer.go @@ -76,7 +76,7 @@ func fuzz(id byte, data []byte) int { } cpy := make([]byte, len(data)) copy(cpy, data) - _, err := precompile.Run(cpy) + _, err := precompile.Run(nil, common.Address{}, common.Address{}, cpy, nil, false, false) if !bytes.Equal(cpy, data) { panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy)) } diff --git a/tests/gen_btheader.go b/tests/gen_btheader.go index 80ad89e03bf5..5434cb7bfe02 100644 --- a/tests/gen_btheader.go +++ b/tests/gen_btheader.go @@ -12,10 +12,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -var _ = (*btHeaderMarshaling)(nil) +var _ = (*BtHeaderMarshaling)(nil) // MarshalJSON marshals as JSON. -func (b btHeader) MarshalJSON() ([]byte, error) { +func (b BtHeader) MarshalJSON() ([]byte, error) { type btHeader struct { Bloom types.Bloom Coinbase common.Address @@ -65,7 +65,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) { } // UnmarshalJSON unmarshals from JSON. -func (b *btHeader) UnmarshalJSON(input []byte) error { +func (b *BtHeader) UnmarshalJSON(input []byte) error { type btHeader struct { Bloom *types.Bloom Coinbase *common.Address