@@ -149,9 +149,6 @@ func TestNewStatefulPrecompile(t *testing.T) {
149149 gasCost := rng .Uint64n (gasLimit )
150150
151151 run := func (env vm.PrecompileEnvironment , input []byte , suppliedGas uint64 ) ([]byte , uint64 , error ) {
152- if got , want := env .StateDB () != nil , env .StateMutability () == vm .MutableState ; got != want {
153- return nil , 0 , fmt .Errorf ("PrecompileEnvironment().StateDB() must be non-nil i.f.f. state is mutable; got non-nil? %t; want %t" , got , want )
154- }
155152 hdr , err := env .BlockHeader ()
156153 if err != nil {
157154 return nil , 0 , err
@@ -770,3 +767,75 @@ func ExamplePrecompileEnvironment() {
770767 // variable to include it in this example function.
771768 _ = actualCaller
772769}
770+
771+ func TestStateMutability (t * testing.T ) {
772+ rng := ethtest .NewPseudoRand (0 )
773+ precompile := rng .Address ()
774+ chainID := rng .BigUint64 ()
775+
776+ const precompileReturn = "precompile executed"
777+ hooks := & hookstest.Stub {
778+ PrecompileOverrides : map [common.Address ]libevm.PrecompiledContract {
779+ precompile : vm .NewStatefulPrecompile (func (env vm.PrecompileEnvironment , input []byte ) (ret []byte , err error ) {
780+
781+ tests := []struct {
782+ name string
783+ env vm.PrecompileEnvironment
784+ want vm.StateMutability
785+ }{
786+ {
787+ name : "incoming argument" ,
788+ env : env ,
789+ want : vm .MutableState ,
790+ },
791+ {
792+ name : "AsReadOnly()" ,
793+ env : env .AsReadOnly (),
794+ want : vm .ReadOnlyState ,
795+ },
796+ {
797+ name : "AsPure()" ,
798+ env : env .AsPure (),
799+ want : vm .Pure ,
800+ },
801+ {
802+ name : "AsPure().AsReadOnly() is still pure" ,
803+ env : env .AsPure ().AsReadOnly (),
804+ want : vm .Pure ,
805+ },
806+ }
807+
808+ for _ , tt := range tests {
809+ t .Run (tt .name , func (t * testing.T ) {
810+ env := tt .env // deliberately shadow the incoming arg
811+ t .Run ("mutability_and_access" , func (t * testing.T ) {
812+ assert .Equal (t , tt .want , env .StateMutability (), "env.StateMutability()" )
813+ assert .Equal (t , env .StateDB () != nil , tt .want == vm .MutableState , "env.StateDB() != nil i.f.f. MutableState" )
814+ assert .Equal (t , env .ReadOnlyState () != nil , tt .want != vm .Pure , "env.ReadOnlyState() != nil i.f.f !Pure" )
815+ })
816+
817+ t .Run ("environment_unmodified" , func (t * testing.T ) {
818+ // Each of these demonstrate that the underlying
819+ // copy of the environment propagates everything but
820+ // mutability.
821+ assert .Equal (t , chainID , env .ChainConfig ().ChainID , "Chain ID preserved" )
822+ assert .Equalf (t , precompile , env .Addresses ().Self , "%T preserved" , env .Addresses ())
823+ assert .Equalf (t , vm .Call , env .IncomingCallType (), "%T preserved" , env .IncomingCallType ())
824+ })
825+ })
826+ }
827+
828+ return []byte (precompileReturn ), nil
829+ }),
830+ },
831+ }
832+ hooks .Register (t )
833+
834+ _ , evm := ethtest .NewZeroEVM (t , ethtest .WithChainConfig (& params.ChainConfig {
835+ ChainID : chainID ,
836+ }))
837+ got , _ , err := evm .Call (vm.AccountRef {}, precompile , nil , 0 , uint256 .NewInt (0 ))
838+ if got , want := string (got ), precompileReturn ; err != nil || got != want {
839+ t .Errorf ("%T.Call([precompile]) got {%q, %v}; want {%q, nil}" , evm , got , err , want )
840+ }
841+ }
0 commit comments