From 73735ff936c39d3965a23ad3178f94b312103a0f Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Thu, 3 Oct 2024 15:26:31 +1000 Subject: [PATCH] more refundable tx --- app/keepers/keepers.go | 1 + test/e2e/btc_staking_e2e_test.go | 38 +++++++++---------- test/e2e/btc_staking_pre_approval_e2e_test.go | 37 +++++++++++------- test/e2e/configurer/chain/commands.go | 23 ++++++++++- testutil/keeper/btcstaking.go | 2 + x/btccheckpoint/keeper/msg_server.go | 4 ++ x/btccheckpoint/types/expected_keepers.go | 3 ++ x/btccheckpoint/types/mock_keepers.go | 4 ++ x/btcstaking/genesis_test.go | 2 +- x/btcstaking/keeper/btc_height_index_test.go | 2 +- x/btcstaking/keeper/grpc_query_test.go | 16 ++++---- x/btcstaking/keeper/keeper.go | 3 ++ x/btcstaking/keeper/keeper_test.go | 9 ++++- x/btcstaking/keeper/msg_server.go | 18 +++++++++ x/btcstaking/keeper/params_test.go | 6 +-- x/btcstaking/keeper/query_params_test.go | 4 +- x/btcstaking/types/expected_keepers.go | 5 +++ x/btcstaking/types/mocked_keepers.go | 36 ++++++++++++++++++ x/finality/keeper/msg_server.go | 2 +- x/finality/types/expected_keepers.go | 3 +- x/incentive/keeper/keeper.go | 6 +-- x/incentive/types/expected_keepers.go | 1 + 22 files changed, 168 insertions(+), 57 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 0fb61c6b..974d7f6b 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -498,6 +498,7 @@ func (ak *AppKeepers) InitKeepers( // setting the finality keeper as nil for now // need to set it after finality keeper is initiated nil, + &ak.IncentiveKeeper, btcNetParams, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) diff --git a/test/e2e/btc_staking_e2e_test.go b/test/e2e/btc_staking_e2e_test.go index 78e8d52b..5980a73d 100644 --- a/test/e2e/btc_staking_e2e_test.go +++ b/test/e2e/btc_staking_e2e_test.go @@ -228,13 +228,16 @@ func (s *BTCStakingTestSuite) Test2SubmitCovenantSignature() { s.NoError(err) for i := 0; i < int(s.covenantQuorum); i++ { - nonValidatorNode.AddCovenantSigs( - covenantSlashingSigs[i].CovPk, - stakingTxHash, - covenantSlashingSigs[i].AdaptorSigs, - bbn.NewBIP340SignatureFromBTCSig(covUnbondingSigs[i]), - covenantUnbondingSlashingSigs[i].AdaptorSigs, - ) + nonValidatorNode.SubmitRefundableTxWithAssertion(func() { + // add covenant sigs + nonValidatorNode.AddCovenantSigs( + covenantSlashingSigs[i].CovPk, + stakingTxHash, + covenantSlashingSigs[i].AdaptorSigs, + bbn.NewBIP340SignatureFromBTCSig(covUnbondingSigs[i]), + covenantUnbondingSlashingSigs[i].AdaptorSigs, + ) + }) // wait for a block so that above txs take effect nonValidatorNode.WaitForNextBlock() } @@ -346,12 +349,10 @@ func (s *BTCStakingTestSuite) Test3CommitPublicRandomnessAndSubmitFinalitySignat s.NoError(err) eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig) - // balance before the finality signature is submitted - submitterBalanceBefore, err := nonValidatorNode.QueryBalances(s.cacheFP.Addr) - s.NoError(err) - - // submit finality signature - nonValidatorNode.AddFinalitySig(s.cacheFP.BtcPk, activatedHeight, &randListInfo.PRList[idx], *randListInfo.ProofList[idx].ToProto(), appHash, eotsSig) + nonValidatorNode.SubmitRefundableTxWithAssertion(func() { + // submit finality signature + nonValidatorNode.AddFinalitySig(s.cacheFP.BtcPk, activatedHeight, &randListInfo.PRList[idx], *randListInfo.ProofList[idx].ToProto(), appHash, eotsSig) + }) // ensure vote is eventually cast var finalizedBlocks []*ftypes.IndexedBlock @@ -363,11 +364,6 @@ func (s *BTCStakingTestSuite) Test3CommitPublicRandomnessAndSubmitFinalitySignat s.Equal(appHash.Bytes(), finalizedBlocks[0].AppHash) s.T().Logf("the block %d is finalized", activatedHeight) - // ensure the tx fee is refunded and the balance is not changed - submitterBalanceAfter, err := nonValidatorNode.QueryBalances(s.cacheFP.Addr) - s.NoError(err) - s.Equal(submitterBalanceBefore, submitterBalanceAfter) - // ensure finality provider has received rewards after the block is finalised fpRewardGauges, err := nonValidatorNode.QueryRewardGauge(fpBabylonAddr) s.NoError(err) @@ -473,8 +469,10 @@ func (s *BTCStakingTestSuite) Test5SubmitStakerUnbonding() { delUnbondingSig, err := activeDel.SignUnbondingTx(params, s.net, s.delBTCSK) s.NoError(err) - // submit the message for creating BTC undelegation - nonValidatorNode.BTCUndelegate(&stakingTxHash, delUnbondingSig) + nonValidatorNode.SubmitRefundableTxWithAssertion(func() { + // submit the message for creating BTC undelegation + nonValidatorNode.BTCUndelegate(&stakingTxHash, delUnbondingSig) + }) // wait for a block so that above txs take effect nonValidatorNode.WaitForNextBlock() diff --git a/test/e2e/btc_staking_pre_approval_e2e_test.go b/test/e2e/btc_staking_pre_approval_e2e_test.go index 2a4059a0..0e358519 100644 --- a/test/e2e/btc_staking_pre_approval_e2e_test.go +++ b/test/e2e/btc_staking_pre_approval_e2e_test.go @@ -220,13 +220,16 @@ func (s *BTCStakingPreApprovalTestSuite) Test2SubmitCovenantSignature() { s.NoError(err) for i := 0; i < int(s.covenantQuorum); i++ { - nonValidatorNode.AddCovenantSigs( - covenantSlashingSigs[i].CovPk, - stakingTxHash, - covenantSlashingSigs[i].AdaptorSigs, - bbn.NewBIP340SignatureFromBTCSig(covUnbondingSigs[i]), - covenantUnbondingSlashingSigs[i].AdaptorSigs, - ) + nonValidatorNode.SubmitRefundableTxWithAssertion(func() { + nonValidatorNode.AddCovenantSigs( + covenantSlashingSigs[i].CovPk, + stakingTxHash, + covenantSlashingSigs[i].AdaptorSigs, + bbn.NewBIP340SignatureFromBTCSig(covUnbondingSigs[i]), + covenantUnbondingSlashingSigs[i].AdaptorSigs, + ) + }) + // wait for a block so that above txs take effect nonValidatorNode.WaitForNextBlock() } @@ -266,10 +269,12 @@ func (s *BTCStakingPreApprovalTestSuite) Test3SendStakingTransctionInclusionProo s.NoError(err) stakingTxHash := stakingMsgTx.TxHash() - nonValidatorNode.AddBTCDelegationInclusionProof( - &stakingTxHash, - s.cachedInclusionProof, - ) + nonValidatorNode.SubmitRefundableTxWithAssertion(func() { + nonValidatorNode.AddBTCDelegationInclusionProof( + &stakingTxHash, + s.cachedInclusionProof, + ) + }) nonValidatorNode.WaitForNextBlock() nonValidatorNode.WaitForNextBlock() @@ -366,7 +371,9 @@ func (s *BTCStakingPreApprovalTestSuite) Test4CommitPublicRandomnessAndSubmitFin s.NoError(err) eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig) // submit finality signature - nonValidatorNode.AddFinalitySig(s.cacheFP.BtcPk, activatedHeight, &randListInfo.PRList[idx], *randListInfo.ProofList[idx].ToProto(), appHash, eotsSig) + nonValidatorNode.SubmitRefundableTxWithAssertion(func() { + nonValidatorNode.AddFinalitySig(s.cacheFP.BtcPk, activatedHeight, &randListInfo.PRList[idx], *randListInfo.ProofList[idx].ToProto(), appHash, eotsSig) + }) // ensure vote is eventually cast var finalizedBlocks []*ftypes.IndexedBlock @@ -483,8 +490,10 @@ func (s *BTCStakingPreApprovalTestSuite) Test5SubmitStakerUnbonding() { delUnbondingSig, err := activeDel.SignUnbondingTx(params, s.net, s.delBTCSK) s.NoError(err) - // submit the message for creating BTC undelegation - nonValidatorNode.BTCUndelegate(&stakingTxHash, delUnbondingSig) + nonValidatorNode.SubmitRefundableTxWithAssertion(func() { + // submit the message for creating BTC undelegation + nonValidatorNode.BTCUndelegate(&stakingTxHash, delUnbondingSig) + }) // wait for a block so that above txs take effect nonValidatorNode.WaitForNextBlock() diff --git a/test/e2e/configurer/chain/commands.go b/test/e2e/configurer/chain/commands.go index 73c6e188..2721d93d 100644 --- a/test/e2e/configurer/chain/commands.go +++ b/test/e2e/configurer/chain/commands.go @@ -234,7 +234,10 @@ func (n *NodeConfig) FinalizeSealedEpochs(startEpoch uint64, lastEpoch uint64) { n.InsertHeader(&opReturn1.HeaderBytes) n.InsertHeader(&opReturn2.HeaderBytes) - n.InsertProofs(opReturn1.SpvProof, opReturn2.SpvProof) + + n.SubmitRefundableTxWithAssertion(func() { + n.InsertProofs(opReturn1.SpvProof, opReturn2.SpvProof) + }) n.WaitForCondition(func() bool { ckpt, err := n.QueryRawCheckpoint(checkpoint.Ckpt.EpochNum) @@ -444,3 +447,21 @@ func (n *NodeConfig) TxGovVote(from string, propID int, option govv1.VoteOption, n.LogActionF("successfully submitted vote %s to prop %d", option, propID) } + +// submitRefundableTxWithAssertion submits a refundable transaction, +// and asserts that the tx fee is refunded +func (n *NodeConfig) SubmitRefundableTxWithAssertion( + f func(), +) { + // balance before submitting the refundable tx + submitterBalanceBefore, err := n.QueryBalances(n.PublicAddress) + require.NoError(n.t, err) + + // submit refundable tx + f() + + // ensure the tx fee is refunded and the balance is not changed + submitterBalanceAfter, err := n.QueryBalances(n.PublicAddress) + require.NoError(n.t, err) + require.Equal(n.t, submitterBalanceBefore, submitterBalanceAfter) +} diff --git a/testutil/keeper/btcstaking.go b/testutil/keeper/btcstaking.go index a3bcad46..85f9641b 100644 --- a/testutil/keeper/btcstaking.go +++ b/testutil/keeper/btcstaking.go @@ -28,6 +28,7 @@ func BTCStakingKeeper( btclcKeeper types.BTCLightClientKeeper, btccKeeper types.BtcCheckpointKeeper, finalityKeeper types.FinalityKeeper, + iKeeper types.IncentiveKeeper, ) (*keeper.Keeper, sdk.Context) { storeKey := storetypes.NewKVStoreKey(types.StoreKey) @@ -45,6 +46,7 @@ func BTCStakingKeeper( btclcKeeper, btccKeeper, finalityKeeper, + iKeeper, &chaincfg.SimNetParams, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) diff --git a/x/btccheckpoint/keeper/msg_server.go b/x/btccheckpoint/keeper/msg_server.go index d2cc6531..9714b09b 100644 --- a/x/btccheckpoint/keeper/msg_server.go +++ b/x/btccheckpoint/keeper/msg_server.go @@ -92,6 +92,10 @@ func (ms msgServer) InsertBTCSpvProof(ctx context.Context, req *types.MsgInsertB return nil, err } + // At this point, the BTC checkpoint is considered the first valid one for the epoch. + // Thus, we can safely consider this message as refundable + ms.k.incentiveKeeper.IndexRefundableMsg(sdkCtx, req) + return &types.MsgInsertBTCSpvProofResponse{}, nil } diff --git a/x/btccheckpoint/types/expected_keepers.go b/x/btccheckpoint/types/expected_keepers.go index 324fccef..395611ec 100644 --- a/x/btccheckpoint/types/expected_keepers.go +++ b/x/btccheckpoint/types/expected_keepers.go @@ -2,8 +2,10 @@ package types import ( "context" + txformat "github.com/babylonlabs-io/babylon/btctxformatter" bbn "github.com/babylonlabs-io/babylon/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) type BTCLightClientKeeper interface { @@ -40,4 +42,5 @@ type CheckpointingKeeper interface { type IncentiveKeeper interface { RewardBTCTimestamping(ctx context.Context, epoch uint64, rewardDistInfo *RewardDistInfo) + IndexRefundableMsg(ctx context.Context, msg sdk.Msg) } diff --git a/x/btccheckpoint/types/mock_keepers.go b/x/btccheckpoint/types/mock_keepers.go index 8d58c3dc..5e4037a9 100644 --- a/x/btccheckpoint/types/mock_keepers.go +++ b/x/btccheckpoint/types/mock_keepers.go @@ -6,6 +6,7 @@ import ( txformat "github.com/babylonlabs-io/babylon/btctxformatter" bbn "github.com/babylonlabs-io/babylon/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) type MockBTCLightClientKeeper struct { @@ -97,3 +98,6 @@ func (ck MockCheckpointingKeeper) SetCheckpointForgotten(ctx context.Context, ep func (ik *MockIncentiveKeeper) RewardBTCTimestamping(ctx context.Context, epoch uint64, rewardDistInfo *RewardDistInfo) { } + +func (ik *MockIncentiveKeeper) IndexRefundableMsg(ctx context.Context, msg sdk.Msg) { +} diff --git a/x/btcstaking/genesis_test.go b/x/btcstaking/genesis_test.go index 2ab7d793..9a367272 100644 --- a/x/btcstaking/genesis_test.go +++ b/x/btcstaking/genesis_test.go @@ -17,7 +17,7 @@ func TestGenesis(t *testing.T) { Params: []*types.Params{&p}, } - k, ctx := keepertest.BTCStakingKeeper(t, nil, nil, nil) + k, ctx := keepertest.BTCStakingKeeper(t, nil, nil, nil, nil) btcstaking.InitGenesis(ctx, *k, genesisState) got := btcstaking.ExportGenesis(ctx, *k) require.NotNil(t, got) diff --git a/x/btcstaking/keeper/btc_height_index_test.go b/x/btcstaking/keeper/btc_height_index_test.go index a099df26..b3d7e951 100644 --- a/x/btcstaking/keeper/btc_height_index_test.go +++ b/x/btcstaking/keeper/btc_height_index_test.go @@ -23,7 +23,7 @@ func FuzzBTCHeightIndex(f *testing.F) { // mock BTC light client btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, nil, nil) + keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, nil, nil, nil) // randomise Babylon height and BTC height babylonHeight := datagen.RandomInt(r, 100) diff --git a/x/btcstaking/keeper/grpc_query_test.go b/x/btcstaking/keeper/grpc_query_test.go index 8239462a..df9691f0 100644 --- a/x/btcstaking/keeper/grpc_query_test.go +++ b/x/btcstaking/keeper/grpc_query_test.go @@ -29,7 +29,7 @@ func FuzzActivatedHeight(f *testing.F) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) ctx = sdk.UnwrapSDKContext(ctx) // not activated yet @@ -54,7 +54,7 @@ func FuzzFinalityProviders(f *testing.F) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) ctx = sdk.UnwrapSDKContext(ctx) // Generate random finality providers and add them to kv store @@ -119,7 +119,7 @@ func FuzzFinalityProvider(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) ctx = sdk.UnwrapSDKContext(ctx) // Generate random finality providers and add them to kv store @@ -175,7 +175,7 @@ func FuzzPendingBTCDelegations(f *testing.F) { btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() - keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil, nil) // covenant and slashing addr covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r) @@ -279,7 +279,7 @@ func FuzzFinalityProviderPowerAtHeight(f *testing.F) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) // random finality provider fp, err := datagen.GenRandomFinalityProvider(r) @@ -328,7 +328,7 @@ func FuzzFinalityProviderCurrentVotingPower(f *testing.F) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) // random finality provider fp, err := datagen.GenRandomFinalityProvider(r) @@ -380,7 +380,7 @@ func FuzzActiveFinalityProvidersAtHeight(f *testing.F) { btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 10}).AnyTimes() btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() - keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil, nil) // covenant and slashing addr covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r) @@ -500,7 +500,7 @@ func FuzzFinalityProviderDelegations(f *testing.F) { btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() - keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil, nil) // covenant and slashing addr covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r) diff --git a/x/btcstaking/keeper/keeper.go b/x/btcstaking/keeper/keeper.go index e1d38689..e9e3dbaa 100644 --- a/x/btcstaking/keeper/keeper.go +++ b/x/btcstaking/keeper/keeper.go @@ -22,6 +22,7 @@ type ( btclcKeeper types.BTCLightClientKeeper btccKeeper types.BtcCheckpointKeeper FinalityKeeper types.FinalityKeeper + iKeeper types.IncentiveKeeper hooks types.BtcStakingHooks @@ -39,6 +40,7 @@ func NewKeeper( btclcKeeper types.BTCLightClientKeeper, btccKeeper types.BtcCheckpointKeeper, finalityKeeper types.FinalityKeeper, + iKeeper types.IncentiveKeeper, btcNet *chaincfg.Params, authority string, @@ -50,6 +52,7 @@ func NewKeeper( btclcKeeper: btclcKeeper, btccKeeper: btccKeeper, FinalityKeeper: finalityKeeper, + iKeeper: iKeeper, hooks: nil, diff --git a/x/btcstaking/keeper/keeper_test.go b/x/btcstaking/keeper/keeper_test.go index ce2bf0aa..78ccf6e9 100644 --- a/x/btcstaking/keeper/keeper_test.go +++ b/x/btcstaking/keeper/keeper_test.go @@ -46,11 +46,16 @@ func NewHelper( btccKeeper *types.MockBtcCheckpointKeeper, finalityKeeper *types.MockFinalityKeeper, ) *Helper { - k, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper, finalityKeeper) + ctrl := gomock.NewController(t) + + // mock refundable messages + iKeeper := types.NewMockIncentiveKeeper(ctrl) + iKeeper.EXPECT().IndexRefundableMsg(gomock.Any(), gomock.Any()).AnyTimes() + + k, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper, finalityKeeper, iKeeper) ctx = ctx.WithHeaderInfo(header.Info{Height: 1}) msgSrvr := keeper.NewMsgServerImpl(*k) - ctrl := gomock.NewController(t) mockedHooks := types.NewMockBtcStakingHooks(ctrl) mockedHooks.EXPECT().AfterFinalityProviderActivated(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() k.SetHooks(mockedHooks) diff --git a/x/btcstaking/keeper/msg_server.go b/x/btcstaking/keeper/msg_server.go index a39169b6..0355a600 100644 --- a/x/btcstaking/keeper/msg_server.go +++ b/x/btcstaking/keeper/msg_server.go @@ -306,6 +306,10 @@ func (ms msgServer) AddBTCDelegationInclusionProof( wValue := ms.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout ms.addPowerDistUpdateEvent(ctx, btcDel.EndHeight-wValue, unbondedEvent) + // at this point, the BTC delegation inclusion proof is verified and is not duplicated + // thus, we can safely consider this message as refundable + ms.iKeeper.IndexRefundableMsg(ctx, req) + return &types.MsgAddBTCDelegationInclusionProofResponse{}, nil } @@ -466,6 +470,12 @@ func (ms msgServer) AddCovenantSigs(goCtx context.Context, req *types.MsgAddCove params, ) + // at this point, the covenant signatures are verified and are not duplicated. + // Thus, we can safely consider this message as refundable + // TODO: currently we refund tx fee for covenant signatures even if the BTC + // delegation already has a covenant quorum. Should we refund in this case? + ms.iKeeper.IndexRefundableMsg(ctx, req) + return &types.MsgAddCovenantSigsResponse{}, nil } @@ -530,6 +540,10 @@ func (ms msgServer) BTCUndelegate(goCtx context.Context, req *types.MsgBTCUndele // and set back ms.btcUndelegate(ctx, btcDel, req.UnbondingTxSig) + // At this point, the unbonding signature is verified. + // Thus, we can safely consider this message as refundable + ms.iKeeper.IndexRefundableMsg(ctx, req) + return &types.MsgBTCUndelegateResponse{}, nil } @@ -594,5 +608,9 @@ func (ms msgServer) SelectiveSlashingEvidence(goCtx context.Context, req *types. panic(fmt.Errorf("failed to emit EventSelectiveSlashing event: %w", err)) } + // At this point, the selective slashing evidence is verified and is not duplicated. + // Thus, we can safely consider this message as refundable + ms.iKeeper.IndexRefundableMsg(ctx, req) + return &types.MsgSelectiveSlashingEvidenceResponse{}, nil } diff --git a/x/btcstaking/keeper/params_test.go b/x/btcstaking/keeper/params_test.go index 81f4d53a..147bd401 100644 --- a/x/btcstaking/keeper/params_test.go +++ b/x/btcstaking/keeper/params_test.go @@ -13,7 +13,7 @@ import ( ) func TestGetParams(t *testing.T) { - k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) params := types.DefaultParams() err := k.SetParams(ctx, params) @@ -23,7 +23,7 @@ func TestGetParams(t *testing.T) { } func TestGetParamsVersions(t *testing.T) { - k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) params := types.DefaultParams() pv := k.GetParamsWithVersion(ctx) @@ -56,7 +56,7 @@ func FuzzParamsVersioning(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) numVersionsToGenerate := r.Intn(100) + 1 params0 := k.GetParams(ctx) var generatedParams []*types.Params diff --git a/x/btcstaking/keeper/query_params_test.go b/x/btcstaking/keeper/query_params_test.go index 9522e06c..b1d5f248 100644 --- a/x/btcstaking/keeper/query_params_test.go +++ b/x/btcstaking/keeper/query_params_test.go @@ -10,7 +10,7 @@ import ( ) func TestParamsQuery(t *testing.T) { - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) params := types.DefaultParams() err := keeper.SetParams(ctx, params) @@ -22,7 +22,7 @@ func TestParamsQuery(t *testing.T) { } func TestParamsByVersionQuery(t *testing.T) { - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) // starting with `1` as BTCStakingKeeper creates params with version 0 params1 := types.DefaultParams() diff --git a/x/btcstaking/types/expected_keepers.go b/x/btcstaking/types/expected_keepers.go index 3f4a1262..72b75cfe 100644 --- a/x/btcstaking/types/expected_keepers.go +++ b/x/btcstaking/types/expected_keepers.go @@ -6,6 +6,7 @@ import ( bbn "github.com/babylonlabs-io/babylon/types" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) type BTCLightClientKeeper interface { @@ -25,3 +26,7 @@ type FinalityKeeper interface { type BtcStakingHooks interface { AfterFinalityProviderActivated(ctx context.Context, fpPk *bbn.BIP340PubKey) error } + +type IncentiveKeeper interface { + IndexRefundableMsg(ctx context.Context, msg sdk.Msg) +} diff --git a/x/btcstaking/types/mocked_keepers.go b/x/btcstaking/types/mocked_keepers.go index 2b7cfc3e..ced9e5be 100644 --- a/x/btcstaking/types/mocked_keepers.go +++ b/x/btcstaking/types/mocked_keepers.go @@ -11,6 +11,7 @@ import ( types "github.com/babylonlabs-io/babylon/types" types0 "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" types1 "github.com/babylonlabs-io/babylon/x/btclightclient/types" + types2 "github.com/cosmos/cosmos-sdk/types" gomock "github.com/golang/mock/gomock" ) @@ -189,3 +190,38 @@ func (mr *MockBtcStakingHooksMockRecorder) AfterFinalityProviderActivated(ctx, f mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterFinalityProviderActivated", reflect.TypeOf((*MockBtcStakingHooks)(nil).AfterFinalityProviderActivated), ctx, fpPk) } + +// MockIncentiveKeeper is a mock of IncentiveKeeper interface. +type MockIncentiveKeeper struct { + ctrl *gomock.Controller + recorder *MockIncentiveKeeperMockRecorder +} + +// MockIncentiveKeeperMockRecorder is the mock recorder for MockIncentiveKeeper. +type MockIncentiveKeeperMockRecorder struct { + mock *MockIncentiveKeeper +} + +// NewMockIncentiveKeeper creates a new mock instance. +func NewMockIncentiveKeeper(ctrl *gomock.Controller) *MockIncentiveKeeper { + mock := &MockIncentiveKeeper{ctrl: ctrl} + mock.recorder = &MockIncentiveKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIncentiveKeeper) EXPECT() *MockIncentiveKeeperMockRecorder { + return m.recorder +} + +// IndexRefundableMsg mocks base method. +func (m *MockIncentiveKeeper) IndexRefundableMsg(ctx context.Context, msg types2.Msg) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IndexRefundableMsg", ctx, msg) +} + +// IndexRefundableMsg indicates an expected call of IndexRefundableMsg. +func (mr *MockIncentiveKeeperMockRecorder) IndexRefundableMsg(ctx, msg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexRefundableMsg", reflect.TypeOf((*MockIncentiveKeeper)(nil).IndexRefundableMsg), ctx, msg) +} diff --git a/x/finality/keeper/msg_server.go b/x/finality/keeper/msg_server.go index fb06784c..978eb652 100644 --- a/x/finality/keeper/msg_server.go +++ b/x/finality/keeper/msg_server.go @@ -179,7 +179,7 @@ func (ms msgServer) AddFinalitySig(goCtx context.Context, req *types.MsgAddFinal // at this point, the finality signature is 1) valid, 2) over a canonical block, // and 3) not duplicated - // Thus, we can safety consider this message as refundable + // Thus, we can safely consider this message as refundable ms.IncentiveKeeper.IndexRefundableMsg(ctx, req) return &types.MsgAddFinalitySigResponse{}, nil diff --git a/x/finality/types/expected_keepers.go b/x/finality/types/expected_keepers.go index 72d6b294..e77b37a2 100644 --- a/x/finality/types/expected_keepers.go +++ b/x/finality/types/expected_keepers.go @@ -27,7 +27,8 @@ type CheckpointingKeeper interface { GetLastFinalizedEpoch(ctx context.Context) uint64 } -// IncentiveKeeper defines the expected interface needed to distribute rewards. +// IncentiveKeeper defines the expected interface needed for distributing rewards +// and refund transaction fee for finality signatures type IncentiveKeeper interface { RewardBTCStaking(ctx context.Context, height uint64, filteredDc *bstypes.VotingPowerDistCache) IndexRefundableMsg(ctx context.Context, msg sdk.Msg) diff --git a/x/incentive/keeper/keeper.go b/x/incentive/keeper/keeper.go index 378ee7a5..c66d3804 100644 --- a/x/incentive/keeper/keeper.go +++ b/x/incentive/keeper/keeper.go @@ -16,9 +16,9 @@ type ( cdc codec.BinaryCodec storeService corestoretypes.KVStoreService - epochingKeeper types.EpochingKeeper bankKeeper types.BankKeeper accountKeeper types.AccountKeeper + epochingKeeper types.EpochingKeeper // RefundableMsgKeySet is the set of hashes of messages that can be refunded // Each key is a hash of the message bytes @@ -46,15 +46,15 @@ func NewKeeper( return Keeper{ cdc: cdc, storeService: storeService, - epochingKeeper: epochingKeeper, bankKeeper: bankKeeper, + accountKeeper: accountKeeper, + epochingKeeper: epochingKeeper, RefundableMsgKeySet: collections.NewKeySet( sb, types.RefundableMsgKeySetPrefix, "refundable_msg_key_set", collections.BytesKey, ), - accountKeeper: accountKeeper, authority: authority, feeCollectorName: feeCollectorName, } diff --git a/x/incentive/types/expected_keepers.go b/x/incentive/types/expected_keepers.go index 434a96c0..aac8d081 100644 --- a/x/incentive/types/expected_keepers.go +++ b/x/incentive/types/expected_keepers.go @@ -2,6 +2,7 @@ package types import ( "context" + epochingtypes "github.com/babylonlabs-io/babylon/x/epoching/types" sdk "github.com/cosmos/cosmos-sdk/types" )