diff --git a/core/predicate_check_test.go b/core/predicate_check_test.go index c141c1c3c0..ddb6a76c48 100644 --- a/core/predicate_check_test.go +++ b/core/predicate_check_test.go @@ -109,7 +109,7 @@ func TestCheckBlockPredicates(t *testing.T) { rules := params.TestChainConfig.Rules(common.Big0, params.IsMergeTODO, 0) predicater := precompileconfig.NewMockPredicater(gomock.NewController(t)) - predicater.EXPECT().PredicateGas(gomock.Any()).Return(uint64(0), nil).AnyTimes() + predicater.EXPECT().PredicateGas(gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes() predicater.EXPECT().VerifyPredicate(gomock.Any(), gomock.Any()).DoAndReturn(verifyMockPredicate).AnyTimes() rulesExtra := params.GetRulesExtra(rules) @@ -311,7 +311,7 @@ func TestCheckTxPredicates(t *testing.T) { rules := params.TestChainConfig.Rules(common.Big0, params.IsMergeTODO, 0) predicater := precompileconfig.NewMockPredicater(gomock.NewController(t)) - predicater.EXPECT().PredicateGas(gomock.Any()).Return(test.predicateGas, nil).AnyTimes() + predicater.EXPECT().PredicateGas(gomock.Any(), gomock.Any()).Return(test.predicateGas, nil).AnyTimes() predicater.EXPECT().VerifyPredicate(gomock.Any(), gomock.Any()).DoAndReturn(verifyMockPredicate).AnyTimes() rulesExtra := params.GetRulesExtra(rules) diff --git a/core/state_transition.go b/core/state_transition.go index 5eaa60ab74..7894f0accd 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -162,7 +162,7 @@ func accessListGas(rules params.Rules, accessList types.AccessList) (uint64, err } gas = totalGas } else { - predicateGas, err := predicaterContract.PredicateGas(predicate.Predicate(accessTuple.StorageKeys)) + predicateGas, err := predicaterContract.PredicateGas(predicate.Predicate(accessTuple.StorageKeys), rulesExtra) if err != nil { return 0, err } diff --git a/params/extras/network_upgrades.go b/params/extras/network_upgrades.go index 18d04e2877..57c1b33729 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -264,6 +264,11 @@ type AvalancheRules struct { IsGranite bool } +// IsGraniteActivated is used by the warp precompile to determine which gas costs to use. +func (a AvalancheRules) IsGraniteActivated() bool { + return a.IsGranite +} + func (n *NetworkUpgrades) GetAvalancheRules(timestamp uint64) AvalancheRules { return AvalancheRules{ IsApricotPhase1: n.IsApricotPhase1(timestamp), diff --git a/params/hooks_libevm.go b/params/hooks_libevm.go index 78633266e8..ec832eece3 100644 --- a/params/hooks_libevm.go +++ b/params/hooks_libevm.go @@ -195,6 +195,11 @@ func (a accessibleState) GetChainConfig() precompileconfig.ChainConfig { return GetExtra(a.env.ChainConfig()) } +func (a accessibleState) GetRules() precompileconfig.Rules { + chainConfigExtra := GetExtra(a.GetPrecompileEnv().ChainConfig()) + return chainConfigExtra.GetAvalancheRules(a.GetBlockContext().Timestamp()) +} + func (a accessibleState) GetSnowContext() *snow.Context { return GetExtra(a.env.ChainConfig()).SnowCtx } diff --git a/precompile/contract/interfaces.go b/precompile/contract/interfaces.go index 868dc0a22b..27b386b749 100644 --- a/precompile/contract/interfaces.go +++ b/precompile/contract/interfaces.go @@ -60,6 +60,7 @@ type AccessibleState interface { GetSnowContext() *snow.Context GetChainConfig() precompileconfig.ChainConfig GetPrecompileEnv() vm.PrecompileEnvironment + GetRules() precompileconfig.Rules } // ConfigurationBlockContext defines the interface required to configure a precompile. diff --git a/precompile/contract/mocks.go b/precompile/contract/mocks.go index 12ff4ea60e..c0147ef340 100644 --- a/precompile/contract/mocks.go +++ b/precompile/contract/mocks.go @@ -348,6 +348,20 @@ func (mr *MockAccessibleStateMockRecorder) GetPrecompileEnv() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrecompileEnv", reflect.TypeOf((*MockAccessibleState)(nil).GetPrecompileEnv)) } +// GetRules mocks base method. +func (m *MockAccessibleState) GetRules() precompileconfig.Rules { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRules") + ret0, _ := ret[0].(precompileconfig.Rules) + return ret0 +} + +// GetRules indicates an expected call of GetRules. +func (mr *MockAccessibleStateMockRecorder) GetRules() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRules", reflect.TypeOf((*MockAccessibleState)(nil).GetRules)) +} + // GetSnowContext mocks base method. func (m *MockAccessibleState) GetSnowContext() *snow.Context { m.ctrl.T.Helper() diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 843a3b7dcc..9997f1c5e2 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -144,9 +144,11 @@ func (*Config) Accept(acceptCtx *precompileconfig.AcceptContext, blockHash commo // 4. TODO: Lookup of the validator set // // If the payload of the warp message fails parsing, return a non-nil error invalidating the transaction. -func (*Config) PredicateGas(pred predicate.Predicate) (uint64, error) { - totalGas := GasCostPerSignatureVerification - bytesGasCost, overflow := math.SafeMul(GasCostPerWarpMessageChunk, uint64(len(pred))) +func (*Config) PredicateGas(pred predicate.Predicate, rules precompileconfig.Rules) (uint64, error) { + gasConfig := CurrentGasConfig(rules) + + totalGas := gasConfig.PerSignatureVerification + bytesGasCost, overflow := math.SafeMul(gasConfig.PerWarpMessageChunk, uint64(len(pred))) if overflow { return 0, fmt.Errorf("overflow calculating gas cost for %d warp message chunks", len(pred)) } @@ -172,7 +174,7 @@ func (*Config) PredicateGas(pred predicate.Predicate) (uint64, error) { if err != nil { return 0, fmt.Errorf("%w: %w", errCannotGetNumSigners, err) } - signerGas, overflow := math.SafeMul(uint64(numSigners), GasCostPerWarpSigner) + signerGas, overflow := math.SafeMul(uint64(numSigners), gasConfig.PerWarpSigner) if overflow { return 0, errOverflowSignersGasCost } diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index 2bff47837e..851d000759 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/coreth/accounts/abi" "github.com/ava-labs/coreth/precompile/contract" + "github.com/ava-labs/coreth/precompile/precompileconfig" ) const ( @@ -31,12 +32,17 @@ const ( SendWarpMessageGasCost uint64 = contract.LogGas + 3*contract.LogTopicGas + AddWarpMessageGasCost + contract.WriteGasCostPerSlot // SendWarpMessageGasCostPerByte cost accounts for producing a signed message of a given size SendWarpMessageGasCostPerByte uint64 = contract.LogDataGas - - GasCostPerWarpSigner uint64 = 500 - GasCostPerWarpMessageChunk uint64 = 3_200 - GasCostPerSignatureVerification uint64 = 200_000 ) +type GasConfig struct { + // Gas cost per warp signer in the validator set + PerWarpSigner uint64 + // Gas cost per chunk of the warp message (each chunk is 128 bytes) + PerWarpMessageChunk uint64 + // Gas cost to verify a BLS signature + PerSignatureVerification uint64 +} + var ( errInvalidSendInput = errors.New("invalid sendWarpMessage input") errInvalidIndexInput = errors.New("invalid index to specify warp message") @@ -343,3 +349,18 @@ func createWarpPrecompile() contract.StatefulPrecompiledContract { } return statefulContract } + +func CurrentGasConfig(rules precompileconfig.Rules) GasConfig { + if rules.IsGraniteActivated() { + return GasConfig{ + PerWarpSigner: 250, + PerWarpMessageChunk: 3_200, + PerSignatureVerification: 100_000, + } + } + return GasConfig{ + PerWarpSigner: 500, + PerWarpMessageChunk: 3_200, + PerSignatureVerification: 200_000, + } +} diff --git a/precompile/contracts/warp/contract_test.go b/precompile/contracts/warp/contract_test.go index ec2263aedd..21f3fd95e0 100644 --- a/precompile/contracts/warp/contract_test.go +++ b/precompile/contracts/warp/contract_test.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/libevm/core/vm" "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/precompile/contract" "github.com/ava-labs/coreth/precompile/precompiletest" @@ -179,6 +180,8 @@ func TestSendWarpMessage(t *testing.T) { func TestGetVerifiedWarpMessage(t *testing.T) { networkID := uint32(54321) + preGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: false}) + postGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: true}) callerAddr := common.HexToAddress("0x0123") sourceAddress := common.HexToAddress("0x456789") sourceChainID := ids.GenerateTestID() @@ -220,7 +223,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -248,6 +252,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedRes: func() []byte { @@ -269,7 +274,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(set.NewBits(0)) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -297,6 +303,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(set.NewBits(0, 1)) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedRes: func() []byte { @@ -313,6 +320,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedRes: func() []byte { @@ -330,7 +338,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -353,6 +362,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: true, ExpectedRes: func() []byte { @@ -367,6 +377,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { Caller: callerAddr, InputFn: func(testing.TB) []byte { return getVerifiedWarpMsg }, Predicates: []predicate.Predicate{warpMessagePredicate}, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), @@ -378,7 +389,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -389,7 +401,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -400,7 +413,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -411,7 +425,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), ReadOnly: false, ExpectedErr: errInvalidAddressedPayload.Error(), }, @@ -420,6 +435,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { InputFn: func(testing.TB) []byte { return append(WarpABI.Methods["getVerifiedWarpMessage"].ID, new(big.Int).SetInt64(math.MaxInt64).Bytes()...) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedErr: errInvalidIndexInput.Error(), @@ -431,6 +447,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { require.NoError(t, err) return res }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedErr: errInvalidIndexInput.Error(), @@ -442,10 +459,138 @@ func TestGetVerifiedWarpMessage(t *testing.T) { require.NoError(t, err) return res[:len(res)-2] }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedErr: errInvalidIndexInput.Error(), }, + "get message success granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpMsg }, + Predicates: []predicate.Predicate{warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + ReadOnly: false, + ExpectedRes: func() []byte { + res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ + Message: WarpMessage{ + SourceChainID: common.Hash(sourceChainID), + OriginSenderAddress: sourceAddress, + Payload: packagedPayloadBytes, + }, + Valid: true, + }) + if err != nil { + panic(err) + } + return res + }(), + }, + "get message success non-zero index granite": { + Caller: callerAddr, + InputFn: func(t testing.TB) []byte { + input, err := PackGetVerifiedWarpMessage(1) + require.NoError(t, err) + return input + }, + Predicates: []predicate.Predicate{{}, warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(set.NewBits(0)) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + ReadOnly: false, + ExpectedRes: func() []byte { + res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ + Message: WarpMessage{ + SourceChainID: common.Hash(sourceChainID), + OriginSenderAddress: sourceAddress, + Payload: packagedPayloadBytes, + }, + Valid: true, + }) + if err != nil { + panic(err) + } + return res + }(), + }, + "get message success readOnly granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpMsg }, + Predicates: []predicate.Predicate{warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + ReadOnly: true, + ExpectedRes: func() []byte { + res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ + Message: WarpMessage{ + SourceChainID: common.Hash(sourceChainID), + OriginSenderAddress: sourceAddress, + Payload: packagedPayloadBytes, + }, + Valid: true, + }) + if err != nil { + panic(err) + } + return res + }(), + }, + "get message out of gas granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpMsg }, + Predicates: []predicate.Predicate{warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, + ReadOnly: false, + ExpectedErr: vm.ErrOutOfGas.Error(), + }, + "get message invalid predicate packing granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpMsg }, + Predicates: []predicate.Predicate{invalidPackedPredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + ReadOnly: false, + ExpectedErr: errInvalidPredicateBytes.Error(), + }, + "get message invalid warp message granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpMsg }, + Predicates: []predicate.Predicate{invalidWarpMsgPredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + ReadOnly: false, + ExpectedErr: errInvalidWarpMsg.Error(), + }, + "get message invalid addressed payload granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpMsg }, + Predicates: []predicate.Predicate{invalidAddressedPredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), + ReadOnly: false, + ExpectedErr: errInvalidAddressedPayload.Error(), + }, } precompiletest.RunPrecompileTests(t, Module, tests) @@ -453,6 +598,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { func TestGetVerifiedWarpBlockHash(t *testing.T) { networkID := uint32(54321) + preGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: false}) + postGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: false}) callerAddr := common.HexToAddress("0x0123") sourceChainID := ids.GenerateTestID() blockHash := ids.GenerateTestID() @@ -490,7 +637,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -517,6 +665,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedRes: func() []byte { @@ -538,7 +687,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(set.NewBits(0)) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -565,6 +715,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(set.NewBits(0, 1)) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedRes: func() []byte { @@ -581,6 +732,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedRes: func() []byte { @@ -598,7 +750,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -620,6 +773,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: true, ExpectedRes: func() []byte { @@ -634,6 +788,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { Caller: callerAddr, InputFn: func(testing.TB) []byte { return getVerifiedWarpBlockHash }, Predicates: []predicate.Predicate{warpMessagePredicate}, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), @@ -645,7 +800,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -656,7 +812,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -667,7 +824,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -678,7 +836,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidHashPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidHashPredicate)), ReadOnly: false, ExpectedErr: errInvalidBlockHashPayload.Error(), }, @@ -687,6 +846,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { InputFn: func(testing.TB) []byte { return append(WarpABI.Methods["getVerifiedWarpBlockHash"].ID, new(big.Int).SetInt64(math.MaxInt64).Bytes()...) }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedErr: errInvalidIndexInput.Error(), @@ -698,6 +858,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { require.NoError(t, err) return res }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedErr: errInvalidIndexInput.Error(), @@ -709,10 +870,135 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { require.NoError(t, err) return res[:len(res)-2] }, + Rules: extras.AvalancheRules{IsGranite: false}, SuppliedGas: GetVerifiedWarpMessageBaseCost, ReadOnly: false, ExpectedErr: errInvalidIndexInput.Error(), }, + "get message success granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpBlockHash }, + Predicates: []predicate.Predicate{warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + ReadOnly: false, + ExpectedRes: func() []byte { + res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ + WarpBlockHash: WarpBlockHash{ + SourceChainID: common.Hash(sourceChainID), + BlockHash: common.Hash(blockHash), + }, + Valid: true, + }) + if err != nil { + panic(err) + } + return res + }(), + }, + "get message success non-zero index granite": { + Caller: callerAddr, + InputFn: func(t testing.TB) []byte { + input, err := PackGetVerifiedWarpBlockHash(1) + require.NoError(t, err) + return input + }, + Predicates: []predicate.Predicate{{}, warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(set.NewBits(0)) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + ReadOnly: false, + ExpectedRes: func() []byte { + res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ + WarpBlockHash: WarpBlockHash{ + SourceChainID: common.Hash(sourceChainID), + BlockHash: common.Hash(blockHash), + }, + Valid: true, + }) + if err != nil { + panic(err) + } + return res + }(), + }, + "get message success readOnly granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpBlockHash }, + Predicates: []predicate.Predicate{warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + ReadOnly: true, + ExpectedRes: func() []byte { + res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ + WarpBlockHash: WarpBlockHash{ + SourceChainID: common.Hash(sourceChainID), + BlockHash: common.Hash(blockHash), + }, + Valid: true, + }) + if err != nil { + panic(err) + } + return res + }(), + }, + "get message out of gas granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpBlockHash }, + Predicates: []predicate.Predicate{warpMessagePredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, + ReadOnly: false, + ExpectedErr: vm.ErrOutOfGas.Error(), + }, + "get message invalid predicate packing granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpBlockHash }, + Predicates: []predicate.Predicate{invalidPackedPredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + ReadOnly: false, + ExpectedErr: errInvalidPredicateBytes.Error(), + }, + "get message invalid warp message granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpBlockHash }, + Predicates: []predicate.Predicate{invalidWarpMsgPredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + ReadOnly: false, + ExpectedErr: errInvalidWarpMsg.Error(), + }, + "get message invalid block hash payload granite": { + Caller: callerAddr, + InputFn: func(testing.TB) []byte { return getVerifiedWarpBlockHash }, + Predicates: []predicate.Predicate{invalidHashPredicate}, + SetupBlockContext: func(mbc *contract.MockBlockContext) { + mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) + }, + Rules: extras.AvalancheRules{IsGranite: true}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + postGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidHashPredicate)), + ReadOnly: false, + ExpectedErr: errInvalidBlockHashPayload.Error(), + }, } precompiletest.RunPrecompileTests(t, Module, tests) diff --git a/precompile/contracts/warp/contract_warp_handler.go b/precompile/contracts/warp/contract_warp_handler.go index 7dd790b599..45c43bb708 100644 --- a/precompile/contracts/warp/contract_warp_handler.go +++ b/precompile/contracts/warp/contract_warp_handler.go @@ -66,9 +66,11 @@ func handleWarpMessage(accessibleState contract.AccessibleState, input []byte, s return handler.packFailed(), remainingGas, nil } + warpGasConfig := CurrentGasConfig(accessibleState.GetRules()) + // Note: we charge for the size of the message during both predicate verification and each time the message is read during // EVM execution because each execution incurs an additional read cost. - msgBytesGas, overflow := math.SafeMul(GasCostPerWarpMessageChunk, uint64(len(pred))) + msgBytesGas, overflow := math.SafeMul(warpGasConfig.PerWarpMessageChunk, uint64(len(pred))) if overflow { return nil, 0, vm.ErrOutOfGas } diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index 93bc288c3d..a67046814f 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -25,6 +25,7 @@ import ( "github.com/ava-labs/libevm/common" "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/precompile/precompiletest" "github.com/ava-labs/coreth/utils" @@ -54,6 +55,9 @@ var ( numTestVdrs = 10_000 testVdrs []*testValidator vdrs map[ids.NodeID]*validators.GetValidatorOutput + + graniteRules precompileconfig.Rules + graniteGasConfig GasConfig ) func init() { @@ -96,6 +100,9 @@ func init() { panic(err) } + graniteRules = extras.AvalancheRules{IsGranite: true} + graniteGasConfig = CurrentGasConfig(graniteRules) + for _, testVdr := range testVdrs { blsSignature, err := testVdr.sk.Sign(unsignedMsg.Bytes()) if err != nil { @@ -230,7 +237,8 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate p }, }, Predicate: predicate, - Gas: GasCostPerSignatureVerification + uint64(len(predicate))*GasCostPerWarpMessageChunk + numKeys*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(predicate))*graniteGasConfig.PerWarpMessageChunk + numKeys*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -307,7 +315,8 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -337,7 +346,8 @@ func TestInvalidPredicatePacking(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidPredicateBytes, } @@ -368,7 +378,8 @@ func TestInvalidWarpMessage(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidWarpMsg, } @@ -412,7 +423,8 @@ func TestInvalidAddressedPayload(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidWarpMsgPayload, } @@ -457,7 +469,8 @@ func TestInvalidBitSet(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errCannotGetNumSigners, } @@ -502,7 +515,8 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } @@ -543,10 +557,10 @@ func TestWarpMultiplePredicates(t *testing.T) { ) if valid { pred = validPredicate - expectedGas = GasCostPerSignatureVerification + uint64(len(validPredicate))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner + expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner expectedErr = nil } else { - expectedGas = GasCostPerSignatureVerification + uint64(len(invalidPredicate))*GasCostPerWarpMessageChunk + uint64(1)*GasCostPerWarpSigner + expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(invalidPredicate))*graniteGasConfig.PerWarpMessageChunk + uint64(1)*graniteGasConfig.PerWarpSigner pred = invalidPredicate expectedErr = errFailedVerification } @@ -560,6 +574,7 @@ func TestWarpMultiplePredicates(t *testing.T) { }, }, Predicate: pred, + Rules: graniteRules, Gas: expectedGas, GasErr: nil, ExpectedErr: expectedErr, @@ -604,7 +619,8 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner, + Rules: graniteRules, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } @@ -620,7 +636,7 @@ func TestWarpNoValidatorsAndOverflowUseSameGas(t *testing.T) { PChainHeight: 1, } pred = createPredicate(0) - expectedGas = GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk ) noValidators := precompiletest.PredicateTest{ Config: config, @@ -628,6 +644,7 @@ func TestWarpNoValidatorsAndOverflowUseSameGas(t *testing.T) { SnowCtx: createSnowCtx(t, nil /*=validators*/), // No validators in state ProposerVMBlockCtx: proposervmContext, }, + Rules: graniteRules, Predicate: pred, Gas: expectedGas, GasErr: nil, @@ -647,6 +664,7 @@ func TestWarpNoValidatorsAndOverflowUseSameGas(t *testing.T) { ProposerVMBlockCtx: proposervmContext, }, Predicate: pred, + Rules: graniteRules, Gas: expectedGas, GasErr: nil, ExpectedErr: safemath.ErrOverflow, diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 7a81b1b622..406d20d3bd 100644 --- a/precompile/precompileconfig/config.go +++ b/precompile/precompileconfig/config.go @@ -48,7 +48,7 @@ type PredicateContext struct { // The bitset is stored in the block, so that historical blocks can be re-verified // without calling VerifyPredicate. type Predicater interface { - PredicateGas(pred predicate.Predicate) (uint64, error) + PredicateGas(pred predicate.Predicate, rules Rules) (uint64, error) VerifyPredicate(predicateContext *PredicateContext, pred predicate.Predicate) error } @@ -78,3 +78,8 @@ type ChainConfig interface { // IsDurango returns true if the time is after Durango. IsDurango(time uint64) bool } + +// Rules defines the interface that provides information about the current rules of the chain. +type Rules interface { + IsGraniteActivated() bool +} diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index dea7588537..bfef6f3f95 100644 --- a/precompile/precompileconfig/mocks.go +++ b/precompile/precompileconfig/mocks.go @@ -42,18 +42,18 @@ func (m *MockPredicater) EXPECT() *MockPredicaterMockRecorder { } // PredicateGas mocks base method. -func (m *MockPredicater) PredicateGas(pred predicate.Predicate) (uint64, error) { +func (m *MockPredicater) PredicateGas(pred predicate.Predicate, rules Rules) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PredicateGas", pred) + ret := m.ctrl.Call(m, "PredicateGas", pred, rules) ret0, _ := ret[0].(uint64) ret1, _ := ret[1].(error) return ret0, ret1 } // PredicateGas indicates an expected call of PredicateGas. -func (mr *MockPredicaterMockRecorder) PredicateGas(pred any) *gomock.Call { +func (mr *MockPredicaterMockRecorder) PredicateGas(pred, rules any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PredicateGas", reflect.TypeOf((*MockPredicater)(nil).PredicateGas), pred) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PredicateGas", reflect.TypeOf((*MockPredicater)(nil).PredicateGas), pred, rules) } // VerifyPredicate mocks base method. diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index 11303e6566..b1ac2a09ee 100644 --- a/precompile/precompiletest/test_precompile.go +++ b/precompile/precompiletest/test_precompile.go @@ -17,6 +17,7 @@ import ( "go.uber.org/mock/gomock" "github.com/ava-labs/coreth/core/extstate" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/precompile/contract" "github.com/ava-labs/coreth/precompile/modules" "github.com/ava-labs/coreth/precompile/precompileconfig" @@ -54,6 +55,9 @@ type PrecompileTest struct { // ChainConfig is the chain config to use for the precompile's block context // If nil, the default chain config will be used. ChainConfig precompileconfig.ChainConfig + // Rules is the rules to use for the precompile's block context + // If nil, the default rules will be used. + Rules precompileconfig.Rules } type PrecompileRunparams struct { @@ -91,6 +95,10 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes t.Helper() contractAddress := module.Address + if test.Rules == nil { + test.Rules = extras.AvalancheRules{IsGranite: true} + } + ctrl := gomock.NewController(t) chainConfig := test.ChainConfig @@ -101,11 +109,11 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes } blockContext := contract.NewMockBlockContext(ctrl) + blockContext.EXPECT().Timestamp().Return(uint64(time.Now().Unix())).AnyTimes() if test.SetupBlockContext != nil { test.SetupBlockContext(blockContext) } else { blockContext.EXPECT().Number().Return(big.NewInt(0)).AnyTimes() - blockContext.EXPECT().Timestamp().Return(uint64(time.Now().Unix())).AnyTimes() } snowContext := snowtest.Context(t, snowtest.CChainID) @@ -114,6 +122,7 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes accessibleState.EXPECT().GetBlockContext().Return(blockContext).AnyTimes() accessibleState.EXPECT().GetSnowContext().Return(snowContext).AnyTimes() accessibleState.EXPECT().GetChainConfig().Return(chainConfig).AnyTimes() + accessibleState.EXPECT().GetRules().Return(test.Rules).AnyTimes() if test.Config != nil { require.NoError(t, module.Configure(chainConfig, test.Config, state, blockContext)) diff --git a/precompile/precompiletest/test_predicate.go b/precompile/precompiletest/test_predicate.go index b3bdf576c6..6cbe903dcd 100644 --- a/precompile/precompiletest/test_predicate.go +++ b/precompile/precompiletest/test_predicate.go @@ -20,6 +20,7 @@ type PredicateTest struct { PredicateContext *precompileconfig.PredicateContext Predicate predicate.Predicate + Rules precompileconfig.Rules Gas uint64 GasErr error ExpectedErr error @@ -30,7 +31,7 @@ func (test PredicateTest) Run(t testing.TB) { require := require.New(t) predicater := test.Config.(precompileconfig.Predicater) - predicateGas, predicateGasErr := predicater.PredicateGas(test.Predicate) + predicateGas, predicateGasErr := predicater.PredicateGas(test.Predicate, test.Rules) require.ErrorIs(predicateGasErr, test.GasErr) if test.GasErr != nil { return