Skip to content

Commit 56f25ba

Browse files
MariusVanDerWijdenholimankaralabe
authored andcommitted
core, eth, internal/ethapi: create access list RPC API (ethereum#22550)
* core/vm: implement AccessListTracer * eth: implement debug.createAccessList * core/vm: fixed nil panics in accessListTracer * eth: better error messages for createAccessList * eth: some fixes on CreateAccessList * eth: allow for provided accesslists * eth: pass accesslist by value * eth: remove created acocunt from accesslist * core/vm: simplify access list tracer * core/vm: unexport accessListTracer * eth: return best guess if al iteration times out * eth: return best guess if al iteration times out * core: docstring, unexport methods * eth: typo * internal/ethapi: move createAccessList to eth package * internal/ethapi: remove reexec from createAccessList * internal/ethapi: break if al is equal to last run, not if gas is equal * internal/web3ext: fixed arguments * core/types: fixed equality check for accesslist * core/types: no hardcoded vals * core, internal: simplify access list generation, make it precise * core/vm: fix typo Co-authored-by: Martin Holst Swende <[email protected]> Co-authored-by: Péter Szilágyi <[email protected]>
1 parent 60a5cfa commit 56f25ba

File tree

10 files changed

+318
-36
lines changed

10 files changed

+318
-36
lines changed

core/state_transition.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
259259
}
260260

261261
// Set up the initial access list.
262-
if st.evm.ChainConfig().IsBerlin(st.evm.Context.BlockNumber) {
263-
st.state.PrepareAccessList(msg.From(), msg.To(), st.evm.ActivePrecompiles(), msg.AccessList())
262+
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin {
263+
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
264264
}
265-
266265
var (
267266
ret []byte
268267
vmerr error // vm errors do not effect consensus and are therefore not assigned to err

core/vm/access_list_tracer.go

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2021 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package vm
18+
19+
import (
20+
"math/big"
21+
"time"
22+
23+
"github.com/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/core/types"
25+
)
26+
27+
// accessList is an accumulator for the set of accounts and storage slots an EVM
28+
// contract execution touches.
29+
type accessList map[common.Address]accessListSlots
30+
31+
// accessListSlots is an accumulator for the set of storage slots within a single
32+
// contract that an EVM contract execution touches.
33+
type accessListSlots map[common.Hash]struct{}
34+
35+
// newAccessList creates a new accessList.
36+
func newAccessList() accessList {
37+
return make(map[common.Address]accessListSlots)
38+
}
39+
40+
// addAddress adds an address to the accesslist.
41+
func (al accessList) addAddress(address common.Address) {
42+
// Set address if not previously present
43+
if _, present := al[address]; !present {
44+
al[address] = make(map[common.Hash]struct{})
45+
}
46+
}
47+
48+
// addSlot adds a storage slot to the accesslist.
49+
func (al accessList) addSlot(address common.Address, slot common.Hash) {
50+
// Set address if not previously present
51+
al.addAddress(address)
52+
53+
// Set the slot on the surely existent storage set
54+
al[address][slot] = struct{}{}
55+
}
56+
57+
// equal checks if the content of the current access list is the same as the
58+
// content of the other one.
59+
func (al accessList) equal(other accessList) bool {
60+
// Cross reference the accounts first
61+
if len(al) != len(other) {
62+
return false
63+
}
64+
for addr := range al {
65+
if _, ok := other[addr]; !ok {
66+
return false
67+
}
68+
}
69+
for addr := range other {
70+
if _, ok := al[addr]; !ok {
71+
return false
72+
}
73+
}
74+
// Accounts match, cross reference the storage slots too
75+
for addr, slots := range al {
76+
otherslots := other[addr]
77+
78+
if len(slots) != len(otherslots) {
79+
return false
80+
}
81+
for hash := range slots {
82+
if _, ok := otherslots[hash]; !ok {
83+
return false
84+
}
85+
}
86+
for hash := range otherslots {
87+
if _, ok := slots[hash]; !ok {
88+
return false
89+
}
90+
}
91+
}
92+
return true
93+
}
94+
95+
// accesslist converts the accesslist to a types.AccessList.
96+
func (al accessList) accessList() types.AccessList {
97+
acl := make(types.AccessList, 0, len(al))
98+
for addr, slots := range al {
99+
tuple := types.AccessTuple{Address: addr}
100+
for slot := range slots {
101+
tuple.StorageKeys = append(tuple.StorageKeys, slot)
102+
}
103+
acl = append(acl, tuple)
104+
}
105+
return acl
106+
}
107+
108+
// AccessListTracer is a tracer that accumulates touched accounts and storage
109+
// slots into an internal set.
110+
type AccessListTracer struct {
111+
excl map[common.Address]struct{} // Set of account to exclude from the list
112+
list accessList // Set of accounts and storage slots touched
113+
}
114+
115+
// NewAccessListTracer creates a new tracer that can generate AccessLists.
116+
// An optional AccessList can be specified to occupy slots and addresses in
117+
// the resulting accesslist.
118+
func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer {
119+
excl := map[common.Address]struct{}{
120+
from: {}, to: {},
121+
}
122+
for _, addr := range precompiles {
123+
excl[addr] = struct{}{}
124+
}
125+
list := newAccessList()
126+
for _, al := range acl {
127+
if _, ok := excl[al.Address]; !ok {
128+
list.addAddress(al.Address)
129+
}
130+
for _, slot := range al.StorageKeys {
131+
list.addSlot(al.Address, slot)
132+
}
133+
}
134+
return &AccessListTracer{
135+
excl: excl,
136+
list: list,
137+
}
138+
}
139+
140+
func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
141+
}
142+
143+
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
144+
func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
145+
stack := scope.Stack
146+
if (op == SLOAD || op == SSTORE) && stack.len() >= 1 {
147+
slot := common.Hash(stack.data[stack.len()-1].Bytes32())
148+
a.list.addSlot(scope.Contract.Address(), slot)
149+
}
150+
if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.len() >= 1 {
151+
addr := common.Address(stack.data[stack.len()-1].Bytes20())
152+
if _, ok := a.excl[addr]; !ok {
153+
a.list.addAddress(addr)
154+
}
155+
}
156+
if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.len() >= 5 {
157+
addr := common.Address(stack.data[stack.len()-2].Bytes20())
158+
if _, ok := a.excl[addr]; !ok {
159+
a.list.addAddress(addr)
160+
}
161+
}
162+
}
163+
164+
func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
165+
}
166+
167+
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
168+
169+
// AccessList returns the current accesslist maintained by the tracer.
170+
func (a *AccessListTracer) AccessList() types.AccessList {
171+
return a.list.accessList()
172+
}
173+
174+
// Equal returns if the content of two access list traces are equal.
175+
func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
176+
return a.list.equal(other.list)
177+
}

