Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e9c59d8
core/vm: implement AccessListTracer
MariusVanDerWijden Mar 22, 2021
f22ae38
eth: implement debug.createAccessList
MariusVanDerWijden Mar 23, 2021
55e608d
core/vm: fixed nil panics in accessListTracer
MariusVanDerWijden Mar 24, 2021
dc9a7b1
eth: better error messages for createAccessList
MariusVanDerWijden Mar 24, 2021
aa2813f
eth: some fixes on CreateAccessList
holiman Mar 25, 2021
09b7653
eth: allow for provided accesslists
MariusVanDerWijden Mar 25, 2021
e90ef62
eth: pass accesslist by value
MariusVanDerWijden Mar 25, 2021
e48e1ec
eth: remove created acocunt from accesslist
MariusVanDerWijden Mar 26, 2021
c920fc4
core/vm: simplify access list tracer
holiman Mar 26, 2021
23060c3
core/vm: unexport accessListTracer
holiman Mar 26, 2021
87b2aec
eth: return best guess if al iteration times out
MariusVanDerWijden Mar 29, 2021
9a96fd4
eth: return best guess if al iteration times out
MariusVanDerWijden Mar 29, 2021
b47f14a
core: docstring, unexport methods
MariusVanDerWijden Mar 30, 2021
97b43d3
eth: typo
MariusVanDerWijden Mar 31, 2021
c058cc8
internal/ethapi: move createAccessList to eth package
MariusVanDerWijden Apr 1, 2021
83754ef
internal/ethapi: remove reexec from createAccessList
MariusVanDerWijden Apr 1, 2021
9a8a9ea
internal/ethapi: break if al is equal to last run, not if gas is equal
MariusVanDerWijden Apr 1, 2021
2521654
internal/web3ext: fixed arguments
MariusVanDerWijden Apr 1, 2021
dd52c47
core/types: fixed equality check for accesslist
MariusVanDerWijden Apr 6, 2021
ee6a32c
core/types: no hardcoded vals
MariusVanDerWijden Apr 6, 2021
05f6399
core, internal: simplify access list generation, make it precise
karalabe Apr 7, 2021
a3f7528
core/vm: fix typo
karalabe Apr 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}

// Set up the initial access list.
if st.evm.ChainConfig().IsBerlin(st.evm.Context.BlockNumber) {
st.state.PrepareAccessList(msg.From(), msg.To(), st.evm.ActivePrecompiles(), msg.AccessList())
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin {
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
}

var (
ret []byte
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
Expand Down
177 changes: 177 additions & 0 deletions core/vm/access_list_tracer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package vm

import (
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

// accessList is an accumulator for the set of accounts and storage slots an EVM
// contract execution touches.
type accessList map[common.Address]accessListSlots

// accessListSlots is an accumulator for the set of storage slots within a single
// contract that an EVM contract execution touches.
type accessListSlots map[common.Hash]struct{}

// newAccessList creates a new accessList.
func newAccessList() accessList {
return make(map[common.Address]accessListSlots)
}

// addAddress adds an address to the accesslist.
func (al accessList) addAddress(address common.Address) {
// Set address if not previously present
if _, present := al[address]; !present {
al[address] = make(map[common.Hash]struct{})
}
}

// addSlot adds a storage slot to the accesslist.
func (al accessList) addSlot(address common.Address, slot common.Hash) {
// Set address if not previously present
al.addAddress(address)

// Set the slot on the surely existent storage set
al[address][slot] = struct{}{}
}

// equal checks if the content of the current access list is the same as the
// content of the other one.
func (al accessList) equal(other accessList) bool {
// Cross reference the accounts first
if len(al) != len(other) {
return false
}
for addr := range al {
if _, ok := other[addr]; !ok {
return false
}
}
for addr := range other {
if _, ok := al[addr]; !ok {
return false
}
}
// Accounts match, cross reference the storage slots too
for addr, slots := range al {
otherslots := other[addr]

if len(slots) != len(otherslots) {
return false
}
for hash := range slots {
if _, ok := otherslots[hash]; !ok {
return false
}
}
for hash := range otherslots {
if _, ok := slots[hash]; !ok {
return false
}
}
}
return true
}

// accesslist converts the accesslist to a types.AccessList.
func (al accessList) accessList() types.AccessList {
acl := make(types.AccessList, 0, len(al))
for addr, slots := range al {
tuple := types.AccessTuple{Address: addr}
for slot := range slots {
tuple.StorageKeys = append(tuple.StorageKeys, slot)
}
acl = append(acl, tuple)
}
return acl
}

// AccessListTracer is a tracer that accumulates touched accounts and storage
// slots into an internal set.
type AccessListTracer struct {
excl map[common.Address]struct{} // Set of account to exclude from the list
list accessList // Set of accounts and storage slots touched
}

// NewAccessListTracer creates a new tracer that can generate AccessLists.
// An optional AccessList can be specified to occupy slots and addresses in
// the resulting accesslist.
func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer {
excl := map[common.Address]struct{}{
from: {}, to: {},
}
for _, addr := range precompiles {
excl[addr] = struct{}{}
}
list := newAccessList()
for _, al := range acl {
if _, ok := excl[al.Address]; !ok {
list.addAddress(al.Address)
}
for _, slot := range al.StorageKeys {
list.addSlot(al.Address, slot)
}
}
return &AccessListTracer{
excl: excl,
list: list,
}
}

func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}

// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
stack := scope.Stack
if (op == SLOAD || op == SSTORE) && stack.len() >= 1 {
slot := common.Hash(stack.data[stack.len()-1].Bytes32())
a.list.addSlot(scope.Contract.Address(), slot)
}
if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.len() >= 1 {
addr := common.Address(stack.data[stack.len()-1].Bytes20())
if _, ok := a.excl[addr]; !ok {
a.list.addAddress(addr)
}
}
if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.len() >= 5 {
addr := common.Address(stack.data[stack.len()-2].Bytes20())
if _, ok := a.excl[addr]; !ok {
a.list.addAddress(addr)
}
}
}

func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
}

