@@ -2,73 +2,180 @@ package vm
22
33import (
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 }
2327type 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
6587func (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+
72179var (
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