core/vm/contracts.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,20 @@ func init() {
128128
}
129129
}
130130

131+
// ActivePrecompiles returns the precompiles enabled with the current configuration.
132+
func ActivePrecompiles(rules params.Rules) []common.Address {
133+
switch {
134+
case rules.IsBerlin:
135+
return PrecompiledAddressesBerlin
136+
case rules.IsIstanbul:
137+
return PrecompiledAddressesIstanbul
138+
case rules.IsByzantium:
139+
return PrecompiledAddressesByzantium
140+
default:
141+
return PrecompiledAddressesHomestead
142+
}
143+
}
144+
131145
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
132146
// It returns
133147
// - the returned bytes,

core/vm/evm.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,6 @@ type (
4242
GetHashFunc func(uint64) common.Hash
4343
)
4444

45-
// ActivePrecompiles returns the addresses of the precompiles enabled with the current
46-
// configuration
47-
func (evm *EVM) ActivePrecompiles() []common.Address {
48-
switch {
49-
case evm.chainRules.IsBerlin:
50-
return PrecompiledAddressesBerlin
51-
case evm.chainRules.IsIstanbul:
52-
return PrecompiledAddressesIstanbul
53-
case evm.chainRules.IsByzantium:
54-
return PrecompiledAddressesByzantium
55-
default:
56-
return PrecompiledAddressesHomestead
57-
}
58-
}
59-
6045
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
6146
var precompiles map[common.Address]PrecompiledContract
6247
switch {

core/vm/runtime/runtime.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
114114
vmenv = NewEnv(cfg)
115115
sender = vm.AccountRef(cfg.Origin)
116116
)
117-
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
118-
cfg.State.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
117+
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
118+
cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
119119
}
120120
cfg.State.CreateAccount(address)
121121
// set the receiver's (the executing contract) code for execution.
@@ -146,10 +146,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
146146
vmenv = NewEnv(cfg)
147147
sender = vm.AccountRef(cfg.Origin)
148148
)
149-
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
150-
cfg.State.PrepareAccessList(cfg.Origin, nil, vmenv.ActivePrecompiles(), nil)
149+
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
150+
cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
151151
}
152-
153152
// Call the code with the given configuration.
154153
code, address, leftOverGas, err := vmenv.Create(
155154
sender,
@@ -172,10 +171,10 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
172171

173172
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
174173
statedb := cfg.State
175-
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
176-
statedb.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
177-
}
178174

175+
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
176+
statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
177+
}
179178
// Call the code with the given configuration.
180179
ret, leftOverGas, err := vmenv.Call(
181180
sender,
@@ -184,6 +183,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
184183
cfg.GasLimit,
185184
cfg.Value,
186185
)
187-
188186
return ret, leftOverGas, err
189187
}

eth/api_backend.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,14 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
196196
return b.eth.blockchain.GetTdByHash(hash)
197197
}
198198

199-
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
199+
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
200200
vmError := func() error { return nil }
201-
201+
if vmConfig == nil {
202+
vmConfig = b.eth.blockchain.GetVMConfig()
203+
}
202204
txContext := core.NewEVMTxContext(msg)
203205
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
204-
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *b.eth.blockchain.GetVMConfig()), vmError, nil
206+
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), vmError, nil
205207
}
206208

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

0 commit comments

Comments
 (0)