From cc0d48e95548865350b297621dc735fb5b338ea0 Mon Sep 17 00:00:00 2001 From: Denys S <150304777+dssei@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:07:48 -0700 Subject: [PATCH] Add delegation and static call check (#1761) * add delegation and static call check * retrigger checks --- precompiles/distribution/distribution.go | 6 + precompiles/distribution/distribution_test.go | 167 ++++++++++++++++-- 2 files changed, 158 insertions(+), 15 deletions(-) diff --git a/precompiles/distribution/distribution.go b/precompiles/distribution/distribution.go index 0e8fde4fe8..6c205fcda9 100644 --- a/precompiles/distribution/distribution.go +++ b/precompiles/distribution/distribution.go @@ -78,6 +78,12 @@ func NewPrecompile(distrKeeper pcommon.DistributionKeeper, evmKeeper pcommon.EVM } func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { + if readOnly { + return nil, 0, errors.New("cannot call distr precompile from staticcall") + } + if caller.Cmp(callingContract) != 0 { + return nil, 0, errors.New("cannot delegatecall distr") + } switch method.Name { case SetWithdrawAddressMethod: return p.setWithdrawAddress(ctx, method, caller, args, value) diff --git a/precompiles/distribution/distribution_test.go b/precompiles/distribution/distribution_test.go index 801f33dbfa..6f5b983e11 100644 --- a/precompiles/distribution/distribution_test.go +++ b/precompiles/distribution/distribution_test.go @@ -346,6 +346,7 @@ func setupValidator(t *testing.T, ctx sdk.Context, a *app.App, bondStatus stakin func TestPrecompile_RunAndCalculateGas_WithdrawDelegationRewards(t *testing.T) { _, notAssociatedCallerEvmAddress := testkeeper.MockAddressPair() + _, contractEvmAddress := testkeeper.MockAddressPair() validatorAddress := "seivaloper1reedlc9w8p7jrpqfky4c5k90nea4p6dhk5yqgd" type fields struct { @@ -364,6 +365,7 @@ func TestPrecompile_RunAndCalculateGas_WithdrawDelegationRewards(t *testing.T) { validator string suppliedGas uint64 value *big.Int + readOnly bool } tests := []struct { name string @@ -402,9 +404,10 @@ func TestPrecompile_RunAndCalculateGas_WithdrawDelegationRewards(t *testing.T) { name: "fails if delegator is not associated", fields: fields{}, args: args{ - caller: notAssociatedCallerEvmAddress, - validator: validatorAddress, - suppliedGas: uint64(1000000), + caller: notAssociatedCallerEvmAddress, + callingContract: notAssociatedCallerEvmAddress, + validator: validatorAddress, + suppliedGas: uint64(1000000), }, wantRet: nil, wantRemainingGas: 0, @@ -420,6 +423,49 @@ func TestPrecompile_RunAndCalculateGas_WithdrawDelegationRewards(t *testing.T) { wantErr: true, wantErrMsg: "{ReadFlat}", }, + { + name: "fails if caller != callingContract", + fields: fields{}, + args: args{ + caller: notAssociatedCallerEvmAddress, + callingContract: contractEvmAddress, + validator: validatorAddress, + suppliedGas: uint64(1000000), + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot delegatecall distr", + }, + { + name: "fails if caller != callingContract and callingContract not set", + fields: fields{}, + args: args{ + caller: notAssociatedCallerEvmAddress, + callingContract: contractEvmAddress, + validator: validatorAddress, + suppliedGas: uint64(1000000), + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot delegatecall distr", + }, + { + name: "fails if readOnly", + fields: fields{}, + args: args{ + caller: notAssociatedCallerEvmAddress, + callingContract: notAssociatedCallerEvmAddress, + validator: validatorAddress, + suppliedGas: uint64(1000000), + readOnly: true, + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot call distr precompile from staticcall", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -435,7 +481,7 @@ func TestPrecompile_RunAndCalculateGas_WithdrawDelegationRewards(t *testing.T) { require.Nil(t, err) inputs, err := withdraw.Inputs.Pack(tt.args.validator) require.Nil(t, err) - gotRet, gotRemainingGas, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*distribution.PrecompileExecutor).WithdrawDelegationRewardsID, inputs...), tt.args.suppliedGas, tt.args.value, nil, false) + gotRet, gotRemainingGas, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*distribution.PrecompileExecutor).WithdrawDelegationRewardsID, inputs...), tt.args.suppliedGas, tt.args.value, nil, tt.args.readOnly) if (err != nil) != tt.wantErr { t.Errorf("RunAndCalculateGas() error = %v, wantErr %v", err, tt.wantErr) return @@ -455,6 +501,7 @@ func TestPrecompile_RunAndCalculateGas_WithdrawDelegationRewards(t *testing.T) { func TestPrecompile_RunAndCalculateGas_WithdrawMultipleDelegationRewards(t *testing.T) { _, notAssociatedCallerEvmAddress := testkeeper.MockAddressPair() + _, contractEvmAddress := testkeeper.MockAddressPair() validatorAddresses := []string{"seivaloper1reedlc9w8p7jrpqfky4c5k90nea4p6dhk5yqgd"} type fields struct { @@ -473,6 +520,7 @@ func TestPrecompile_RunAndCalculateGas_WithdrawMultipleDelegationRewards(t *test validators []string suppliedGas uint64 value *big.Int + readOnly bool } tests := []struct { name string @@ -511,9 +559,10 @@ func TestPrecompile_RunAndCalculateGas_WithdrawMultipleDelegationRewards(t *test name: "fails if delegator is not associated", fields: fields{}, args: args{ - caller: notAssociatedCallerEvmAddress, - validators: validatorAddresses, - suppliedGas: uint64(1000000), + caller: notAssociatedCallerEvmAddress, + callingContract: notAssociatedCallerEvmAddress, + validators: validatorAddresses, + suppliedGas: uint64(1000000), }, wantRet: nil, wantRemainingGas: 0, @@ -529,6 +578,48 @@ func TestPrecompile_RunAndCalculateGas_WithdrawMultipleDelegationRewards(t *test wantErr: true, wantErrMsg: "{ReadFlat}", }, + { + name: "fails if caller != callingContract", + fields: fields{}, + args: args{ + caller: notAssociatedCallerEvmAddress, + callingContract: contractEvmAddress, + validators: validatorAddresses, + suppliedGas: uint64(1000000), + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot delegatecall distr", + }, + { + name: "fails if caller != callingContract and callingContract not set", + fields: fields{}, + args: args{ + caller: notAssociatedCallerEvmAddress, + validators: validatorAddresses, + suppliedGas: uint64(1000000), + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot delegatecall distr", + }, + { + name: "fails if readOnly", + fields: fields{}, + args: args{ + caller: notAssociatedCallerEvmAddress, + callingContract: notAssociatedCallerEvmAddress, + validators: validatorAddresses, + suppliedGas: uint64(1000000), + readOnly: true, + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot call distr precompile from staticcall", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -544,7 +635,7 @@ func TestPrecompile_RunAndCalculateGas_WithdrawMultipleDelegationRewards(t *test require.Nil(t, err) inputs, err := withdraw.Inputs.Pack(tt.args.validators) require.Nil(t, err) - gotRet, gotRemainingGas, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*distribution.PrecompileExecutor).WithdrawMultipleDelegationRewardsID, inputs...), tt.args.suppliedGas, tt.args.value, nil, false) + gotRet, gotRemainingGas, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*distribution.PrecompileExecutor).WithdrawMultipleDelegationRewardsID, inputs...), tt.args.suppliedGas, tt.args.value, nil, tt.args.readOnly) if (err != nil) != tt.wantErr { t.Errorf("RunAndCalculateGas() error = %v, wantErr %v", err, tt.wantErr) return @@ -565,6 +656,7 @@ func TestPrecompile_RunAndCalculateGas_WithdrawMultipleDelegationRewards(t *test func TestPrecompile_RunAndCalculateGas_SetWithdrawAddress(t *testing.T) { _, notAssociatedCallerEvmAddress := testkeeper.MockAddressPair() callerSeiAddress, callerEvmAddress := testkeeper.MockAddressPair() + _, contactEvmAddress := testkeeper.MockAddressPair() type fields struct { Precompile pcommon.Precompile @@ -582,6 +674,7 @@ func TestPrecompile_RunAndCalculateGas_SetWithdrawAddress(t *testing.T) { callingContract common.Address suppliedGas uint64 value *big.Int + readOnly bool } tests := []struct { name string @@ -620,9 +713,10 @@ func TestPrecompile_RunAndCalculateGas_SetWithdrawAddress(t *testing.T) { name: "fails if delegator is not associated", fields: fields{}, args: args{ - addressToSet: notAssociatedCallerEvmAddress, - caller: notAssociatedCallerEvmAddress, - suppliedGas: uint64(1000000), + addressToSet: notAssociatedCallerEvmAddress, + caller: notAssociatedCallerEvmAddress, + callingContract: notAssociatedCallerEvmAddress, + suppliedGas: uint64(1000000), }, wantRet: nil, wantRemainingGas: 0, @@ -633,9 +727,10 @@ func TestPrecompile_RunAndCalculateGas_SetWithdrawAddress(t *testing.T) { name: "fails if address is invalid", fields: fields{}, args: args{ - addressToSet: common.Address{}, - caller: callerEvmAddress, - suppliedGas: uint64(1000000), + addressToSet: common.Address{}, + caller: callerEvmAddress, + callingContract: callerEvmAddress, + suppliedGas: uint64(1000000), }, wantRet: nil, wantRemainingGas: 0, @@ -651,6 +746,48 @@ func TestPrecompile_RunAndCalculateGas_SetWithdrawAddress(t *testing.T) { wantErr: true, wantErrMsg: "{ReadFlat}", }, + { + name: "fails if caller != callingContract", + fields: fields{}, + args: args{ + addressToSet: common.Address{}, + caller: callerEvmAddress, + callingContract: contactEvmAddress, + suppliedGas: uint64(1000000), + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot delegatecall distr", + }, + { + name: "fails if caller != callingContract with callingContract not set", + fields: fields{}, + args: args{ + addressToSet: common.Address{}, + caller: callerEvmAddress, + suppliedGas: uint64(1000000), + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot delegatecall distr", + }, + { + name: "fails if readOnly", + fields: fields{}, + args: args{ + addressToSet: common.Address{}, + caller: callerEvmAddress, + callingContract: callerEvmAddress, + suppliedGas: uint64(1000000), + readOnly: true, + }, + wantRet: nil, + wantRemainingGas: 0, + wantErr: true, + wantErrMsg: "cannot call distr precompile from staticcall", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -668,7 +805,7 @@ func TestPrecompile_RunAndCalculateGas_SetWithdrawAddress(t *testing.T) { require.Nil(t, err) inputs, err := setAddress.Inputs.Pack(tt.args.addressToSet) require.Nil(t, err) - gotRet, gotRemainingGas, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*distribution.PrecompileExecutor).SetWithdrawAddrID, inputs...), tt.args.suppliedGas, tt.args.value, nil, false) + gotRet, gotRemainingGas, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*distribution.PrecompileExecutor).SetWithdrawAddrID, inputs...), tt.args.suppliedGas, tt.args.value, nil, tt.args.readOnly) if (err != nil) != tt.wantErr { t.Errorf("RunAndCalculateGas() error = %v, wantErr %v", err, tt.wantErr) return