test: PrecompileEnvironment.Value() under DELEGATECALL (#216)

## Why this should be merged

Completeness.

## How this works

n/a

## How this was tested

Originally the `DELEGATECALL` test asserted that the `CALLVALUE` was
zero, but this didn't demonstrate that it was inherited (e.g.
`STATICCALL` always receives zero). The value sent to the caller of the
precompile is now non-zero, to precisely demonstrate correct behaviour
of all call types.
This commit is contained in:
Arran Schlosberg 2025-09-25 15:19:03 +01:00 committed by GitHub
parent 7a59fddf77
commit 414b1f5dff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 25 additions and 24 deletions

View file

@ -236,7 +236,7 @@ func (args *evmCallArgs) env() *environment {
self = args.addr self = args.addr
case DelegateCall: case DelegateCall:
value = nil value = nil // inherited from `args.caller` inside [Contract.AsDelegate]
fallthrough fallthrough
case CallCode: case CallCode:
self = args.caller.Address() self = args.caller.Address()

View file

@ -109,7 +109,7 @@ type statefulPrecompileOutput struct {
ChainID *big.Int ChainID *big.Int
Addresses *libevm.AddressContext Addresses *libevm.AddressContext
StateValue common.Hash StateValue common.Hash
ValueReceived *uint256.Int CallValue *uint256.Int
ReadOnly bool ReadOnly bool
BlockNumber, Difficulty *big.Int BlockNumber, Difficulty *big.Int
BlockTime uint64 BlockTime uint64
@ -169,7 +169,7 @@ func TestNewStatefulPrecompile(t *testing.T) {
ChainID: env.ChainConfig().ChainID, ChainID: env.ChainConfig().ChainID,
Addresses: env.Addresses(), Addresses: env.Addresses(),
StateValue: env.ReadOnlyState().GetState(precompile, slot), StateValue: env.ReadOnlyState().GetState(precompile, slot),
ValueReceived: env.Value(), CallValue: env.Value(),
ReadOnly: env.ReadOnly(), ReadOnly: env.ReadOnly(),
BlockNumber: env.BlockNumber(), BlockNumber: env.BlockNumber(),
BlockTime: env.BlockTime(), BlockTime: env.BlockTime(),
@ -197,12 +197,13 @@ func TestNewStatefulPrecompile(t *testing.T) {
} }
input := rng.Bytes(8) input := rng.Bytes(8)
stateValue := rng.Hash() stateValue := rng.Hash()
transferValue := rng.Uint256() callCallerValue := rng.Uint256()
callPrecompileValue := rng.Uint256()
chainID := rng.BigUint64() chainID := rng.BigUint64()
caller := common.HexToAddress("CA11E12") // caller of the precompile caller := common.HexToAddress("CA11E12") // caller of the precompile
eoa := common.HexToAddress("E0A") // caller of the precompile-caller eoa := common.HexToAddress("E0A") // caller of the precompile-caller
callerContract := vm.NewContract(vm.AccountRef(eoa), vm.AccountRef(caller), uint256.NewInt(0), 1e6) callerContract := vm.NewContract(vm.AccountRef(eoa), vm.AccountRef(caller), callCallerValue, 1e6)
state, evm := ethtest.NewZeroEVM( state, evm := ethtest.NewZeroEVM(
t, t,
@ -225,10 +226,10 @@ func TestNewStatefulPrecompile(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
call func() ([]byte, uint64, error) call func() ([]byte, uint64, error)
wantAddresses *libevm.AddressContext wantAddresses *libevm.AddressContext
wantTransferValue *uint256.Int wantCallValue *uint256.Int
// Note that this only covers evm.readOnly being true because of the // Note that this only covers evm.readOnly being true because of the
// precompile's call. See TestInheritReadOnly for alternate case. // precompile's call. See TestInheritReadOnly for alternate case.
wantReadOnly bool wantReadOnly bool
@ -237,21 +238,21 @@ func TestNewStatefulPrecompile(t *testing.T) {
{ {
name: "EVM.Call()", name: "EVM.Call()",
call: func() ([]byte, uint64, error) { call: func() ([]byte, uint64, error) {
return evm.Call(callerContract, precompile, input, gasLimit, transferValue) return evm.Call(callerContract, precompile, input, gasLimit, callPrecompileValue)
}, },
wantAddresses: &libevm.AddressContext{ wantAddresses: &libevm.AddressContext{
Origin: eoa, Origin: eoa,
EVMSemantic: rawAddresses, EVMSemantic: rawAddresses,
Raw: &rawAddresses, Raw: &rawAddresses,
}, },
wantReadOnly: false, wantReadOnly: false,
wantTransferValue: transferValue, wantCallValue: callPrecompileValue,
wantCallType: vm.Call, wantCallType: vm.Call,
}, },
{ {
name: "EVM.CallCode()", name: "EVM.CallCode()",
call: func() ([]byte, uint64, error) { call: func() ([]byte, uint64, error) {
return evm.CallCode(callerContract, precompile, input, gasLimit, transferValue) return evm.CallCode(callerContract, precompile, input, gasLimit, callPrecompileValue)
}, },
wantAddresses: &libevm.AddressContext{ wantAddresses: &libevm.AddressContext{
Origin: eoa, Origin: eoa,
@ -261,9 +262,9 @@ func TestNewStatefulPrecompile(t *testing.T) {
}, },
Raw: &rawAddresses, Raw: &rawAddresses,
}, },
wantReadOnly: false, wantReadOnly: false,
wantTransferValue: transferValue, wantCallValue: callPrecompileValue,
wantCallType: vm.CallCode, wantCallType: vm.CallCode,
}, },
{ {
name: "EVM.DelegateCall()", name: "EVM.DelegateCall()",
@ -278,9 +279,9 @@ func TestNewStatefulPrecompile(t *testing.T) {
}, },
Raw: &rawAddresses, Raw: &rawAddresses,
}, },
wantReadOnly: false, wantReadOnly: false,
wantTransferValue: uint256.NewInt(0), wantCallValue: callCallerValue, // Important difference from [vm.EVM.Call]
wantCallType: vm.DelegateCall, wantCallType: vm.DelegateCall,
}, },
{ {
name: "EVM.StaticCall()", name: "EVM.StaticCall()",
@ -292,9 +293,9 @@ func TestNewStatefulPrecompile(t *testing.T) {
EVMSemantic: rawAddresses, EVMSemantic: rawAddresses,
Raw: &rawAddresses, Raw: &rawAddresses,
}, },
wantReadOnly: true, wantReadOnly: true,
wantTransferValue: uint256.NewInt(0), wantCallValue: uint256.NewInt(0),
wantCallType: vm.StaticCall, wantCallType: vm.StaticCall,
}, },
} }
@ -304,7 +305,7 @@ func TestNewStatefulPrecompile(t *testing.T) {
ChainID: chainID, ChainID: chainID,
Addresses: tt.wantAddresses, Addresses: tt.wantAddresses,
StateValue: stateValue, StateValue: stateValue,
ValueReceived: tt.wantTransferValue, CallValue: tt.wantCallValue,
ReadOnly: tt.wantReadOnly, ReadOnly: tt.wantReadOnly,
BlockNumber: header.Number, BlockNumber: header.Number,
BlockTime: header.Time, BlockTime: header.Time,