@@ -3,6 +3,8 @@ package vm_test
33import (
44 "fmt"
55 "math/big"
6+ "reflect"
7+ "strings"
68 "testing"
79
810 "github.com/holiman/uint256"
@@ -11,6 +13,8 @@ import (
1113 "golang.org/x/exp/rand"
1214
1315 "github.com/ethereum/go-ethereum/common"
16+ "github.com/ethereum/go-ethereum/core"
17+ "github.com/ethereum/go-ethereum/core/types"
1418 "github.com/ethereum/go-ethereum/core/vm"
1519 "github.com/ethereum/go-ethereum/crypto"
1620 "github.com/ethereum/go-ethereum/libevm"
@@ -79,6 +83,31 @@ func TestPrecompileOverride(t *testing.T) {
7983 }
8084}
8185
86+ type statefulPrecompileOutput struct {
87+ Caller , Self common.Address
88+ StateValue common.Hash
89+ ReadOnly bool
90+ BlockNumber , Difficulty * big.Int
91+ BlockTime uint64
92+ Input []byte
93+ }
94+
95+ func (o statefulPrecompileOutput ) String () string {
96+ var lines []string
97+ out := reflect .ValueOf (o )
98+ for i , n := 0 , out .NumField (); i < n ; i ++ {
99+ name := out .Type ().Field (i ).Name
100+ fld := out .Field (i ).Interface ()
101+
102+ verb := "%v"
103+ if _ , ok := fld .([]byte ); ok {
104+ verb = "%#x"
105+ }
106+ lines = append (lines , fmt .Sprintf ("%s: " + verb , name , fld ))
107+ }
108+ return strings .Join (lines , "\n " )
109+ }
110+
82111func TestNewStatefulPrecompile (t * testing.T ) {
83112 rng := ethtest .NewPseudoRand (314159 )
84113 precompile := rng .Address ()
@@ -87,38 +116,47 @@ func TestNewStatefulPrecompile(t *testing.T) {
87116 const gasLimit = 1e6
88117 gasCost := rng .Uint64n (gasLimit )
89118
90- makeOutput := func (caller , self common.Address , input []byte , stateVal common.Hash , readOnly bool ) []byte {
91- return []byte (fmt .Sprintf (
92- "Caller: %v Precompile: %v State: %v Read-only: %t, Input: %#x" ,
93- caller , self , stateVal , readOnly , input ,
94- ))
95- }
96- run := func (env vm.PrecompileEnvironment , input []byte ) ([]byte , error ) {
119+ run := func (env vm.PrecompileEnvironment , input []byte , suppliedGas uint64 ) ([]byte , uint64 , error ) {
97120 if got , want := env .StateDB () != nil , ! env .ReadOnly (); got != want {
98- return nil , fmt .Errorf ("PrecompileEnvironment().StateDB() must be non-nil i.f.f. not read-only; got non-nil? %t; want %t" , got , want )
121+ return nil , 0 , fmt .Errorf ("PrecompileEnvironment().StateDB() must be non-nil i.f.f. not read-only; got non-nil? %t; want %t" , got , want )
122+ }
123+ hdr , err := env .BlockHeader ()
124+ if err != nil {
125+ return nil , 0 , err
99126 }
100127
101128 addrs := env .Addresses ()
102- val := env .ReadOnlyState ().GetState (precompile , slot )
103- return makeOutput (addrs .Caller , addrs .Self , input , val , env .ReadOnly ()), nil
129+ out := & statefulPrecompileOutput {
130+ Caller : addrs .Caller ,
131+ Self : addrs .Self ,
132+ StateValue : env .ReadOnlyState ().GetState (precompile , slot ),
133+ ReadOnly : env .ReadOnly (),
134+ BlockNumber : env .BlockNumber (),
135+ BlockTime : env .BlockTime (),
136+ Difficulty : hdr .Difficulty ,
137+ Input : input ,
138+ }
139+ return []byte (out .String ()), suppliedGas - gasCost , nil
104140 }
105141 hooks := & hookstest.Stub {
106142 PrecompileOverrides : map [common.Address ]libevm.PrecompiledContract {
107- precompile : vm .NewStatefulPrecompile (
108- run ,
109- func (b []byte ) uint64 {
110- return gasCost
111- },
112- ),
143+ precompile : vm .NewStatefulPrecompile (run ),
113144 },
114145 }
115146 hooks .Register (t )
116147
148+ header := & types.Header {
149+ Number : rng .BigUint64 (),
150+ Time : rng .Uint64 (),
151+ Difficulty : rng .BigUint64 (),
152+ }
117153 caller := rng .Address ()
118154 input := rng .Bytes (8 )
119155 value := rng .Hash ()
120156
121- state , evm := ethtest .NewZeroEVM (t )
157+ state , evm := ethtest .NewZeroEVM (t , ethtest .WithBlockContext (
158+ core .NewEVMBlockContext (header , nil , rng .AddressPtr ()),
159+ ))
122160 state .SetState (precompile , slot , value )
123161
124162 tests := []struct {
@@ -160,12 +198,21 @@ func TestNewStatefulPrecompile(t *testing.T) {
160198
161199 for _ , tt := range tests {
162200 t .Run (tt .name , func (t * testing.T ) {
163- wantReturnData := makeOutput (caller , precompile , input , value , tt .wantReadOnly )
201+ wantReturnData := statefulPrecompileOutput {
202+ Caller : caller ,
203+ Self : precompile ,
204+ StateValue : value ,
205+ ReadOnly : tt .wantReadOnly ,
206+ BlockNumber : header .Number ,
207+ BlockTime : header .Time ,
208+ Difficulty : header .Difficulty ,
209+ Input : input ,
210+ }.String ()
164211 wantGasLeft := gasLimit - gasCost
165212
166213 gotReturnData , gotGasLeft , err := tt .call ()
167214 require .NoError (t , err )
168- assert .Equal (t , string ( wantReturnData ) , string (gotReturnData ))
215+ assert .Equal (t , wantReturnData , string (gotReturnData ))
169216 assert .Equal (t , wantGasLeft , gotGasLeft )
170217 })
171218 }
@@ -204,13 +251,12 @@ func TestInheritReadOnly(t *testing.T) {
204251 hooks := & hookstest.Stub {
205252 PrecompileOverrides : map [common.Address ]libevm.PrecompiledContract {
206253 precompile : vm .NewStatefulPrecompile (
207- func (env vm.PrecompileEnvironment , input []byte ) ([]byte , error ) {
254+ func (env vm.PrecompileEnvironment , input []byte , suppliedGas uint64 ) ([]byte , uint64 , error ) {
208255 if env .ReadOnly () {
209- return []byte {ifReadOnly }, nil
256+ return []byte {ifReadOnly }, suppliedGas , nil
210257 }
211- return []byte {ifNotReadOnly }, nil
258+ return []byte {ifNotReadOnly }, suppliedGas , nil
212259 },
213- func ([]byte ) uint64 { return 0 },
214260 ),
215261 },
216262 }
@@ -296,20 +342,23 @@ func TestCanCreateContract(t *testing.T) {
296342 account := rng .Address ()
297343 slot := rng .Hash ()
298344
345+ const gasLimit uint64 = 1e6
346+ gasUsage := rng .Uint64n (gasLimit )
347+
299348 makeErr := func (cc * libevm.AddressContext , stateVal common.Hash ) error {
300349 return fmt .Errorf ("Origin: %v Caller: %v Contract: %v State: %v" , cc .Origin , cc .Caller , cc .Self , stateVal )
301350 }
302351 hooks := & hookstest.Stub {
303- CanCreateContractFn : func (cc * libevm.AddressContext , s libevm.StateReader ) error {
304- return makeErr (cc , s .GetState (account , slot ))
352+ CanCreateContractFn : func (cc * libevm.AddressContext , gas uint64 , s libevm.StateReader ) ( uint64 , error ) {
353+ return gas - gasUsage , makeErr (cc , s .GetState (account , slot ))
305354 },
306355 }
307356 hooks .Register (t )
308357
309358 origin := rng .Address ()
310359 caller := rng .Address ()
311360 value := rng .Hash ()
312- code := rng . Bytes ( 8 )
361+ code := [] byte { byte ( vm . STOP )}
313362 salt := rng .Hash ()
314363
315364 create := crypto .CreateAddress (caller , 0 )
@@ -323,14 +372,14 @@ func TestCanCreateContract(t *testing.T) {
323372 {
324373 name : "Create" ,
325374 create : func (evm * vm.EVM ) ([]byte , common.Address , uint64 , error ) {
326- return evm .Create (vm .AccountRef (caller ), code , 1e6 , uint256 .NewInt (0 ))
375+ return evm .Create (vm .AccountRef (caller ), code , gasLimit , uint256 .NewInt (0 ))
327376 },
328377 wantErr : makeErr (& libevm.AddressContext {Origin : origin , Caller : caller , Self : create }, value ),
329378 },
330379 {
331380 name : "Create2" ,
332381 create : func (evm * vm.EVM ) ([]byte , common.Address , uint64 , error ) {
333- return evm .Create2 (vm .AccountRef (caller ), code , 1e6 , uint256 .NewInt (0 ), new (uint256.Int ).SetBytes (salt [:]))
382+ return evm .Create2 (vm .AccountRef (caller ), code , gasLimit , uint256 .NewInt (0 ), new (uint256.Int ).SetBytes (salt [:]))
334383 },
335384 wantErr : makeErr (& libevm.AddressContext {Origin : origin , Caller : caller , Self : create2 }, value ),
336385 },
@@ -342,8 +391,10 @@ func TestCanCreateContract(t *testing.T) {
342391 state .SetState (account , slot , value )
343392 evm .TxContext .Origin = origin
344393
345- _ , _ , _ , err := tt .create (evm )
394+ _ , _ , gasRemaining , err := tt .create (evm )
346395 require .EqualError (t , err , tt .wantErr .Error ())
396+ // require prints uint64s in hex
397+ require .Equal (t , int (gasLimit - gasUsage ), int (gasRemaining ), "gas remaining" ) //nolint:gosec // G115 won't overflow as <= 1e6
347398 })
348399 }
349400}
0 commit comments