From 66d6ec832650552ff46aed30fe76982f0a459b4c Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 30 Sep 2025 17:53:03 -0400 Subject: [PATCH 01/15] Add new warp gas costs for granite --- core/predicate_check_test.go | 4 +-- core/state_transition.go | 2 +- params/extras/network_upgrades.go | 16 ++++++++++ precompile/contract/utils.go | 4 +++ precompile/contracts/warp/config.go | 10 ++++--- precompile/contracts/warp/contract.go | 28 ++++++++++++++--- precompile/contracts/warp/contract_test.go | 30 ++++++++++--------- .../contracts/warp/contract_warp_handler.go | 4 ++- precompile/contracts/warp/predicate_test.go | 29 +++++++++++------- precompile/precompileconfig/config.go | 12 +++++++- precompile/precompileconfig/mocks.go | 22 +++++++++++--- precompile/precompiletest/test_predicate.go | 3 +- 12 files changed, 122 insertions(+), 42 deletions(-) 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..d1a62380d4 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -264,6 +264,22 @@ type AvalancheRules struct { IsGranite bool } +func (a *AvalancheRules) IsDurangoActivated() bool { + return a.IsDurango +} + +func (a *AvalancheRules) IsEtnaActivated() bool { + return a.IsEtna +} + +func (a *AvalancheRules) IsFortunaActivated() bool { + return a.IsFortuna +} + +func (a *AvalancheRules) IsGraniteActivated() bool { + return a.IsGranite +} + func (n *NetworkUpgrades) GetAvalancheRules(timestamp uint64) AvalancheRules { return AvalancheRules{ IsApricotPhase1: n.IsApricotPhase1(timestamp), diff --git a/precompile/contract/utils.go b/precompile/contract/utils.go index 7ec7424d2d..4711abe0f1 100644 --- a/precompile/contract/utils.go +++ b/precompile/contract/utils.go @@ -59,3 +59,7 @@ func ParseABI(rawABI string) abi.ABI { return parsed } + +func IsGraniteActivated(evm AccessibleState) bool { + return evm.GetChainConfig().IsGranite(evm.GetBlockContext().Timestamp()) +} diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 843a3b7dcc..75383a47ed 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 := WarpPredicateGasConfig(rules.IsGraniteActivated()) + + 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..27ccbe7fb1 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -31,12 +31,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 WarpGasConfig 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 warp message signature + PerSignatureVerification uint64 +} + var ( errInvalidSendInput = errors.New("invalid sendWarpMessage input") errInvalidIndexInput = errors.New("invalid index to specify warp message") @@ -343,3 +348,18 @@ func createWarpPrecompile() contract.StatefulPrecompiledContract { } return statefulContract } + +func WarpPredicateGasConfig(isGraniteActivated bool) WarpGasConfig { + if isGraniteActivated { + return WarpGasConfig{ + PerWarpSigner: 500, + PerWarpMessageChunk: 3_200, + PerSignatureVerification: 200_000, + } + } + return WarpGasConfig{ + PerWarpSigner: 250, + PerWarpMessageChunk: 3_200, + PerSignatureVerification: 100_000, + } +} diff --git a/precompile/contracts/warp/contract_test.go b/precompile/contracts/warp/contract_test.go index ec2263aedd..8d8becb94f 100644 --- a/precompile/contracts/warp/contract_test.go +++ b/precompile/contracts/warp/contract_test.go @@ -179,6 +179,7 @@ func TestSendWarpMessage(t *testing.T) { func TestGetVerifiedWarpMessage(t *testing.T) { networkID := uint32(54321) + gasConfig := WarpPredicateGasConfig(false) callerAddr := common.HexToAddress("0x0123") sourceAddress := common.HexToAddress("0x456789") sourceChainID := ids.GenerateTestID() @@ -220,7 +221,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -269,7 +270,7 @@ 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)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -330,7 +331,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -378,7 +379,7 @@ 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, + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -389,7 +390,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -400,7 +401,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -411,7 +412,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), ReadOnly: false, ExpectedErr: errInvalidAddressedPayload.Error(), }, @@ -453,6 +454,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { func TestGetVerifiedWarpBlockHash(t *testing.T) { networkID := uint32(54321) + gasConfig := WarpPredicateGasConfig(false) callerAddr := common.HexToAddress("0x0123") sourceChainID := ids.GenerateTestID() blockHash := ids.GenerateTestID() @@ -490,7 +492,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -538,7 +540,7 @@ 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)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -598,7 +600,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -645,7 +647,7 @@ 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, + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -656,7 +658,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -667,7 +669,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -678,7 +680,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidHashPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidHashPredicate)), ReadOnly: false, ExpectedErr: errInvalidBlockHashPayload.Error(), }, diff --git a/precompile/contracts/warp/contract_warp_handler.go b/precompile/contracts/warp/contract_warp_handler.go index 7dd790b599..d77c9f64bf 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 := WarpPredicateGasConfig(contract.IsGraniteActivated(accessibleState)) + // 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 1ad8bef7f7..d1717bbafb 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -202,6 +202,7 @@ func createSnowCtx(tb testing.TB, validatorRanges []validatorRange) *snow.Contex } func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate predicate.Predicate) precompiletest.PredicateTest { + gasConfig := WarpPredicateGasConfig(false) return precompiletest.PredicateTest{ Config: NewDefaultConfig(utils.NewUint64(0)), PredicateContext: &precompileconfig.PredicateContext{ @@ -211,7 +212,7 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate p }, }, Predicate: predicate, - Gas: GasCostPerSignatureVerification + uint64(len(predicate))*GasCostPerWarpMessageChunk + numKeys*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(predicate))*gasConfig.PerWarpMessageChunk + numKeys*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -225,6 +226,7 @@ func TestWarpMessageFromPrimaryNetwork(t *testing.T) { func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigners bool) { require := require.New(t) + gasConfig := WarpPredicateGasConfig(false) numKeys := 10 cChainID := ids.GenerateTestID() addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) @@ -288,7 +290,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -297,6 +299,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner } func TestInvalidPredicatePacking(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -318,7 +321,7 @@ func TestInvalidPredicatePacking(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errInvalidPredicateBytes, } @@ -326,6 +329,7 @@ func TestInvalidPredicatePacking(t *testing.T) { } func TestInvalidWarpMessage(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -349,7 +353,7 @@ func TestInvalidWarpMessage(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errInvalidWarpMsg, } @@ -357,6 +361,7 @@ func TestInvalidWarpMessage(t *testing.T) { } func TestInvalidAddressedPayload(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -393,7 +398,7 @@ func TestInvalidAddressedPayload(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errInvalidWarpMsgPayload, } @@ -401,6 +406,7 @@ func TestInvalidAddressedPayload(t *testing.T) { } func TestInvalidBitSet(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) require.NoError(t, err) unsignedMsg, err := avalancheWarp.NewUnsignedMessage( @@ -438,7 +444,7 @@ func TestInvalidBitSet(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errCannotGetNumSigners, } @@ -446,6 +452,7 @@ func TestInvalidBitSet(t *testing.T) { } func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -483,7 +490,7 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } @@ -493,6 +500,7 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { // multiple messages all correct, multiple messages all incorrect, mixed bag func TestWarpMultiplePredicates(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -524,10 +532,10 @@ func TestWarpMultiplePredicates(t *testing.T) { ) if valid { pred = validPredicate - expectedGas = GasCostPerSignatureVerification + uint64(len(validPredicate))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner + expectedGas = gasConfig.PerSignatureVerification + uint64(len(validPredicate))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner expectedErr = nil } else { - expectedGas = GasCostPerSignatureVerification + uint64(len(invalidPredicate))*GasCostPerWarpMessageChunk + uint64(1)*GasCostPerWarpSigner + expectedGas = gasConfig.PerSignatureVerification + uint64(len(invalidPredicate))*gasConfig.PerWarpMessageChunk + uint64(1)*gasConfig.PerWarpSigner pred = invalidPredicate expectedErr = errFailedVerification } @@ -551,6 +559,7 @@ func TestWarpMultiplePredicates(t *testing.T) { } func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -585,7 +594,7 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 7a81b1b622..45224772be 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 } @@ -77,4 +77,14 @@ type Accepter interface { type ChainConfig interface { // IsDurango returns true if the time is after Durango. IsDurango(time uint64) bool + // IsGranite returns true if the time is after Granite. + IsGranite(time uint64) bool +} + +// Rules defines the interface that provides information about the current rules of the chain. +type Rules interface { + IsDurangoActivated() bool + IsEtnaActivated() bool + IsFortunaActivated() bool + IsGraniteActivated() bool } diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index dea7588537..73ca03434c 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. @@ -202,6 +202,20 @@ func (mr *MockChainConfigMockRecorder) IsDurango(time any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDurango", reflect.TypeOf((*MockChainConfig)(nil).IsDurango), time) } +// IsGranite mocks base method. +func (m *MockChainConfig) IsGranite(time uint64) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsGranite", time) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsGranite indicates an expected call of IsGranite. +func (mr *MockChainConfigMockRecorder) IsGranite(time any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsGranite", reflect.TypeOf((*MockChainConfig)(nil).IsGranite), time) +} + // MockAccepter is a mock of Accepter interface. type MockAccepter struct { ctrl *gomock.Controller diff --git a/precompile/precompiletest/test_predicate.go b/precompile/precompiletest/test_predicate.go index b3bdf576c6..99a05792c5 100644 --- a/precompile/precompiletest/test_predicate.go +++ b/precompile/precompiletest/test_predicate.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/precompile/precompileconfig" + "github.com/ava-labs/coreth/params/extras" ) // PredicateTest defines a unit test/benchmark for verifying a precompile predicate. @@ -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, &extras.AvalancheRules{}) require.ErrorIs(predicateGasErr, test.GasErr) if test.GasErr != nil { return From 9f79801eb55daa1c53d168cbee2940c6811de067 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 30 Sep 2025 17:53:03 -0400 Subject: [PATCH 02/15] Add new warp gas costs for granite --- core/predicate_check_test.go | 4 +-- core/state_transition.go | 2 +- params/extras/network_upgrades.go | 16 ++++++++++ precompile/contract/utils.go | 4 +++ precompile/contracts/warp/config.go | 10 ++++--- precompile/contracts/warp/contract.go | 28 ++++++++++++++--- precompile/contracts/warp/contract_test.go | 30 ++++++++++--------- .../contracts/warp/contract_warp_handler.go | 4 ++- precompile/contracts/warp/predicate_test.go | 29 +++++++++++------- precompile/precompileconfig/config.go | 12 +++++++- precompile/precompileconfig/mocks.go | 22 +++++++++++--- precompile/precompiletest/test_precompile.go | 3 +- precompile/precompiletest/test_predicate.go | 3 +- 13 files changed, 124 insertions(+), 43 deletions(-) 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..d1a62380d4 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -264,6 +264,22 @@ type AvalancheRules struct { IsGranite bool } +func (a *AvalancheRules) IsDurangoActivated() bool { + return a.IsDurango +} + +func (a *AvalancheRules) IsEtnaActivated() bool { + return a.IsEtna +} + +func (a *AvalancheRules) IsFortunaActivated() bool { + return a.IsFortuna +} + +func (a *AvalancheRules) IsGraniteActivated() bool { + return a.IsGranite +} + func (n *NetworkUpgrades) GetAvalancheRules(timestamp uint64) AvalancheRules { return AvalancheRules{ IsApricotPhase1: n.IsApricotPhase1(timestamp), diff --git a/precompile/contract/utils.go b/precompile/contract/utils.go index 7ec7424d2d..4711abe0f1 100644 --- a/precompile/contract/utils.go +++ b/precompile/contract/utils.go @@ -59,3 +59,7 @@ func ParseABI(rawABI string) abi.ABI { return parsed } + +func IsGraniteActivated(evm AccessibleState) bool { + return evm.GetChainConfig().IsGranite(evm.GetBlockContext().Timestamp()) +} diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 843a3b7dcc..75383a47ed 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 := WarpPredicateGasConfig(rules.IsGraniteActivated()) + + 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..27ccbe7fb1 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -31,12 +31,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 WarpGasConfig 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 warp message signature + PerSignatureVerification uint64 +} + var ( errInvalidSendInput = errors.New("invalid sendWarpMessage input") errInvalidIndexInput = errors.New("invalid index to specify warp message") @@ -343,3 +348,18 @@ func createWarpPrecompile() contract.StatefulPrecompiledContract { } return statefulContract } + +func WarpPredicateGasConfig(isGraniteActivated bool) WarpGasConfig { + if isGraniteActivated { + return WarpGasConfig{ + PerWarpSigner: 500, + PerWarpMessageChunk: 3_200, + PerSignatureVerification: 200_000, + } + } + return WarpGasConfig{ + PerWarpSigner: 250, + PerWarpMessageChunk: 3_200, + PerSignatureVerification: 100_000, + } +} diff --git a/precompile/contracts/warp/contract_test.go b/precompile/contracts/warp/contract_test.go index ec2263aedd..8d8becb94f 100644 --- a/precompile/contracts/warp/contract_test.go +++ b/precompile/contracts/warp/contract_test.go @@ -179,6 +179,7 @@ func TestSendWarpMessage(t *testing.T) { func TestGetVerifiedWarpMessage(t *testing.T) { networkID := uint32(54321) + gasConfig := WarpPredicateGasConfig(false) callerAddr := common.HexToAddress("0x0123") sourceAddress := common.HexToAddress("0x456789") sourceChainID := ids.GenerateTestID() @@ -220,7 +221,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -269,7 +270,7 @@ 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)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -330,7 +331,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -378,7 +379,7 @@ 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, + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -389,7 +390,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -400,7 +401,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -411,7 +412,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), ReadOnly: false, ExpectedErr: errInvalidAddressedPayload.Error(), }, @@ -453,6 +454,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { func TestGetVerifiedWarpBlockHash(t *testing.T) { networkID := uint32(54321) + gasConfig := WarpPredicateGasConfig(false) callerAddr := common.HexToAddress("0x0123") sourceChainID := ids.GenerateTestID() blockHash := ids.GenerateTestID() @@ -490,7 +492,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -538,7 +540,7 @@ 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)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -598,7 +600,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(warpMessagePredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -645,7 +647,7 @@ 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, + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -656,7 +658,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -667,7 +669,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -678,7 +680,7 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + GasCostPerWarpMessageChunk*uint64(len(invalidHashPredicate)), + SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidHashPredicate)), ReadOnly: false, ExpectedErr: errInvalidBlockHashPayload.Error(), }, diff --git a/precompile/contracts/warp/contract_warp_handler.go b/precompile/contracts/warp/contract_warp_handler.go index 7dd790b599..d77c9f64bf 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 := WarpPredicateGasConfig(contract.IsGraniteActivated(accessibleState)) + // 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 1ad8bef7f7..d1717bbafb 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -202,6 +202,7 @@ func createSnowCtx(tb testing.TB, validatorRanges []validatorRange) *snow.Contex } func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate predicate.Predicate) precompiletest.PredicateTest { + gasConfig := WarpPredicateGasConfig(false) return precompiletest.PredicateTest{ Config: NewDefaultConfig(utils.NewUint64(0)), PredicateContext: &precompileconfig.PredicateContext{ @@ -211,7 +212,7 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate p }, }, Predicate: predicate, - Gas: GasCostPerSignatureVerification + uint64(len(predicate))*GasCostPerWarpMessageChunk + numKeys*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(predicate))*gasConfig.PerWarpMessageChunk + numKeys*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -225,6 +226,7 @@ func TestWarpMessageFromPrimaryNetwork(t *testing.T) { func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigners bool) { require := require.New(t) + gasConfig := WarpPredicateGasConfig(false) numKeys := 10 cChainID := ids.GenerateTestID() addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) @@ -288,7 +290,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -297,6 +299,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner } func TestInvalidPredicatePacking(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -318,7 +321,7 @@ func TestInvalidPredicatePacking(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errInvalidPredicateBytes, } @@ -326,6 +329,7 @@ func TestInvalidPredicatePacking(t *testing.T) { } func TestInvalidWarpMessage(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -349,7 +353,7 @@ func TestInvalidWarpMessage(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errInvalidWarpMsg, } @@ -357,6 +361,7 @@ func TestInvalidWarpMessage(t *testing.T) { } func TestInvalidAddressedPayload(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -393,7 +398,7 @@ func TestInvalidAddressedPayload(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errInvalidWarpMsgPayload, } @@ -401,6 +406,7 @@ func TestInvalidAddressedPayload(t *testing.T) { } func TestInvalidBitSet(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) require.NoError(t, err) unsignedMsg, err := avalancheWarp.NewUnsignedMessage( @@ -438,7 +444,7 @@ func TestInvalidBitSet(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numKeys)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, GasErr: errCannotGetNumSigners, } @@ -446,6 +452,7 @@ func TestInvalidBitSet(t *testing.T) { } func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -483,7 +490,7 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } @@ -493,6 +500,7 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { // multiple messages all correct, multiple messages all incorrect, mixed bag func TestWarpMultiplePredicates(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -524,10 +532,10 @@ func TestWarpMultiplePredicates(t *testing.T) { ) if valid { pred = validPredicate - expectedGas = GasCostPerSignatureVerification + uint64(len(validPredicate))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner + expectedGas = gasConfig.PerSignatureVerification + uint64(len(validPredicate))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner expectedErr = nil } else { - expectedGas = GasCostPerSignatureVerification + uint64(len(invalidPredicate))*GasCostPerWarpMessageChunk + uint64(1)*GasCostPerWarpSigner + expectedGas = gasConfig.PerSignatureVerification + uint64(len(invalidPredicate))*gasConfig.PerWarpMessageChunk + uint64(1)*gasConfig.PerWarpSigner pred = invalidPredicate expectedErr = errFailedVerification } @@ -551,6 +559,7 @@ func TestWarpMultiplePredicates(t *testing.T) { } func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { + gasConfig := WarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -585,7 +594,7 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: GasCostPerSignatureVerification + uint64(len(pred))*GasCostPerWarpMessageChunk + uint64(numSigners)*GasCostPerWarpSigner, + Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 7a81b1b622..45224772be 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 } @@ -77,4 +77,14 @@ type Accepter interface { type ChainConfig interface { // IsDurango returns true if the time is after Durango. IsDurango(time uint64) bool + // IsGranite returns true if the time is after Granite. + IsGranite(time uint64) bool +} + +// Rules defines the interface that provides information about the current rules of the chain. +type Rules interface { + IsDurangoActivated() bool + IsEtnaActivated() bool + IsFortunaActivated() bool + IsGraniteActivated() bool } diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index dea7588537..73ca03434c 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. @@ -202,6 +202,20 @@ func (mr *MockChainConfigMockRecorder) IsDurango(time any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDurango", reflect.TypeOf((*MockChainConfig)(nil).IsDurango), time) } +// IsGranite mocks base method. +func (m *MockChainConfig) IsGranite(time uint64) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsGranite", time) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsGranite indicates an expected call of IsGranite. +func (mr *MockChainConfigMockRecorder) IsGranite(time any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsGranite", reflect.TypeOf((*MockChainConfig)(nil).IsGranite), time) +} + // MockAccepter is a mock of Accepter interface. type MockAccepter struct { ctrl *gomock.Controller diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index 11303e6566..6600dd98fa 100644 --- a/precompile/precompiletest/test_precompile.go +++ b/precompile/precompiletest/test_precompile.go @@ -97,15 +97,16 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes if chainConfig == nil { mockChainConfig := precompileconfig.NewMockChainConfig(ctrl) mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(true) + mockChainConfig.EXPECT().IsGranite(gomock.Any()).AnyTimes().Return(false) chainConfig = mockChainConfig } 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) diff --git a/precompile/precompiletest/test_predicate.go b/precompile/precompiletest/test_predicate.go index b3bdf576c6..99a05792c5 100644 --- a/precompile/precompiletest/test_predicate.go +++ b/precompile/precompiletest/test_predicate.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/precompile/precompileconfig" + "github.com/ava-labs/coreth/params/extras" ) // PredicateTest defines a unit test/benchmark for verifying a precompile predicate. @@ -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, &extras.AvalancheRules{}) require.ErrorIs(predicateGasErr, test.GasErr) if test.GasErr != nil { return From 03ca047ea52db59028202c4fe42b1e92efb52ce7 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 30 Sep 2025 19:36:15 -0400 Subject: [PATCH 03/15] lint --- precompile/precompiletest/test_predicate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precompile/precompiletest/test_predicate.go b/precompile/precompiletest/test_predicate.go index 99a05792c5..4fa92b48d5 100644 --- a/precompile/precompiletest/test_predicate.go +++ b/precompile/precompiletest/test_predicate.go @@ -10,8 +10,8 @@ import ( "github.com/ava-labs/avalanchego/vms/evm/predicate" "github.com/stretchr/testify/require" - "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/precompile/precompileconfig" ) // PredicateTest defines a unit test/benchmark for verifying a precompile predicate. From fd4ea40c96e371441757c7e01ec71e21cdb68a11 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 1 Oct 2025 12:54:53 -0400 Subject: [PATCH 04/15] Rename struct --- precompile/contracts/warp/config.go | 2 +- precompile/contracts/warp/contract.go | 8 ++++---- precompile/contracts/warp/contract_test.go | 4 ++-- .../contracts/warp/contract_warp_handler.go | 2 +- precompile/contracts/warp/predicate_test.go | 18 +++++++++--------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 75383a47ed..cb0271f988 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -145,7 +145,7 @@ func (*Config) Accept(acceptCtx *precompileconfig.AcceptContext, blockHash commo // // If the payload of the warp message fails parsing, return a non-nil error invalidating the transaction. func (*Config) PredicateGas(pred predicate.Predicate, rules precompileconfig.Rules) (uint64, error) { - gasConfig := WarpPredicateGasConfig(rules.IsGraniteActivated()) + gasConfig := GetWarpPredicateGasConfig(rules.IsGraniteActivated()) totalGas := gasConfig.PerSignatureVerification bytesGasCost, overflow := math.SafeMul(gasConfig.PerWarpMessageChunk, uint64(len(pred))) diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index 27ccbe7fb1..c5eab41e60 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -33,7 +33,7 @@ const ( SendWarpMessageGasCostPerByte uint64 = contract.LogDataGas ) -type WarpGasConfig struct { +type WarpPredicateGasConfig 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) @@ -349,15 +349,15 @@ func createWarpPrecompile() contract.StatefulPrecompiledContract { return statefulContract } -func WarpPredicateGasConfig(isGraniteActivated bool) WarpGasConfig { +func GetWarpPredicateGasConfig(isGraniteActivated bool) WarpPredicateGasConfig { if isGraniteActivated { - return WarpGasConfig{ + return WarpPredicateGasConfig{ PerWarpSigner: 500, PerWarpMessageChunk: 3_200, PerSignatureVerification: 200_000, } } - return WarpGasConfig{ + return WarpPredicateGasConfig{ PerWarpSigner: 250, PerWarpMessageChunk: 3_200, PerSignatureVerification: 100_000, diff --git a/precompile/contracts/warp/contract_test.go b/precompile/contracts/warp/contract_test.go index 8d8becb94f..f368683f02 100644 --- a/precompile/contracts/warp/contract_test.go +++ b/precompile/contracts/warp/contract_test.go @@ -179,7 +179,7 @@ func TestSendWarpMessage(t *testing.T) { func TestGetVerifiedWarpMessage(t *testing.T) { networkID := uint32(54321) - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) callerAddr := common.HexToAddress("0x0123") sourceAddress := common.HexToAddress("0x456789") sourceChainID := ids.GenerateTestID() @@ -454,7 +454,7 @@ func TestGetVerifiedWarpMessage(t *testing.T) { func TestGetVerifiedWarpBlockHash(t *testing.T) { networkID := uint32(54321) - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) callerAddr := common.HexToAddress("0x0123") sourceChainID := ids.GenerateTestID() blockHash := ids.GenerateTestID() diff --git a/precompile/contracts/warp/contract_warp_handler.go b/precompile/contracts/warp/contract_warp_handler.go index d77c9f64bf..7da26b2cd9 100644 --- a/precompile/contracts/warp/contract_warp_handler.go +++ b/precompile/contracts/warp/contract_warp_handler.go @@ -66,7 +66,7 @@ func handleWarpMessage(accessibleState contract.AccessibleState, input []byte, s return handler.packFailed(), remainingGas, nil } - warpGasConfig := WarpPredicateGasConfig(contract.IsGraniteActivated(accessibleState)) + warpGasConfig := GetWarpPredicateGasConfig(contract.IsGraniteActivated(accessibleState)) // 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. diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index d1717bbafb..8830080a26 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -202,7 +202,7 @@ func createSnowCtx(tb testing.TB, validatorRanges []validatorRange) *snow.Contex } func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate predicate.Predicate) precompiletest.PredicateTest { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) return precompiletest.PredicateTest{ Config: NewDefaultConfig(utils.NewUint64(0)), PredicateContext: &precompileconfig.PredicateContext{ @@ -226,7 +226,7 @@ func TestWarpMessageFromPrimaryNetwork(t *testing.T) { func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigners bool) { require := require.New(t) - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) numKeys := 10 cChainID := ids.GenerateTestID() addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) @@ -299,7 +299,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner } func TestInvalidPredicatePacking(t *testing.T) { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -329,7 +329,7 @@ func TestInvalidPredicatePacking(t *testing.T) { } func TestInvalidWarpMessage(t *testing.T) { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -361,7 +361,7 @@ func TestInvalidWarpMessage(t *testing.T) { } func TestInvalidAddressedPayload(t *testing.T) { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -406,7 +406,7 @@ func TestInvalidAddressedPayload(t *testing.T) { } func TestInvalidBitSet(t *testing.T) { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) require.NoError(t, err) unsignedMsg, err := avalancheWarp.NewUnsignedMessage( @@ -452,7 +452,7 @@ func TestInvalidBitSet(t *testing.T) { } func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -500,7 +500,7 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { // multiple messages all correct, multiple messages all incorrect, mixed bag func TestWarpMultiplePredicates(t *testing.T) { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -559,7 +559,7 @@ func TestWarpMultiplePredicates(t *testing.T) { } func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { - gasConfig := WarpPredicateGasConfig(false) + gasConfig := GetWarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, From 17b9c343ec522dd675c79bc9d324c7c8a2d8c550 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 1 Oct 2025 13:04:52 -0400 Subject: [PATCH 05/15] Test granite --- precompile/precompiletest/test_precompile.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index 6600dd98fa..c636f2598e 100644 --- a/precompile/precompiletest/test_precompile.go +++ b/precompile/precompiletest/test_precompile.go @@ -66,10 +66,15 @@ type PrecompileRunparams struct { } func (test PrecompileTest) Run(t *testing.T, module modules.Module) { + test.run(t, module, false) + test.run(t, module, true) +} + +func (test PrecompileTest) run(t *testing.T, module modules.Module, isGraniteActivated bool) { state := newTestStateDB(t, map[common.Address][]predicate.Predicate{ module.Address: test.Predicates, }) - runParams := test.setup(t, module, state) + runParams := test.setup(t, module, state, isGraniteActivated) if runParams.Input != nil { ret, remainingGas, err := module.Contract.Run(runParams.AccessibleState, runParams.Caller, runParams.ContractAddress, runParams.Input, runParams.SuppliedGas, runParams.ReadOnly) @@ -87,7 +92,7 @@ func (test PrecompileTest) Run(t *testing.T, module modules.Module) { } } -func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *testStateDB) PrecompileRunparams { +func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *testStateDB, isGraniteActivated bool) PrecompileRunparams { t.Helper() contractAddress := module.Address @@ -97,7 +102,7 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes if chainConfig == nil { mockChainConfig := precompileconfig.NewMockChainConfig(ctrl) mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(true) - mockChainConfig.EXPECT().IsGranite(gomock.Any()).AnyTimes().Return(false) + mockChainConfig.EXPECT().IsGranite(gomock.Any()).AnyTimes().Return(isGraniteActivated) chainConfig = mockChainConfig } From f69660b86714fdcdb434dbc08181e1f993aeaf9a Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 1 Oct 2025 14:45:14 -0400 Subject: [PATCH 06/15] Review feedback --- params/extras/network_upgrades.go | 12 ------------ precompile/contracts/warp/contract.go | 16 ++++++++-------- precompile/precompileconfig/config.go | 3 --- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/params/extras/network_upgrades.go b/params/extras/network_upgrades.go index d1a62380d4..1523880775 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -264,18 +264,6 @@ type AvalancheRules struct { IsGranite bool } -func (a *AvalancheRules) IsDurangoActivated() bool { - return a.IsDurango -} - -func (a *AvalancheRules) IsEtnaActivated() bool { - return a.IsEtna -} - -func (a *AvalancheRules) IsFortunaActivated() bool { - return a.IsFortuna -} - func (a *AvalancheRules) IsGraniteActivated() bool { return a.IsGranite } diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index c5eab41e60..f179381caa 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -33,7 +33,7 @@ const ( SendWarpMessageGasCostPerByte uint64 = contract.LogDataGas ) -type WarpPredicateGasConfig struct { +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) @@ -349,17 +349,17 @@ func createWarpPrecompile() contract.StatefulPrecompiledContract { return statefulContract } -func GetWarpPredicateGasConfig(isGraniteActivated bool) WarpPredicateGasConfig { +func GetWarpPredicateGasConfig(isGraniteActivated bool) GasConfig { if isGraniteActivated { - return WarpPredicateGasConfig{ - PerWarpSigner: 500, + return GasConfig{ + PerWarpSigner: 250, PerWarpMessageChunk: 3_200, - PerSignatureVerification: 200_000, + PerSignatureVerification: 100_000, } } - return WarpPredicateGasConfig{ - PerWarpSigner: 250, + return GasConfig{ + PerWarpSigner: 500, PerWarpMessageChunk: 3_200, - PerSignatureVerification: 100_000, + PerSignatureVerification: 200_000, } } diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 45224772be..42eb837005 100644 --- a/precompile/precompileconfig/config.go +++ b/precompile/precompileconfig/config.go @@ -83,8 +83,5 @@ type ChainConfig interface { // Rules defines the interface that provides information about the current rules of the chain. type Rules interface { - IsDurangoActivated() bool - IsEtnaActivated() bool - IsFortunaActivated() bool IsGraniteActivated() bool } From d0ce926ac2727ae01fb2f68d3bdfaf1388c7e063 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 2 Oct 2025 16:33:45 -0400 Subject: [PATCH 07/15] Implement new granite gas costs --- params/extras/network_upgrades.go | 2 +- params/hooks_libevm.go | 5 + precompile/contract/interfaces.go | 1 + precompile/contract/mocks.go | 14 + precompile/contracts/warp/config.go | 4 +- precompile/contracts/warp/contract.go | 5 +- precompile/contracts/warp/contract_test.go | 316 +++++++++++++++++- .../contracts/warp/contract_warp_handler.go | 2 +- precompile/contracts/warp/predicate_test.go | 47 +-- precompile/contracts/warp/test_predicate.go | 84 +++++ precompile/precompiletest/test_precompile.go | 11 +- precompile/precompiletest/test_predicate.go | 4 +- 12 files changed, 452 insertions(+), 43 deletions(-) create mode 100644 precompile/contracts/warp/test_predicate.go diff --git a/params/extras/network_upgrades.go b/params/extras/network_upgrades.go index 1523880775..d4f834609a 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -264,7 +264,7 @@ type AvalancheRules struct { IsGranite bool } -func (a *AvalancheRules) IsGraniteActivated() bool { +func (a AvalancheRules) IsGraniteActivated() bool { return a.IsGranite } diff --git a/params/hooks_libevm.go b/params/hooks_libevm.go index 78633266e8..3e8c4cfd18 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 { + extra := GetExtra(a.GetPrecompileEnv().ChainConfig()) + return extra.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 cb0271f988..8563243305 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -145,7 +145,9 @@ func (*Config) Accept(acceptCtx *precompileconfig.AcceptContext, blockHash commo // // If the payload of the warp message fails parsing, return a non-nil error invalidating the transaction. func (*Config) PredicateGas(pred predicate.Predicate, rules precompileconfig.Rules) (uint64, error) { - gasConfig := GetWarpPredicateGasConfig(rules.IsGraniteActivated()) + gasConfig := CurrentGasConfig(rules) + + fmt.Printf("warp predicate gas config: %+v\n", gasConfig) totalGas := gasConfig.PerSignatureVerification bytesGasCost, overflow := math.SafeMul(gasConfig.PerWarpMessageChunk, uint64(len(pred))) diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index f179381caa..64d916794e 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 ( @@ -349,8 +350,8 @@ func createWarpPrecompile() contract.StatefulPrecompiledContract { return statefulContract } -func GetWarpPredicateGasConfig(isGraniteActivated bool) GasConfig { - if isGraniteActivated { +func CurrentGasConfig(rules precompileconfig.Rules) GasConfig { + if rules.IsGraniteActivated() { return GasConfig{ PerWarpSigner: 250, PerWarpMessageChunk: 3_200, diff --git a/precompile/contracts/warp/contract_test.go b/precompile/contracts/warp/contract_test.go index f368683f02..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,7 +180,8 @@ func TestSendWarpMessage(t *testing.T) { func TestGetVerifiedWarpMessage(t *testing.T) { networkID := uint32(54321) - gasConfig := GetWarpPredicateGasConfig(false) + preGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: false}) + postGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: true}) callerAddr := common.HexToAddress("0x0123") sourceAddress := common.HexToAddress("0x456789") sourceChainID := ids.GenerateTestID() @@ -221,7 +223,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -249,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 { @@ -270,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 + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -298,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 { @@ -314,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 { @@ -331,7 +338,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ @@ -354,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 { @@ -368,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(), @@ -379,7 +389,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -390,7 +401,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -401,7 +413,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -412,7 +425,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidAddressedPredicate)), ReadOnly: false, ExpectedErr: errInvalidAddressedPayload.Error(), }, @@ -421,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(), @@ -432,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(), @@ -443,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) @@ -454,7 +598,8 @@ func TestGetVerifiedWarpMessage(t *testing.T) { func TestGetVerifiedWarpBlockHash(t *testing.T) { networkID := uint32(54321) - gasConfig := GetWarpPredicateGasConfig(false) + preGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: false}) + postGraniteGasConfig := CurrentGasConfig(extras.AvalancheRules{IsGranite: false}) callerAddr := common.HexToAddress("0x0123") sourceChainID := ids.GenerateTestID() blockHash := ids.GenerateTestID() @@ -492,7 +637,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -519,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 { @@ -540,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 + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: false, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -567,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 { @@ -583,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 { @@ -600,7 +750,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)), ReadOnly: true, ExpectedRes: func() []byte { res, err := PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ @@ -622,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 { @@ -636,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(), @@ -647,7 +800,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(warpMessagePredicate)) - 1, ReadOnly: false, ExpectedErr: vm.ErrOutOfGas.Error(), }, @@ -658,7 +812,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidPackedPredicate)), ReadOnly: false, ExpectedErr: errInvalidPredicateBytes.Error(), }, @@ -669,7 +824,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidWarpMsgPredicate)), ReadOnly: false, ExpectedErr: errInvalidWarpMsg.Error(), }, @@ -680,7 +836,8 @@ func TestGetVerifiedWarpBlockHash(t *testing.T) { SetupBlockContext: func(mbc *contract.MockBlockContext) { mbc.EXPECT().GetPredicateResults(common.Hash{}, ContractAddress).Return(noFailures) }, - SuppliedGas: GetVerifiedWarpMessageBaseCost + gasConfig.PerWarpMessageChunk*uint64(len(invalidHashPredicate)), + Rules: extras.AvalancheRules{IsGranite: false}, + SuppliedGas: GetVerifiedWarpMessageBaseCost + preGraniteGasConfig.PerWarpMessageChunk*uint64(len(invalidHashPredicate)), ReadOnly: false, ExpectedErr: errInvalidBlockHashPayload.Error(), }, @@ -689,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(), @@ -700,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(), @@ -711,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 7da26b2cd9..45c43bb708 100644 --- a/precompile/contracts/warp/contract_warp_handler.go +++ b/precompile/contracts/warp/contract_warp_handler.go @@ -66,7 +66,7 @@ func handleWarpMessage(accessibleState contract.AccessibleState, input []byte, s return handler.packFailed(), remainingGas, nil } - warpGasConfig := GetWarpPredicateGasConfig(contract.IsGraniteActivated(accessibleState)) + 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. diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index 8830080a26..e6683b9542 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -24,6 +24,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" @@ -52,6 +53,10 @@ var ( numTestVdrs = 10_000 testVdrs []*testValidator vdrs map[ids.NodeID]*validators.GetValidatorOutput + + preGranite precompileconfig.Rules + granite precompileconfig.Rules + graniteGasConfig GasConfig ) func init() { @@ -94,6 +99,10 @@ func init() { panic(err) } + preGranite = extras.AvalancheRules{IsGranite: false} + granite = extras.AvalancheRules{IsGranite: true} + graniteGasConfig = CurrentGasConfig(granite) + for _, testVdr := range testVdrs { blsSignature, err := testVdr.sk.Sign(unsignedMsg.Bytes()) if err != nil { @@ -202,7 +211,6 @@ func createSnowCtx(tb testing.TB, validatorRanges []validatorRange) *snow.Contex } func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate predicate.Predicate) precompiletest.PredicateTest { - gasConfig := GetWarpPredicateGasConfig(false) return precompiletest.PredicateTest{ Config: NewDefaultConfig(utils.NewUint64(0)), PredicateContext: &precompileconfig.PredicateContext{ @@ -212,7 +220,8 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate p }, }, Predicate: predicate, - Gas: gasConfig.PerSignatureVerification + uint64(len(predicate))*gasConfig.PerWarpMessageChunk + numKeys*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(predicate))*graniteGasConfig.PerWarpMessageChunk + numKeys*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -226,7 +235,6 @@ func TestWarpMessageFromPrimaryNetwork(t *testing.T) { func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigners bool) { require := require.New(t) - gasConfig := GetWarpPredicateGasConfig(false) numKeys := 10 cChainID := ids.GenerateTestID() addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) @@ -290,7 +298,8 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner }, }, Predicate: pred, - Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, } @@ -299,7 +308,6 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner } func TestInvalidPredicatePacking(t *testing.T) { - gasConfig := GetWarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -321,7 +329,8 @@ func TestInvalidPredicatePacking(t *testing.T) { }, }, Predicate: pred, - Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidPredicateBytes, } @@ -329,7 +338,6 @@ func TestInvalidPredicatePacking(t *testing.T) { } func TestInvalidWarpMessage(t *testing.T) { - gasConfig := GetWarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -353,7 +361,8 @@ func TestInvalidWarpMessage(t *testing.T) { }, }, Predicate: pred, - Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidWarpMsg, } @@ -361,7 +370,6 @@ func TestInvalidWarpMessage(t *testing.T) { } func TestInvalidAddressedPayload(t *testing.T) { - gasConfig := GetWarpPredicateGasConfig(false) numKeys := 1 snowCtx := createSnowCtx(t, []validatorRange{ { @@ -398,7 +406,8 @@ func TestInvalidAddressedPayload(t *testing.T) { }, }, Predicate: pred, - Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidWarpMsgPayload, } @@ -406,7 +415,6 @@ func TestInvalidAddressedPayload(t *testing.T) { } func TestInvalidBitSet(t *testing.T) { - gasConfig := GetWarpPredicateGasConfig(false) addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100)) require.NoError(t, err) unsignedMsg, err := avalancheWarp.NewUnsignedMessage( @@ -444,7 +452,8 @@ func TestInvalidBitSet(t *testing.T) { }, }, Predicate: pred, - Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numKeys)*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errCannotGetNumSigners, } @@ -452,7 +461,6 @@ func TestInvalidBitSet(t *testing.T) { } func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { - gasConfig := GetWarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -490,7 +498,8 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } @@ -500,7 +509,6 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { // multiple messages all correct, multiple messages all incorrect, mixed bag func TestWarpMultiplePredicates(t *testing.T) { - gasConfig := GetWarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -532,10 +540,10 @@ func TestWarpMultiplePredicates(t *testing.T) { ) if valid { pred = validPredicate - expectedGas = gasConfig.PerSignatureVerification + uint64(len(validPredicate))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner + expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner expectedErr = nil } else { - expectedGas = gasConfig.PerSignatureVerification + uint64(len(invalidPredicate))*gasConfig.PerWarpMessageChunk + uint64(1)*gasConfig.PerWarpSigner + expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(invalidPredicate))*graniteGasConfig.PerWarpMessageChunk + uint64(1)*graniteGasConfig.PerWarpSigner pred = invalidPredicate expectedErr = errFailedVerification } @@ -549,6 +557,7 @@ func TestWarpMultiplePredicates(t *testing.T) { }, }, Predicate: pred, + Rules: granite, Gas: expectedGas, GasErr: nil, ExpectedErr: expectedErr, @@ -559,7 +568,6 @@ func TestWarpMultiplePredicates(t *testing.T) { } func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { - gasConfig := GetWarpPredicateGasConfig(false) snowCtx := createSnowCtx(t, []validatorRange{ { start: 0, @@ -594,7 +602,8 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Gas: gasConfig.PerSignatureVerification + uint64(len(pred))*gasConfig.PerWarpMessageChunk + uint64(numSigners)*gasConfig.PerWarpSigner, + Rules: granite, + Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, } diff --git a/precompile/contracts/warp/test_predicate.go b/precompile/contracts/warp/test_predicate.go new file mode 100644 index 0000000000..38097a50a1 --- /dev/null +++ b/precompile/contracts/warp/test_predicate.go @@ -0,0 +1,84 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "testing" + "time" + + "github.com/ava-labs/avalanchego/vms/evm/predicate" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/precompile/precompileconfig" +) + +// PredicateTest defines a unit test/benchmark for verifying a precompile predicate. +type PredicateTest struct { + Config precompileconfig.Config + + PredicateContext *precompileconfig.PredicateContext + + Predicate predicate.Predicate + Gas uint64 + Rules precompileconfig.Rules + GasErr error + ExpectedErr error +} + +func (test PredicateTest) run(t testing.TB) { + t.Helper() + require := require.New(t) + predicater := test.Config.(precompileconfig.Predicater) + + predicateGas, predicateGasErr := predicater.PredicateGas(test.Predicate, test.Rules) + require.ErrorIs(predicateGasErr, test.GasErr) + if test.GasErr != nil { + return + } + + require.Equal(test.Gas, predicateGas) + + predicateRes := predicater.VerifyPredicate(test.PredicateContext, test.Predicate) + require.ErrorIs(predicateRes, test.ExpectedErr) +} + +func RunPredicateTests(t *testing.T, predicateTests map[string]PredicateTest) { + t.Helper() + + for name, test := range predicateTests { + t.Run(name, func(t *testing.T) { + test.run(t) + }) + } +} + +func (test PredicateTest) RunBenchmark(b *testing.B) { + b.ReportAllocs() + start := time.Now() + b.ResetTimer() + for i := 0; i < b.N; i++ { + test.run(b) + } + b.StopTimer() + elapsed := uint64(time.Since(start)) + if elapsed < 1 { + elapsed = 1 + } + + gasUsed := test.Gas * uint64(b.N) + b.ReportMetric(float64(test.Gas), "gas/op") + // Keep it as uint64, multiply 100 to get two digit float later + mgasps := (100 * 1000 * gasUsed) / elapsed + b.ReportMetric(float64(mgasps)/100, "mgas/s") +} + +func RunPredicateBenchmarks(b *testing.B, predicateTests map[string]PredicateTest) { + b.Helper() + + for name, test := range predicateTests { + b.Run(name, func(b *testing.B) { + test.RunBenchmark(b) + }) + } +} diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index c636f2598e..b2600cdee6 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 { @@ -96,13 +100,17 @@ 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 if chainConfig == nil { mockChainConfig := precompileconfig.NewMockChainConfig(ctrl) mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(true) - mockChainConfig.EXPECT().IsGranite(gomock.Any()).AnyTimes().Return(isGraniteActivated) + mockChainConfig.EXPECT().IsGranite(gomock.Any()).AnyTimes().Return(test.Rules.IsGraniteActivated()) chainConfig = mockChainConfig } @@ -120,6 +128,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 4fa92b48d5..6cbe903dcd 100644 --- a/precompile/precompiletest/test_predicate.go +++ b/precompile/precompiletest/test_predicate.go @@ -10,7 +10,6 @@ import ( "github.com/ava-labs/avalanchego/vms/evm/predicate" "github.com/stretchr/testify/require" - "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/precompile/precompileconfig" ) @@ -21,6 +20,7 @@ type PredicateTest struct { PredicateContext *precompileconfig.PredicateContext Predicate predicate.Predicate + Rules precompileconfig.Rules Gas uint64 GasErr error ExpectedErr error @@ -31,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, &extras.AvalancheRules{}) + predicateGas, predicateGasErr := predicater.PredicateGas(test.Predicate, test.Rules) require.ErrorIs(predicateGasErr, test.GasErr) if test.GasErr != nil { return From 61cd8231614365b50f5d7317e86822ca647d048f Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 2 Oct 2025 16:45:22 -0400 Subject: [PATCH 08/15] Cleanup --- precompile/contracts/warp/config.go | 2 - precompile/contracts/warp/predicate_test.go | 26 +++---- precompile/contracts/warp/test_predicate.go | 84 --------------------- 3 files changed, 12 insertions(+), 100 deletions(-) delete mode 100644 precompile/contracts/warp/test_predicate.go diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 8563243305..9997f1c5e2 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -147,8 +147,6 @@ func (*Config) Accept(acceptCtx *precompileconfig.AcceptContext, blockHash commo func (*Config) PredicateGas(pred predicate.Predicate, rules precompileconfig.Rules) (uint64, error) { gasConfig := CurrentGasConfig(rules) - fmt.Printf("warp predicate gas config: %+v\n", gasConfig) - totalGas := gasConfig.PerSignatureVerification bytesGasCost, overflow := math.SafeMul(gasConfig.PerWarpMessageChunk, uint64(len(pred))) if overflow { diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index e6683b9542..d5ba97d232 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -54,8 +54,7 @@ var ( testVdrs []*testValidator vdrs map[ids.NodeID]*validators.GetValidatorOutput - preGranite precompileconfig.Rules - granite precompileconfig.Rules + graniteRules precompileconfig.Rules graniteGasConfig GasConfig ) @@ -99,9 +98,8 @@ func init() { panic(err) } - preGranite = extras.AvalancheRules{IsGranite: false} - granite = extras.AvalancheRules{IsGranite: true} - graniteGasConfig = CurrentGasConfig(granite) + graniteRules = extras.AvalancheRules{IsGranite: true} + graniteGasConfig = CurrentGasConfig(graniteRules) for _, testVdr := range testVdrs { blsSignature, err := testVdr.sk.Sign(unsignedMsg.Bytes()) @@ -220,7 +218,7 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate p }, }, Predicate: predicate, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(predicate))*graniteGasConfig.PerWarpMessageChunk + numKeys*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, @@ -298,7 +296,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: nil, @@ -329,7 +327,7 @@ func TestInvalidPredicatePacking(t *testing.T) { }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidPredicateBytes, } @@ -361,7 +359,7 @@ func TestInvalidWarpMessage(t *testing.T) { }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidWarpMsg, } @@ -406,7 +404,7 @@ func TestInvalidAddressedPayload(t *testing.T) { }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errInvalidWarpMsgPayload, } @@ -452,7 +450,7 @@ func TestInvalidBitSet(t *testing.T) { }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, GasErr: errCannotGetNumSigners, } @@ -498,7 +496,7 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, @@ -557,7 +555,7 @@ func TestWarpMultiplePredicates(t *testing.T) { }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: expectedGas, GasErr: nil, ExpectedErr: expectedErr, @@ -602,7 +600,7 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { }, }, Predicate: pred, - Rules: granite, + Rules: graniteRules, Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, GasErr: nil, ExpectedErr: expectedErr, diff --git a/precompile/contracts/warp/test_predicate.go b/precompile/contracts/warp/test_predicate.go deleted file mode 100644 index 38097a50a1..0000000000 --- a/precompile/contracts/warp/test_predicate.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package warp - -import ( - "testing" - "time" - - "github.com/ava-labs/avalanchego/vms/evm/predicate" - "github.com/stretchr/testify/require" - - "github.com/ava-labs/coreth/precompile/precompileconfig" -) - -// PredicateTest defines a unit test/benchmark for verifying a precompile predicate. -type PredicateTest struct { - Config precompileconfig.Config - - PredicateContext *precompileconfig.PredicateContext - - Predicate predicate.Predicate - Gas uint64 - Rules precompileconfig.Rules - GasErr error - ExpectedErr error -} - -func (test PredicateTest) run(t testing.TB) { - t.Helper() - require := require.New(t) - predicater := test.Config.(precompileconfig.Predicater) - - predicateGas, predicateGasErr := predicater.PredicateGas(test.Predicate, test.Rules) - require.ErrorIs(predicateGasErr, test.GasErr) - if test.GasErr != nil { - return - } - - require.Equal(test.Gas, predicateGas) - - predicateRes := predicater.VerifyPredicate(test.PredicateContext, test.Predicate) - require.ErrorIs(predicateRes, test.ExpectedErr) -} - -func RunPredicateTests(t *testing.T, predicateTests map[string]PredicateTest) { - t.Helper() - - for name, test := range predicateTests { - t.Run(name, func(t *testing.T) { - test.run(t) - }) - } -} - -func (test PredicateTest) RunBenchmark(b *testing.B) { - b.ReportAllocs() - start := time.Now() - b.ResetTimer() - for i := 0; i < b.N; i++ { - test.run(b) - } - b.StopTimer() - elapsed := uint64(time.Since(start)) - if elapsed < 1 { - elapsed = 1 - } - - gasUsed := test.Gas * uint64(b.N) - b.ReportMetric(float64(test.Gas), "gas/op") - // Keep it as uint64, multiply 100 to get two digit float later - mgasps := (100 * 1000 * gasUsed) / elapsed - b.ReportMetric(float64(mgasps)/100, "mgas/s") -} - -func RunPredicateBenchmarks(b *testing.B, predicateTests map[string]PredicateTest) { - b.Helper() - - for name, test := range predicateTests { - b.Run(name, func(b *testing.B) { - test.RunBenchmark(b) - }) - } -} From 30cc067f39eaf9153f20b31c8f4ca80aa885e5b6 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 2 Oct 2025 16:51:23 -0400 Subject: [PATCH 09/15] Fixup --- precompile/contracts/warp/predicate_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index e195c05759..a67046814f 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -636,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, @@ -644,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, @@ -663,6 +664,7 @@ func TestWarpNoValidatorsAndOverflowUseSameGas(t *testing.T) { ProposerVMBlockCtx: proposervmContext, }, Predicate: pred, + Rules: graniteRules, Gas: expectedGas, GasErr: nil, ExpectedErr: safemath.ErrOverflow, From aee99ec554188308f8f8f2ec0e2f3ca02175e372 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 2 Oct 2025 16:57:44 -0400 Subject: [PATCH 10/15] Cleanup --- precompile/contract/utils.go | 4 ---- precompile/precompileconfig/config.go | 2 -- precompile/precompileconfig/mocks.go | 14 -------------- precompile/precompiletest/test_precompile.go | 10 ++-------- 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/precompile/contract/utils.go b/precompile/contract/utils.go index 4711abe0f1..7ec7424d2d 100644 --- a/precompile/contract/utils.go +++ b/precompile/contract/utils.go @@ -59,7 +59,3 @@ func ParseABI(rawABI string) abi.ABI { return parsed } - -func IsGraniteActivated(evm AccessibleState) bool { - return evm.GetChainConfig().IsGranite(evm.GetBlockContext().Timestamp()) -} diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 42eb837005..406d20d3bd 100644 --- a/precompile/precompileconfig/config.go +++ b/precompile/precompileconfig/config.go @@ -77,8 +77,6 @@ type Accepter interface { type ChainConfig interface { // IsDurango returns true if the time is after Durango. IsDurango(time uint64) bool - // IsGranite returns true if the time is after Granite. - IsGranite(time uint64) bool } // Rules defines the interface that provides information about the current rules of the chain. diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index 73ca03434c..bfef6f3f95 100644 --- a/precompile/precompileconfig/mocks.go +++ b/precompile/precompileconfig/mocks.go @@ -202,20 +202,6 @@ func (mr *MockChainConfigMockRecorder) IsDurango(time any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDurango", reflect.TypeOf((*MockChainConfig)(nil).IsDurango), time) } -// IsGranite mocks base method. -func (m *MockChainConfig) IsGranite(time uint64) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsGranite", time) - ret0, _ := ret[0].(bool) - return ret0 -} - -// IsGranite indicates an expected call of IsGranite. -func (mr *MockChainConfigMockRecorder) IsGranite(time any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsGranite", reflect.TypeOf((*MockChainConfig)(nil).IsGranite), time) -} - // MockAccepter is a mock of Accepter interface. type MockAccepter struct { ctrl *gomock.Controller diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index b2600cdee6..b1ac2a09ee 100644 --- a/precompile/precompiletest/test_precompile.go +++ b/precompile/precompiletest/test_precompile.go @@ -70,15 +70,10 @@ type PrecompileRunparams struct { } func (test PrecompileTest) Run(t *testing.T, module modules.Module) { - test.run(t, module, false) - test.run(t, module, true) -} - -func (test PrecompileTest) run(t *testing.T, module modules.Module, isGraniteActivated bool) { state := newTestStateDB(t, map[common.Address][]predicate.Predicate{ module.Address: test.Predicates, }) - runParams := test.setup(t, module, state, isGraniteActivated) + runParams := test.setup(t, module, state) if runParams.Input != nil { ret, remainingGas, err := module.Contract.Run(runParams.AccessibleState, runParams.Caller, runParams.ContractAddress, runParams.Input, runParams.SuppliedGas, runParams.ReadOnly) @@ -96,7 +91,7 @@ func (test PrecompileTest) run(t *testing.T, module modules.Module, isGraniteAct } } -func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *testStateDB, isGraniteActivated bool) PrecompileRunparams { +func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *testStateDB) PrecompileRunparams { t.Helper() contractAddress := module.Address @@ -110,7 +105,6 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes if chainConfig == nil { mockChainConfig := precompileconfig.NewMockChainConfig(ctrl) mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(true) - mockChainConfig.EXPECT().IsGranite(gomock.Any()).AnyTimes().Return(test.Rules.IsGraniteActivated()) chainConfig = mockChainConfig } From a10c9385e5eefd4ad67d6c2f7ec1aaa7ab2c8ac5 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 3 Oct 2025 16:18:35 -0400 Subject: [PATCH 11/15] Update precompile/contracts/warp/contract.go Co-authored-by: Michael Kaplan <55204436+michaelkaplan13@users.noreply.github.com> Signed-off-by: Geoff Stuart --- precompile/contracts/warp/contract.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index 64d916794e..851d000759 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -39,7 +39,7 @@ type GasConfig struct { PerWarpSigner uint64 // Gas cost per chunk of the warp message (each chunk is 128 bytes) PerWarpMessageChunk uint64 - // Gas cost to verify a warp message signature + // Gas cost to verify a BLS signature PerSignatureVerification uint64 } From 4de622181f00e4a53fbc4dedbc7f59c8c927d702 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 6 Oct 2025 10:28:50 -0400 Subject: [PATCH 12/15] nits --- params/extras/network_upgrades.go | 1 + params/hooks_libevm.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/params/extras/network_upgrades.go b/params/extras/network_upgrades.go index d4f834609a..57c1b33729 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -264,6 +264,7 @@ 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 } diff --git a/params/hooks_libevm.go b/params/hooks_libevm.go index 3e8c4cfd18..ec832eece3 100644 --- a/params/hooks_libevm.go +++ b/params/hooks_libevm.go @@ -196,8 +196,8 @@ func (a accessibleState) GetChainConfig() precompileconfig.ChainConfig { } func (a accessibleState) GetRules() precompileconfig.Rules { - extra := GetExtra(a.GetPrecompileEnv().ChainConfig()) - return extra.GetAvalancheRules(a.GetBlockContext().Timestamp()) + chainConfigExtra := GetExtra(a.GetPrecompileEnv().ChainConfig()) + return chainConfigExtra.GetAvalancheRules(a.GetBlockContext().Timestamp()) } func (a accessibleState) GetSnowContext() *snow.Context { From ea41b66bc6dd57f56b4b3ebaff552b5e2c15ce4e Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 7 Oct 2025 15:03:10 -0400 Subject: [PATCH 13/15] Make gas costs const --- precompile/contracts/warp/contract.go | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index 851d000759..52b8638830 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -32,6 +32,16 @@ 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 + + // Warp gas costs before Granite activation + preGraniteGasCostPerWarpSigner uint64 = 500 + preGraniteGasCostPerWarpMessageChunk uint64 = 3_200 + preGraniteGasCostPerSignatureVerification uint64 = 200_000 + + // Granite gas costs + graniteGasCostPerWarpSigner uint64 = 250 + graniteGasCostPerWarpMessageChunk uint64 = 3_200 + graniteGasCostPerSignatureVerification uint64 = 100_000 ) type GasConfig struct { @@ -351,16 +361,18 @@ func createWarpPrecompile() contract.StatefulPrecompiledContract { } func CurrentGasConfig(rules precompileconfig.Rules) GasConfig { - if rules.IsGraniteActivated() { + switch { + case rules.IsGraniteActivated(): return GasConfig{ - PerWarpSigner: 250, - PerWarpMessageChunk: 3_200, - PerSignatureVerification: 100_000, + PerWarpSigner: graniteGasCostPerWarpSigner, + PerWarpMessageChunk: graniteGasCostPerWarpMessageChunk, + PerSignatureVerification: graniteGasCostPerSignatureVerification, + } + default: + return GasConfig{ + PerWarpSigner: preGraniteGasCostPerWarpSigner, + PerWarpMessageChunk: preGraniteGasCostPerWarpMessageChunk, + PerSignatureVerification: preGraniteGasCostPerSignatureVerification, } - } - return GasConfig{ - PerWarpSigner: 500, - PerWarpMessageChunk: 3_200, - PerSignatureVerification: 200_000, } } From 17833b7fdf487e3c5a65719f4b28ca112054fd14 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 8 Oct 2025 14:59:18 -0400 Subject: [PATCH 14/15] Review feedback --- precompile/contracts/warp/contract.go | 4 ++ precompile/contracts/warp/predicate_test.go | 42 ++++++++++---------- precompile/precompiletest/test_precompile.go | 5 --- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index 52b8638830..edf081d67e 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -376,3 +376,7 @@ func CurrentGasConfig(rules precompileconfig.Rules) GasConfig { } } } + +func (g GasConfig) PredicateGasCost(chunks int, signers int) uint64 { + return g.PerSignatureVerification + uint64(chunks)*g.PerWarpMessageChunk + uint64(signers)*g.PerWarpSigner +} diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index 4565da6619..9d35782041 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" + "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" @@ -24,7 +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/params/extras/extrastest" "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/precompile/precompiletest" "github.com/ava-labs/coreth/utils" @@ -52,8 +53,8 @@ var ( testVdrs []*testValidator vdrs map[ids.NodeID]*validators.GetValidatorOutput - graniteRules precompileconfig.Rules - graniteGasConfig GasConfig + graniteRules = extrastest.ForkToRules(upgradetest.Granite) + graniteGasConfig = CurrentGasConfig(graniteRules) ) func init() { @@ -96,9 +97,6 @@ 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 { @@ -221,7 +219,7 @@ func createSnowCtx(tb testing.TB, validatorRanges []validatorRange) *snow.Contex return snowCtx } -func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate predicate.Predicate) precompiletest.PredicateTest { +func createValidPredicateTest(snowCtx *snow.Context, numKeys int, predicate predicate.Predicate) precompiletest.PredicateTest { return precompiletest.PredicateTest{ Config: NewDefaultConfig(utils.NewUint64(0)), PredicateContext: &precompileconfig.PredicateContext{ @@ -232,7 +230,7 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicate p }, Predicate: predicate, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(predicate))*graniteGasConfig.PerWarpMessageChunk + numKeys*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(predicate), numKeys), GasErr: nil, ExpectedErr: nil, } @@ -319,7 +317,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner }, Predicate: pred, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(pred), numKeys), GasErr: nil, ExpectedErr: nil, } @@ -350,7 +348,7 @@ func TestInvalidPredicatePacking(t *testing.T) { }, Predicate: pred, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(pred), numKeys), GasErr: errInvalidPredicateBytes, } @@ -382,7 +380,7 @@ func TestInvalidWarpMessage(t *testing.T) { }, Predicate: pred, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(pred), numKeys), GasErr: errInvalidWarpMsg, } @@ -427,7 +425,7 @@ func TestInvalidAddressedPayload(t *testing.T) { }, Predicate: pred, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(pred), numKeys), GasErr: errInvalidWarpMsgPayload, } @@ -473,7 +471,7 @@ func TestInvalidBitSet(t *testing.T) { }, Predicate: pred, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numKeys)*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(pred), numKeys), GasErr: errCannotGetNumSigners, } @@ -519,7 +517,7 @@ func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) { }, Predicate: pred, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(pred), numSigners), GasErr: nil, ExpectedErr: expectedErr, } @@ -560,10 +558,10 @@ func TestWarpMultiplePredicates(t *testing.T) { ) if valid { pred = validPredicate - expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner + expectedGas = graniteGasConfig.PredicateGasCost(len(pred), numSigners) expectedErr = nil } else { - expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(invalidPredicate))*graniteGasConfig.PerWarpMessageChunk + uint64(1)*graniteGasConfig.PerWarpSigner + expectedGas = graniteGasConfig.PredicateGasCost(len(pred), 1) pred = invalidPredicate expectedErr = errFailedVerification } @@ -623,7 +621,7 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { }, Predicate: pred, Rules: graniteRules, - Gas: graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + uint64(numSigners)*graniteGasConfig.PerWarpSigner, + Gas: graniteGasConfig.PredicateGasCost(len(pred), numSigners), GasErr: nil, ExpectedErr: expectedErr, } @@ -639,7 +637,7 @@ func TestWarpNoValidatorsAndOverflowUseSameGas(t *testing.T) { PChainHeight: 1, } pred = createPredicate(0) - expectedGas = graniteGasConfig.PerSignatureVerification + uint64(len(pred))*graniteGasConfig.PerWarpMessageChunk + expectedGas = graniteGasConfig.PredicateGasCost(len(pred), 0) ) noValidators := precompiletest.PredicateTest{ Config: config, @@ -692,7 +690,7 @@ func makeWarpPredicateTests(tb testing.TB) map[string]precompiletest.PredicateTe publicKey: true, }, }) - predicateTests[testName] = createValidPredicateTest(snowCtx, uint64(totalNodes), pred) + predicateTests[testName] = createValidPredicateTest(snowCtx, totalNodes, pred) } numSigners := 10 @@ -714,7 +712,7 @@ func makeWarpPredicateTests(tb testing.TB) map[string]precompiletest.PredicateTe publicKey: true, }, }) - predicateTests[testName] = createValidPredicateTest(snowCtx, uint64(numSigners), pred) + predicateTests[testName] = createValidPredicateTest(snowCtx, numSigners, pred) } for _, totalNodes := range []int{100, 1_000, 10_000} { @@ -735,7 +733,7 @@ func makeWarpPredicateTests(tb testing.TB) map[string]precompiletest.PredicateTe publicKey: false, }, }) - predicateTests[testName] = createValidPredicateTest(snowCtx, uint64(numSigners), pred) + predicateTests[testName] = createValidPredicateTest(snowCtx, numSigners, pred) } for _, totalNodes := range []int{100, 1_000, 10_000} { @@ -763,7 +761,7 @@ func makeWarpPredicateTests(tb testing.TB) map[string]precompiletest.PredicateTe }, } - predicateTests[testName] = createValidPredicateTest(snowCtx, uint64(numSigners), pred) + predicateTests[testName] = createValidPredicateTest(snowCtx, numSigners, pred) } return predicateTests } diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index b1ac2a09ee..d77c6636af 100644 --- a/precompile/precompiletest/test_precompile.go +++ b/precompile/precompiletest/test_precompile.go @@ -17,7 +17,6 @@ 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" @@ -95,10 +94,6 @@ 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 From 0376c5e30507a74e82fe4be702d0702021fad7e2 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 8 Oct 2025 16:59:34 -0400 Subject: [PATCH 15/15] fix --- params/extras/extrastest/test_rules.go | 6 ++++++ precompile/contracts/warp/predicate_test.go | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/params/extras/extrastest/test_rules.go b/params/extras/extrastest/test_rules.go index d11352d2d3..2d157ea1d7 100644 --- a/params/extras/extrastest/test_rules.go +++ b/params/extras/extrastest/test_rules.go @@ -5,6 +5,7 @@ package extrastest import ( "fmt" + "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/libevm/common" @@ -20,3 +21,8 @@ func ForkToRules(fork upgradetest.Fork) *extras.Rules { } return params.GetRulesExtra(chainConfig.Rules(common.Big0, params.IsMergeTODO, 0)) } + +func ForkToAvalancheRules(fork upgradetest.Fork) extras.AvalancheRules { + networkUpgrades := extras.GetNetworkUpgrades(upgradetest.GetConfig(fork)) + return networkUpgrades.GetAvalancheRules(uint64(upgrade.InitiallyActiveTime.Unix())) +} diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index 9d35782041..dfb17757d6 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -53,7 +53,7 @@ var ( testVdrs []*testValidator vdrs map[ids.NodeID]*validators.GetValidatorOutput - graniteRules = extrastest.ForkToRules(upgradetest.Granite) + graniteRules = extrastest.ForkToAvalancheRules(upgradetest.Granite) graniteGasConfig = CurrentGasConfig(graniteRules) ) @@ -561,8 +561,8 @@ func TestWarpMultiplePredicates(t *testing.T) { expectedGas = graniteGasConfig.PredicateGasCost(len(pred), numSigners) expectedErr = nil } else { - expectedGas = graniteGasConfig.PredicateGasCost(len(pred), 1) pred = invalidPredicate + expectedGas = graniteGasConfig.PredicateGasCost(len(invalidPredicate), 1) expectedErr = errFailedVerification }