func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}

// AccessList returns the current accesslist maintained by the tracer.
func (a *AccessListTracer) AccessList() types.AccessList {
return a.list.accessList()
}

// Equal returns if the content of two access list traces are equal.
func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
return a.list.equal(other.list)
}
14 changes: 14 additions & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ func init() {
}
}

// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsBerlin:
return PrecompiledAddressesBerlin
case rules.IsIstanbul:
return PrecompiledAddressesIstanbul
case rules.IsByzantium:
return PrecompiledAddressesByzantium
default:
return PrecompiledAddressesHomestead
}
}

// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
// It returns
// - the returned bytes,
Expand Down
15 changes: 0 additions & 15 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,6 @@ type (
GetHashFunc func(uint64) common.Hash
)

// ActivePrecompiles returns the addresses of the precompiles enabled with the current
// configuration
func (evm *EVM) ActivePrecompiles() []common.Address {
switch {
case evm.chainRules.IsBerlin:
return PrecompiledAddressesBerlin
case evm.chainRules.IsIstanbul:
return PrecompiledAddressesIstanbul
case evm.chainRules.IsByzantium:
return PrecompiledAddressesByzantium
default:
return PrecompiledAddressesHomestead
}
}

func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
switch {
Expand Down
16 changes: 7 additions & 9 deletions core/vm/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
)
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
cfg.State.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
}
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
Expand Down Expand Up @@ -146,10 +146,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
)
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
cfg.State.PrepareAccessList(cfg.Origin, nil, vmenv.ActivePrecompiles(), nil)
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
}

// Call the code with the given configuration.
code, address, leftOverGas, err := vmenv.Create(
sender,
Expand All @@ -172,10 +171,10 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er

sender := cfg.State.GetOrNewStateObject(cfg.Origin)
statedb := cfg.State
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
statedb.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
}

if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
}
// Call the code with the given configuration.
ret, leftOverGas, err := vmenv.Call(
sender,
Expand All @@ -184,6 +183,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
cfg.GasLimit,
cfg.Value,
)

return ret, leftOverGas, err
}
8 changes: 5 additions & 3 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,14 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(hash)
}

func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
vmError := func() error { return nil }

if vmConfig == nil {
vmConfig = b.eth.blockchain.GetVMConfig()
}
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *b.eth.blockchain.GetVMConfig()), vmError, nil
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), vmError, nil
}

func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
Expand Down
Loading