Skip to content

Commit 7105d78

Browse files
committed
Merge branch 'libevm' into arr4n/jumptable-override
2 parents 83002ea + ab357e0 commit 7105d78

File tree

12 files changed

+488
-72
lines changed

12 files changed

+488
-72
lines changed

.github/pull_request_template.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Why this should be merged
2+
3+
## How this works
4+
5+
## How this was tested

.github/workflows/libevm-delta.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: libevm delta
2+
3+
on:
4+
push:
5+
branches: [ libevm ]
6+
pull_request:
7+
branches: [ libevm ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
diffs:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0 # everything
17+
fetch-tags: true
18+
19+
- name: Color-blindness a11y
20+
run: | # https://davidmathlogic.com/colorblind/#%23D81B60-%231E88E5-%23FFC107-%23004D40:~:text=8%20pairs%20of%20contrasting%20colors
21+
git config color.diff.old "#DC3220";
22+
git config color.diff.new "#005AB5";
23+
24+
- name: git diff libevm-base
25+
run: |
26+
git diff --diff-filter=a --word-diff --unified=0 --color=always \
27+
libevm-base \
28+
':(exclude).golangci.yml' \
29+
':(exclude).github/**';
30+
31+
- name: git diff libevm-base..libevm
32+
run: |
33+
git checkout libevm --;
34+
git diff --diff-filter=a --word-diff --unified=0 --color=always \
35+
libevm-base \
36+
':(exclude).golangci.yml' \
37+
':(exclude).github/**';
38+

core/evm.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
7373
BlobBaseFee: blobBaseFee,
7474
GasLimit: header.GasLimit,
7575
Random: random,
76+
Header: header,
7677
}
7778
}
7879

core/vm/contracts.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,7 @@ func (args *evmCallArgs) RunPrecompiledContract(p PrecompiledContract, input []b
174174
return nil, 0, ErrOutOfGas
175175
}
176176
suppliedGas -= gasCost
177-
output, err := args.run(p, input)
178-
return output, suppliedGas, err
177+
return args.run(p, input, suppliedGas)
179178
}
180179

181180
// ECRECOVER implemented as a native contract.

core/vm/contracts.libevm.go

Lines changed: 130 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,73 +2,180 @@ package vm
22

33
import (
44
"fmt"
5+
"math/big"
56

67
"github.com/holiman/uint256"
78

89
"github.com/ethereum/go-ethereum/common"
10+
"github.com/ethereum/go-ethereum/core/types"
11+
"github.com/ethereum/go-ethereum/libevm"
912
"github.com/ethereum/go-ethereum/params"
1013
)
1114

1215
// evmCallArgs mirrors the parameters of the [EVM] methods Call(), CallCode(),
1316
// DelegateCall() and StaticCall(). Its fields are identical to those of the
14-
// parameters, prepended with the receiver name. As {Delegate,Static}Call don't
15-
// accept a value, they MUST set the respective field to nil.
17+
// parameters, prepended with the receiver name and appended with additional
18+
// values. As {Delegate,Static}Call don't accept a value, they MUST set the
19+
// respective field to nil.
1620
//
1721
// Instantiation can be achieved by merely copying the parameter names, in
1822
// order, which is trivially achieved with AST manipulation:
1923
//
2024
// func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) ... {
21-
// ...
22-
// args := &evmCallArgs{evm, caller, addr, input, gas, value}
25+
// ...
26+
// args := &evmCallArgs{evm, caller, addr, input, gas, value, false}
2327
type evmCallArgs struct {
24-
evm *EVM
28+
evm *EVM
29+
// args:start
2530
caller ContractRef
2631
addr common.Address
2732
input []byte
2833
gas uint64
2934
value *uint256.Int
35+
// args:end
36+
37+
// evm.interpreter.readOnly is only set to true via a call to
38+
// EVMInterpreter.Run() so, if a precompile is called directly with
39+
// StaticCall(), then readOnly might not be set yet. StaticCall() MUST set
40+
// this to forceReadOnly and all other methods MUST set it to
41+
// inheritReadOnly; i.e. equivalent to the boolean they each pass to
42+
// EVMInterpreter.Run().
43+
readWrite rwInheritance
3044
}
3145

