diff --git a/core/state/statedb.go b/core/state/statedb.go index a59de16a70..3cd70266eb 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1375,6 +1375,99 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre return s.accessList.Contains(addr, slot) } +// +func (s *StateDB) GetCurrentLogs() []*types.Log { + return s.logs[s.thash] +} + +func (s *StateDB) GetCurrentAccessListForProof() (map[common.Address]int, []map[common.Hash]struct{}) { + return s.accessList.addresses, s.accessList.slots +} + +func (s *StateDB) GetStateRootForProof(addr common.Address) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return common.Hash{} + } + return stateObject.data.Root +} + +func (s *StateDB) GetRootForProof() common.Hash { + return s.trie.Hash() +} + +func (s *StateDB) CommitForProof() { + addressesToPrefetch := make([][]byte, 0, len(s.journal.dirties)) + for addr := range s.journal.dirties { + obj, exist := s.stateObjects[addr] + if !exist { + continue + } + if obj.empty() { // rollup-specific: we ignore suicided accounts here + obj.deleted = true + + // We need to maintain account deletions explicitly (will remain + // set indefinitely). Note only the first occurred self-destruct + // event is tracked. + if _, ok := s.stateObjectsDestruct[obj.address]; !ok { + s.stateObjectsDestruct[obj.address] = obj.origin + } + // Note, we can't do this only at the end of a block because multiple + // transactions within the same block might self destruct and then + // resurrect an account; but the snapshotter needs both events. + delete(s.accounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect) + delete(s.storages, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect) + delete(s.accountsOrigin, obj.address) // Clear out any previously updated account data (may be recreated via a resurrect) + delete(s.storagesOrigin, obj.address) // Clear out any previously updated storage data (may be recreated via a resurrect) + } else { + obj.finalise(true) // Prefetch slots in the background + } + s.stateObjectsPending[addr] = struct{}{} + s.stateObjectsDirty[addr] = struct{}{} + addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure + } + if s.prefetcher != nil && len(addressesToPrefetch) > 0 { + s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch) + } + for addr := range s.stateObjectsPending { + if obj := s.stateObjects[addr]; !obj.deleted { + obj.updateRoot() + } + } + if s.prefetcher != nil { + if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie != nil { + s.trie = trie + } + } + for addr := range s.stateObjectsPending { + if obj := s.stateObjects[addr]; obj.deleted { + s.deleteStateObject(obj) + } else { + s.updateStateObject(obj) + } + } + if len(s.stateObjectsPending) > 0 { + s.stateObjectsPending = make(map[common.Address]struct{}) + } +} + +func (s *StateDB) DeleteSuicidedAccountForProof(addr common.Address) { + obj, exist := s.stateObjects[addr] + if !exist { + return + } + obj.deleted = true + if s.prefetcher != nil { + s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, [][]byte{common.CopyBytes(addr[:])}) + if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie != nil { + s.trie = trie + } + } + s.deleteStateObject(obj) +} + +// + // convertAccountSet converts a provided account set from address keyed to hash keyed. func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount) map[common.Hash]struct{} { ret := make(map[common.Hash]struct{}, len(set)) diff --git a/core/state_transition.go b/core/state_transition.go index f84757be78..d31a2975b0 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -146,6 +146,65 @@ type Message struct { SkipAccountChecks bool } +// + +// The vm package doesn't have access to the Message type defined above. +// Getters are defined for EVMHook which uses MessageInterface instead. + +func (m *Message) GetTo() *common.Address { + return m.To +} + +func (m *Message) GetFrom() common.Address { + return m.From +} + +func (m *Message) GetNonce() uint64 { + return m.Nonce +} + +func (m *Message) GetValue() *big.Int { + return m.Value +} + +func (m *Message) GetGasLimit() uint64 { + return m.GasLimit +} + +func (m *Message) GetGasPrice() *big.Int { + return m.GasPrice +} + +func (m *Message) GetGasFeeCap() *big.Int { + return m.GasFeeCap +} + +func (m *Message) GetGasTipCap() *big.Int { + return m.GasTipCap +} + +func (m *Message) GetData() []byte { + return m.Data +} + +func (m *Message) GetAccessList() types.AccessList { + return m.AccessList +} + +func (m *Message) GetBlobGasFeeCap() *big.Int { + return m.BlobGasFeeCap +} + +func (m *Message) GetBlobHashes() []common.Hash { + return m.BlobHashes +} + +func (m *Message) GetSkipAccountChecks() bool { + return m.SkipAccountChecks +} + +// + // TransactionToMessage converts a transaction into a Message. func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { msg := &Message{ @@ -389,6 +448,15 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } st.gasRemaining -= gas + // + if st.evm.Config.SpecularEVMPreTransferHook != nil { + err = st.evm.Config.SpecularEVMPreTransferHook(st.msg, st.evm) + if err != nil { + return nil, err + } + } + // + // Check clause 6 if msg.Value.Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From, msg.Value) { return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex()) diff --git a/core/vm/interface.go b/core/vm/interface.go index 26814d3d2f..ba53e50120 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,6 +20,7 @@ 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/params" ) @@ -78,6 +79,17 @@ type StateDB interface { AddLog(*types.Log) AddPreimage(common.Hash, []byte) + + // + Copy() *state.StateDB + GetCurrentLogs() []*types.Log + GetCurrentAccessListForProof() (map[common.Address]int, []map[common.Hash]struct{}) + GetStateRootForProof(common.Address) common.Hash + GetRootForProof() common.Hash + GetStorageRoot(addr common.Address) common.Hash + CommitForProof() + DeleteSuicidedAccountForProof(addr common.Address) + // } // 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 873337850e..ecfbd9c0d0 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -17,18 +17,46 @@ package vm import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" ) +// +type MessageInterface interface { + GetTo() *common.Address + GetFrom() common.Address + GetNonce() uint64 + GetValue() *big.Int + GetGasLimit() uint64 + GetGasPrice() *big.Int + GetGasFeeCap() *big.Int + GetGasTipCap() *big.Int + GetData() []byte + GetAccessList() types.AccessList + GetBlobGasFeeCap() *big.Int + GetBlobHashes() []common.Hash + GetSkipAccountChecks() bool +} + +type EVMHook func(msg MessageInterface, evm *EVM) error + +// + // Config are the configuration options for the Interpreter type Config struct { Tracer EVMLogger // Opcode logger NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages ExtraEips []int // Additional EIPS that are to be enabled + + // + SpecularEVMPreTransferHook EVMHook + // } // ScopeContext contains the things that are per-call, such as stack and memory,