46+
type rwInheritance uint8
47+
48+
const (
49+
inheritReadOnly rwInheritance = iota + 1
50+
forceReadOnly
51+
)
52+
3253
// run runs the [PrecompiledContract], differentiating between stateful and
3354
// regular types.
34-
func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, err error) {
55+
func (args *evmCallArgs) run(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
3556
if p, ok := p.(statefulPrecompile); ok {
36-
return p.run(args.evm.StateDB, &args.evm.chainRules, args.caller.Address(), args.addr, input)
57+
return p(args, input, suppliedGas)
3758
}
38-
return p.Run(input)
59+
// Gas consumption for regular precompiles was already handled by the native
60+
// RunPrecompiledContract(), which called this method.
61+
ret, err = p.Run(input)
62+
return ret, suppliedGas, err
3963
}
4064

41-
// PrecompiledStatefulRun is the stateful equivalent of the Run() method of a
65+
// PrecompiledStatefulContract is the stateful equivalent of a
4266
// [PrecompiledContract].
43-
type PrecompiledStatefulRun func(_ StateDB, _ *params.Rules, caller, self common.Address, input []byte) ([]byte, error)
67+
type PrecompiledStatefulContract func(env PrecompileEnvironment, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error)
4468

4569
// NewStatefulPrecompile constructs a new PrecompiledContract that can be used
4670
// via an [EVM] instance but MUST NOT be called directly; a direct call to Run()
4771
// reserves the right to panic. See other requirements defined in the comments
4872
// on [PrecompiledContract].
49-
func NewStatefulPrecompile(run PrecompiledStatefulRun, requiredGas func([]byte) uint64) PrecompiledContract {
50-
return statefulPrecompile{
51-
gas: requiredGas,
52-
run: run,
53-
}
73+
func NewStatefulPrecompile(run PrecompiledStatefulContract) PrecompiledContract {
74+
return statefulPrecompile(run)
5475
}
5576

56-
type statefulPrecompile struct {
57-
gas func([]byte) uint64
58-
run PrecompiledStatefulRun
59-
}
77+
// statefulPrecompile implements the [PrecompiledContract] interface to allow a
78+
// [PrecompiledStatefulContract] to be carried with regular geth plumbing. The
79+
// methods are defined on this unexported type instead of directly on
80+
// [PrecompiledStatefulContract] to hide implementation details.
81+
type statefulPrecompile PrecompiledStatefulContract
6082

61-
func (p statefulPrecompile) RequiredGas(input []byte) uint64 {
62-
return p.gas(input)
63-
}
83+
// RequiredGas always returns zero as this gas is consumed by native geth code
84+
// before the contract is run.
85+
func (statefulPrecompile) RequiredGas([]byte) uint64 { return 0 }
6486

6587
func (p statefulPrecompile) Run([]byte) ([]byte, error) {
6688
// https://google.github.io/styleguide/go/best-practices.html#when-to-panic
6789
// This would indicate an API misuse and would occur in tests, not in
6890
// production.
69-
panic(fmt.Sprintf("BUG: call to %T.Run(); MUST call %T", p, p.run))
91+
panic(fmt.Sprintf("BUG: call to %T.Run(); MUST call %T itself", p, p))
92+
}
93+
94+
// A PrecompileEnvironment provides information about the context in which a
95+
// precompiled contract is being run.
96+
type PrecompileEnvironment interface {
97+
Rules() params.Rules
98+
ReadOnly() bool
99+
// StateDB will be non-nil i.f.f !ReadOnly().
100+
StateDB() StateDB
101+
// ReadOnlyState will always be non-nil.
102+
ReadOnlyState() libevm.StateReader
103+
Addresses() *libevm.AddressContext
104+
105+
BlockHeader() (types.Header, error)
106+
BlockNumber() *big.Int
107+
BlockTime() uint64
108+
}
109+
110+
//
111+
// ****** SECURITY ******
112+
//
113+
// If you are updating PrecompileEnvironment to provide the ability to call back
114+
// into another contract, you MUST revisit the evmCallArgs.forceReadOnly flag.
115+
//
116+
// It is possible that forceReadOnly is true but evm.interpreter.readOnly is
117+
// false. This is safe for now, but may not be if recursive calling *from* a
118+
// precompile is enabled.
119+
//
120+
// ****** SECURITY ******
121+
122+
var _ PrecompileEnvironment = (*evmCallArgs)(nil)
123+
124+
func (args *evmCallArgs) Rules() params.Rules { return args.evm.chainRules }
125+
126+
func (args *evmCallArgs) ReadOnly() bool {
127+
if args.readWrite == inheritReadOnly {
128+
if args.evm.interpreter.readOnly { //nolint:gosimple // Clearer code coverage for difficult-to-test branch
129+
return true
130+
}
131+
return false
132+
}
133+
// Even though args.readWrite may be some value other than forceReadOnly,
134+
// that would be an invalid use of the API so we default to read-only as the
135+
// safest failure mode.
136+
return true
70137
}
71138

139+
func (args *evmCallArgs) StateDB() StateDB {
140+
if args.ReadOnly() {
141+
return nil
142+
}
143+
return args.evm.StateDB
144+
}
145+
146+
func (args *evmCallArgs) ReadOnlyState() libevm.StateReader {
147+
// Even though we're actually returning a full state database, the user
148+
// would have to actively circumvent the returned interface to use it. At
149+
// that point they're off-piste and it's not our problem.
150+
return args.evm.StateDB
151+
}
152+
153+
func (args *evmCallArgs) Addresses() *libevm.AddressContext {
154+
return &libevm.AddressContext{
155+
Origin: args.evm.TxContext.Origin,
156+
Caller: args.caller.Address(),
157+
Self: args.addr,
158+
}
159+
}
160+
161+
func (args *evmCallArgs) BlockHeader() (types.Header, error) {
162+
hdr := args.evm.Context.Header
163+
if hdr == nil {
164+
// Although [core.NewEVMBlockContext] sets the field and is in the
165+
// typical hot path (e.g. miner), there are other ways to create a
166+
// [vm.BlockContext] (e.g. directly in tests) that may result in no
167+
// available header.
168+
return types.Header{}, fmt.Errorf("nil %T in current %T", hdr, args.evm.Context)
169+
}
170+
return *hdr, nil
171+
}
172+
173+
func (args *evmCallArgs) BlockNumber() *big.Int {
174+
return new(big.Int).Set(args.evm.Context.BlockNumber)
175+
}
176+
177+
func (args *evmCallArgs) BlockTime() uint64 { return args.evm.Context.Time }
178+
72179
var (
73180
// These lock in the assumptions made when implementing [evmCallArgs]. If
74181
// these break then the struct fields SHOULD be changed to match these

0 commit comments

Comments
 (0)