From 2df260c32448e0b71feba92418f5311329ee302e Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Fri, 28 Feb 2025 09:03:27 -0300 Subject: [PATCH] fix: slashed fp inclusion proof (#579) - Add a new check if the FP was slashed in `ProcessAllPowerDistUpdateEvents` - Func `NewFinalityProviderDistInfo` was also modified to load the properties `IsJailed` and `IsSlashed` from the FP that was in DB (only this check was already enough to fix the issue), but to keep consistency with slashed event processing in the `ProcessAllPowerDistUpdateEvents` the slashed finality provider should not even be included in the `newDc` This check was to avoid activating a slashed FP The slashed FP could be activated if a BTC delegation without inclusion proof was created before the FP was slashed and having the inclusion proof sent after the slashing --- CHANGELOG.md | 2 + test/replay/driver.go | 6 +- testutil/datagen/btcstaking.go | 9 + x/btcstaking/keeper/btc_reorg_test.go | 2 +- x/finality/keeper/power_dist_change.go | 27 +- x/finality/keeper/power_dist_change_test.go | 380 ++++++++++++++++---- x/finality/types/power_table.go | 7 +- x/finality/types/power_table_test.go | 27 ++ 8 files changed, 379 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cc31b617..b8d28f7fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,8 @@ block bank send and still create BTC delegations - [#539](https://github.com/babylonlabs-io/babylon/pull/539) fix: add missing `x/checkpointing` hooks invocation +- [#579](https://github.com/babylonlabs-io/babylon/pull/579) Slashed FP gets activated in voting power distribution +cache if an old BTC delegation receives inclusion proof - [#592](https://github.com/babylonlabs-io/babylon/pull/592) finality: avoid refunding finality signatures over forks - [#525](https://github.com/babylonlabs-io/babylon/pull/525) fix: add back `NewIBCHeaderDecorator` post handler - [#563](https://github.com/babylonlabs-io/babylon/pull/563) reject coinbase staking transactions diff --git a/test/replay/driver.go b/test/replay/driver.go index eca2403f7..faa52e5df 100644 --- a/test/replay/driver.go +++ b/test/replay/driver.go @@ -550,7 +550,7 @@ func (d *BabylonAppDriver) GetBTCLCTip() (*wire.BlockHeader, uint32) { return tipInfo.Header.ToBlockHeader(), tipInfo.Height } -func blocksWithProofsToHeaderBytes(blocks []*datagen.BlockWithProofs) []bbn.BTCHeaderBytes { +func BlocksWithProofsToHeaderBytes(blocks []*datagen.BlockWithProofs) []bbn.BTCHeaderBytes { headerBytes := []bbn.BTCHeaderBytes{} for _, block := range blocks { headerBytes = append(headerBytes, bbn.NewBTCHeaderBytesFromBlockHeader(&block.Block.Header)) @@ -565,7 +565,7 @@ func (d *BabylonAppDriver) ExtendBTCLcWithNEmptyBlocks( ) (*wire.BlockHeader, uint32) { tip, _ := d.GetBTCLCTip() blocks := datagen.GenNEmptyBlocks(r, uint64(n), tip) - headers := blocksWithProofsToHeaderBytes(blocks) + headers := BlocksWithProofsToHeaderBytes(blocks) d.SendTxWithMsgsFromDriverAccount(t, &btclighttypes.MsgInsertHeaders{ Signer: d.GetDriverAccountAddress().String(), @@ -583,7 +583,7 @@ func (d *BabylonAppDriver) GenBlockWithTransactions( ) *datagen.BlockWithProofs { tip, _ := d.GetBTCLCTip() block := datagen.GenRandomBtcdBlockWithTransactions(r, txs, tip) - headers := blocksWithProofsToHeaderBytes([]*datagen.BlockWithProofs{block}) + headers := BlocksWithProofsToHeaderBytes([]*datagen.BlockWithProofs{block}) d.SendTxWithMsgsFromDriverAccount(t, &btclighttypes.MsgInsertHeaders{ Signer: d.GetDriverAccountAddress().String(), diff --git a/testutil/datagen/btcstaking.go b/testutil/datagen/btcstaking.go index 523d5290e..fc5edf1ac 100644 --- a/testutil/datagen/btcstaking.go +++ b/testutil/datagen/btcstaking.go @@ -36,6 +36,15 @@ func GenRandomFinalityProvider(r *rand.Rand) (*bstypes.FinalityProvider, error) return GenRandomFinalityProviderWithBTCSK(r, btcSK) } +func GenRandomMsgCreateFinalityProvider(r *rand.Rand) (*bstypes.MsgCreateFinalityProvider, error) { + // BTC key pairs + btcSK, _, err := GenRandomBTCKeyPair(r) + if err != nil { + return nil, err + } + return GenRandomCreateFinalityProviderMsgWithBTCBabylonSKs(r, btcSK, GenRandomAccount().GetAddress()) +} + func CreateNFinalityProviders(r *rand.Rand, t *testing.T, n int) []*bstypes.FinalityProvider { fps := make([]*bstypes.FinalityProvider, n) for i := 0; i < n; i++ { diff --git a/x/btcstaking/keeper/btc_reorg_test.go b/x/btcstaking/keeper/btc_reorg_test.go index 56ebaa96a..e7ca94a38 100644 --- a/x/btcstaking/keeper/btc_reorg_test.go +++ b/x/btcstaking/keeper/btc_reorg_test.go @@ -198,7 +198,7 @@ func TestSetLargestBtcReorg(t *testing.T) { require.NoError(t, err) actLargestBtcReorg := k.GetLargestBtcReorg(ctx) - require.Equal(t, tc.expectedLargestBtcReorg, actLargestBtcReorg) + require.EqualValues(t, tc.expectedLargestBtcReorg, actLargestBtcReorg) }) } } diff --git a/x/finality/keeper/power_dist_change.go b/x/finality/keeper/power_dist_change.go index 75e602163..de429af64 100644 --- a/x/finality/keeper/power_dist_change.go +++ b/x/finality/keeper/power_dist_change.go @@ -174,7 +174,7 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents( ) *ftypes.VotingPowerDistCache { // a map where key is finality provider's BTC PK hex and value is a list // of BTC delegations satoshis amount that newly become active under this provider - activedSatsByFpBtcPk := map[string][]uint64{} + activatedSatsByFpBtcPk := map[string][]uint64{} // a map where key is finality provider's BTC PK hex and value is a list // of BTC delegations satoshis that were unbonded or expired without previously // being unbonded @@ -210,9 +210,11 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents( // add the BTC delegation to each restaked finality provider for _, fpBTCPK := range btcDel.FpBtcPkList { fpBTCPKHex := fpBTCPK.MarshalHex() - activedSatsByFpBtcPk[fpBTCPKHex] = append(activedSatsByFpBtcPk[fpBTCPKHex], btcDel.TotalSat) + activatedSatsByFpBtcPk[fpBTCPKHex] = append(activatedSatsByFpBtcPk[fpBTCPKHex], btcDel.TotalSat) } + // FP could be already slashed when it is being activated, but it is okay + // since slashed finality providers do not earn rewards k.processRewardTracker(ctx, fpByBtcPkHex, btcDel, func(fp, del sdk.AccAddress, sats uint64) { k.MustProcessBtcDelegationActivated(ctx, fp, del, sats) }) @@ -287,7 +289,7 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents( } // process all new BTC delegations under this finality provider - if fpActiveSats, ok := activedSatsByFpBtcPk[fpBTCPKHex]; ok { + if fpActiveSats, ok := activatedSatsByFpBtcPk[fpBTCPKHex]; ok { // handle new BTC delegations for this finality provider for _, activatedSats := range fpActiveSats { fp.AddBondedSats(activatedSats) @@ -295,7 +297,7 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents( // remove the finality provider entry in fpActiveSats map, so that // after the for loop the rest entries in fpActiveSats belongs to new // finality providers with new BTC delegations - delete(activedSatsByFpBtcPk, fpBTCPKHex) + delete(activatedSatsByFpBtcPk, fpBTCPKHex) } // process all new unbonding BTC delegations under this finality provider @@ -321,8 +323,8 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents( process new BTC delegations under new finality providers in activeBTCDels */ // sort new finality providers in activeBTCDels to ensure determinism - fpActiveBtcPkHexList := make([]string, 0, len(activedSatsByFpBtcPk)) - for fpBTCPKHex := range activedSatsByFpBtcPk { + fpActiveBtcPkHexList := make([]string, 0, len(activatedSatsByFpBtcPk)) + for fpBTCPKHex := range activatedSatsByFpBtcPk { // if the fp was slashed, should not even be added to the list _, isSlashed := slashedFPs[fpBTCPKHex] if isSlashed { @@ -338,6 +340,17 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents( for _, fpBTCPKHex := range fpActiveBtcPkHexList { // get the finality provider and initialise its dist info newFP := k.loadFP(ctx, fpByBtcPkHex, fpBTCPKHex) + if newFP == nil { + // This is a consumer FP rather than Babylon FP, skip it + continue + } + // if the fp is slashed it shouldn't be included in the newDc + if newFP.IsSlashed() { + // case if a BTC delegation is created without inclusion proof + // the finality provider gets slashed + // inclusion proof gets included and generates an EventPowerDistUpdate_BtcDelStateUpdate + continue + } fpDistInfo := ftypes.NewFinalityProviderDistInfo(newFP) // check for jailing cases @@ -349,7 +362,7 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents( } // add each BTC delegation - fpActiveSats := activedSatsByFpBtcPk[fpBTCPKHex] + fpActiveSats := activatedSatsByFpBtcPk[fpBTCPKHex] for _, activatedSats := range fpActiveSats { fpDistInfo.AddBondedSats(activatedSats) } diff --git a/x/finality/keeper/power_dist_change_test.go b/x/finality/keeper/power_dist_change_test.go index 2452800a0..bfb81b7a8 100644 --- a/x/finality/keeper/power_dist_change_test.go +++ b/x/finality/keeper/power_dist_change_test.go @@ -2,10 +2,13 @@ package keeper_test import ( "math/rand" + "strings" "testing" "time" sdkmath "cosmossdk.io/math" + babylonApp "github.com/babylonlabs-io/babylon/app" + "github.com/babylonlabs-io/babylon/test/replay" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" @@ -15,19 +18,199 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - babylonApp "github.com/babylonlabs-io/babylon/app" testutil "github.com/babylonlabs-io/babylon/testutil/btcstaking-helper" "github.com/babylonlabs-io/babylon/testutil/datagen" bbn "github.com/babylonlabs-io/babylon/types" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" btclckeeper "github.com/babylonlabs-io/babylon/x/btclightclient/keeper" + btclightclientkeeper "github.com/babylonlabs-io/babylon/x/btclightclient/keeper" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" btcstakingkeeper "github.com/babylonlabs-io/babylon/x/btcstaking/keeper" - "github.com/babylonlabs-io/babylon/x/btcstaking/types" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" finalitykeeper "github.com/babylonlabs-io/babylon/x/finality/keeper" + "github.com/babylonlabs-io/babylon/x/finality/types" ftypes "github.com/babylonlabs-io/babylon/x/finality/types" ) +func FuzzDistributionCacheVpCheck_FpSlashedBeforeInclusionProof(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() + r := rand.New(rand.NewSource(seed)) + + app := babylonApp.Setup(t, false) + ctx := app.BaseApp.NewContext(false) + + initHeader := ctx.HeaderInfo() + initHeader.Height = int64(1) + ctx = ctx.WithHeaderInfo(initHeader) + + btcStkK, finalityK, checkPointK, btcCheckK, btcLightK := app.BTCStakingKeeper, app.FinalityKeeper, app.CheckpointingKeeper, app.BtcCheckpointKeeper, app.BTCLightClientKeeper + msgSrvrBtcStk := btcstakingkeeper.NewMsgServerImpl(btcStkK) + btcNet := app.BTCLightClientKeeper.GetBTCNet() + btcStkParams, btcCheckParams := btcStkK.GetParams(ctx), btcCheckK.GetParams(ctx) + + covenantSKs, _, _ := bstypes.DefaultCovenantCommittee() + + createdFps := datagen.RandomInt(r, 4) + 2 + numDelsPerFp := datagen.RandomInt(r, 3) + 2 + createFpMsgsByBtcPk := make([]*btcstktypes.MsgCreateFinalityProvider, createdFps) + + var ( + btcDelWithoutInclusionProof *datagen.CreateDelegationInfo + fpToBeSlashed *btcstktypes.MsgCreateFinalityProvider + fpSlashedSK *secp256k1.PrivateKey + delegationInfosToIncludeProof []*datagen.CreateDelegationInfo + ) + for i := 0; i < int(createdFps); i++ { + fpBtcSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + fpMsg, err := datagen.GenRandomCreateFinalityProviderMsgWithBTCBabylonSKs(r, fpBtcSK, datagen.GenRandomAddress()) + require.NoError(t, err) + + createFpMsgsByBtcPk[i] = fpMsg + _, err = msgSrvrBtcStk.CreateFinalityProvider(ctx, fpMsg) + require.NoError(t, err) + + ctx = MaybeProduceBlock(t, r, app, ctx) + + fpBtcPk := []bbn.BIP340PubKey{*fpMsg.BtcPk} + for j := 0; j < int(numDelsPerFp); j++ { + delBtcSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + delCreationInfo := datagen.GenRandomMsgCreateBtcDelegationAndMsgAddCovenantSignatures(r, t, btcNet, datagen.GenRandomAddress(), fpBtcPk, delBtcSK, covenantSKs, &btcStkParams) + _, err = msgSrvrBtcStk.CreateBTCDelegation(ctx, delCreationInfo.MsgCreateBTCDelegation) + require.NoError(t, err) + + ctx = MaybeProduceBlock(t, r, app, ctx) + + for covI, _ := range covenantSKs { + _, err = msgSrvrBtcStk.AddCovenantSigs(ctx, delCreationInfo.MsgAddCovenantSigs[covI]) + require.NoError(t, err) + + ctx = MaybeProduceBlock(t, r, app, ctx) + } + + if btcDelWithoutInclusionProof == nil { + fpToBeSlashed = fpMsg + btcDelWithoutInclusionProof = delCreationInfo + fpSlashedSK = fpBtcSK + // the first one will be slashed, and the inclusion proof sent later + continue + } + + // add inclusion proof + delegationInfosToIncludeProof = append(delegationInfosToIncludeProof, delCreationInfo) + } + + // fps set pub rand + randListInfo, _, err := datagen.GenRandomMsgCommitPubRandList(r, fpBtcSK, uint64(ctx.BlockHeader().Height), 3000) + require.NoError(t, err) + + prc := &types.PubRandCommit{ + StartHeight: uint64(ctx.BlockHeader().Height), + NumPubRand: 3000, + Commitment: randListInfo.Commitment, + } + + finalityK.SetPubRandCommit(ctx, fpMsg.BtcPk, prc) + } + + ctx = ProduceBlock(t, r, app, ctx) + + checkPointK.SetLastFinalizedEpoch(ctx, 1) + + block, stakingTransactions := AddBtcBlockWithDelegations(t, r, app, ctx, delegationInfosToIncludeProof...) + + ctx = ProduceBlock(t, r, app, ctx) + + // make staking txs k-deep + AddNBtcBlock(t, r, app, ctx, uint(btcCheckParams.BtcConfirmationDepth)) + + // send proofs + for i, stakingTx := range stakingTransactions { + msgSrvrBtcStk.AddBTCDelegationInclusionProof(ctx, &bstypes.MsgAddBTCDelegationInclusionProof{ + Signer: datagen.GenRandomAccount().Address, + StakingTxHash: stakingTx.TxHash().String(), + StakingTxInclusionProof: bstypes.NewInclusionProofFromSpvProof(block.Proofs[i+1]), + }) + } + + // produce btc block to update tip height + AddBtcBlock(t, r, app, ctx) + + ctx = ProduceBlock(t, r, app, ctx) + + // all the fps are in the vp dst cache + vpDstCache := finalityK.GetVotingPowerDistCache(ctx, uint64(ctx.HeaderInfo().Height-1)) + require.Equal(t, len(vpDstCache.FinalityProviders), int(createdFps)) + + activeFps := vpDstCache.GetActiveFinalityProviderSet() + require.Equal(t, len(activeFps), int(createdFps)) + + // gets any active delegation from the fp to be slashed + var delSlashed *datagen.CreateDelegationInfo + for _, activeDel := range delegationInfosToIncludeProof { + if strings.EqualFold(fpToBeSlashed.BtcPk.MarshalHex(), activeDel.MsgCreateBTCDelegation.FpBtcPkList[0].MarshalHex()) { + delSlashed = activeDel + break + } + } + + _, err := msgSrvrBtcStk.SelectiveSlashingEvidence(ctx, &btcstktypes.MsgSelectiveSlashingEvidence{ + Signer: datagen.GenRandomAddress().String(), + StakingTxHash: delSlashed.StakingTxHash, + RecoveredFpBtcSk: fpSlashedSK.Serialize(), + }) + require.NoError(t, err) + + AddBtcBlock(t, r, app, ctx) + ctx = ProduceBlock(t, r, app, ctx) + + vpDstCacheAfterSlash := finalityK.GetVotingPowerDistCache(ctx, uint64(ctx.HeaderInfo().Height-1)) + activeFps = vpDstCacheAfterSlash.GetActiveFinalityProviderSet() + // since it was slashed, the number of active fps should be reduced + require.Equal(t, len(activeFps), int(createdFps-1)) + + // adds the inclusion proof of the btc delegation which the FP was slashed + block, stakingSlashedTx := AddBtcBlockWithDelegations(t, r, app, ctx, btcDelWithoutInclusionProof) + + ctx = ProduceBlock(t, r, app, ctx) + + // make staking txs k-deep + AddNBtcBlock(t, r, app, ctx, uint(btcCheckParams.BtcConfirmationDepth)) + + // send proofs + for i, stakingTx := range stakingSlashedTx { + msgSrvrBtcStk.AddBTCDelegationInclusionProof(ctx, &bstypes.MsgAddBTCDelegationInclusionProof{ + Signer: datagen.GenRandomAccount().Address, + StakingTxHash: stakingTx.TxHash().String(), + StakingTxInclusionProof: bstypes.NewInclusionProofFromSpvProof(block.Proofs[i+1]), + }) + } + + // check if the event to update delegation is there + height := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + tip := btcLightK.GetTipInfo(ctx) + lastBTCTipHeight := btcStkK.GetBTCHeightAtBabylonHeight(ctx, height-1) + events := btcStkK.GetAllPowerDistUpdateEvents(ctx, lastBTCTipHeight, tip.Height) + require.Equal(t, len(events), 1) + + AddBtcBlock(t, r, app, ctx) + ctx = ProduceBlock(t, r, app, ctx) + vpDstCacheAfterInclusionProofOfSlashedFp := finalityK.GetVotingPowerDistCache(ctx, uint64(ctx.HeaderInfo().Height-1)) + activeFps = vpDstCacheAfterInclusionProofOfSlashedFp.GetActiveFinalityProviderSet() + + // last check to verify that the voting power distribution cache didn't changed after including proof of an BTC delegation that contains a slashed finality provider + require.Equal(t, len(activeFps), int(createdFps-1)) + require.Equal(t, vpDstCacheAfterSlash, vpDstCacheAfterInclusionProofOfSlashedFp) + }) +} + func FuzzProcessAllPowerDistUpdateEvents_Determinism(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) @@ -37,8 +220,8 @@ func FuzzProcessAllPowerDistUpdateEvents_Determinism(f *testing.F) { defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -57,7 +240,7 @@ func FuzzProcessAllPowerDistUpdateEvents_Determinism(f *testing.F) { stakingValue := int64(2 * 10e8) // generate many new BTC delegations under each finality provider, and their corresponding events - events := []*types.EventPowerDistUpdate{} + events := []*btcstktypes.EventPowerDistUpdate{} for _, fpPK := range fpPKs { for i := 0; i < 5; i++ { delSK, _, err := datagen.GenRandomBTCKeyPair(r) @@ -76,9 +259,9 @@ func FuzzProcessAllPowerDistUpdateEvents_Determinism(f *testing.F) { 30, ) h.NoError(err) - event := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + event := btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: del.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_ACTIVE, + NewState: btcstktypes.BTCDelegationStatus_ACTIVE, }) events = append(events, event) } @@ -97,15 +280,15 @@ func CreateFpAndBtcDel( r *rand.Rand, ) ( h *testutil.Helper, - del *types.BTCDelegation, + del *btcstktypes.BTCDelegation, covenantSKs []*secp256k1.PrivateKey, ) { ctrl := gomock.NewController(t) defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h = testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -139,15 +322,15 @@ func FuzzProcessAllPowerDistUpdateEvents_ActiveAndUnbondTogether(f *testing.F) { r := rand.New(rand.NewSource(seed)) h, del, _ := CreateFpAndBtcDel(t, r) - eventActive := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + eventActive := btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: del.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_ACTIVE, + NewState: btcstktypes.BTCDelegationStatus_ACTIVE, }) - eventUnbond := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + eventUnbond := btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: del.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_UNBONDED, + NewState: btcstktypes.BTCDelegationStatus_UNBONDED, }) - events := []*types.EventPowerDistUpdate{eventActive, eventUnbond} + events := []*btcstktypes.EventPowerDistUpdate{eventActive, eventUnbond} newDc := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, ftypes.NewVotingPowerDistCache(), events) require.Len(t, newDc.FinalityProviders, 0) @@ -161,12 +344,12 @@ func FuzzProcessAllPowerDistUpdateEvents_ActiveAndSlashTogether(f *testing.F) { r := rand.New(rand.NewSource(seed)) h, del, _ := CreateFpAndBtcDel(t, r) - eventActive := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + eventActive := btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: del.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_ACTIVE, + NewState: btcstktypes.BTCDelegationStatus_ACTIVE, }) - eventSlash := types.NewEventPowerDistUpdateWithSlashedFP(&del.FpBtcPkList[0]) - events := []*types.EventPowerDistUpdate{eventActive, eventSlash} + eventSlash := btcstktypes.NewEventPowerDistUpdateWithSlashedFP(&del.FpBtcPkList[0]) + events := []*btcstktypes.EventPowerDistUpdate{eventActive, eventSlash} dc := ftypes.NewVotingPowerDistCache() newDc := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events) @@ -182,12 +365,12 @@ func FuzzProcessAllPowerDistUpdateEvents_PreApprovalWithSlahedFP(f *testing.F) { h, delNoPreApproval, covenantSKs := CreateFpAndBtcDel(t, r) // activates one delegation to the finality provider without preapproval - eventActive := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + eventActive := btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: delNoPreApproval.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_ACTIVE, + NewState: btcstktypes.BTCDelegationStatus_ACTIVE, }) - newDc := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, ftypes.NewVotingPowerDistCache(), []*types.EventPowerDistUpdate{eventActive}) + newDc := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, ftypes.NewVotingPowerDistCache(), []*btcstktypes.EventPowerDistUpdate{eventActive}) // updates as if that fp is timestamping for _, fp := range newDc.FinalityProviders { fp.IsTimestamped = true @@ -222,8 +405,8 @@ func FuzzProcessAllPowerDistUpdateEvents_PreApprovalWithSlahedFP(f *testing.F) { require.Equal(t, newDc.TotalVotingPower, delPreApproval.TotalSat) // slash the fp - slashEvent := types.NewEventPowerDistUpdateWithSlashedFP(&delPreApproval.FpBtcPkList[0]) - newDc = h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, newDc, []*types.EventPowerDistUpdate{slashEvent}) + slashEvent := btcstktypes.NewEventPowerDistUpdateWithSlashedFP(&delPreApproval.FpBtcPkList[0]) + newDc = h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, newDc, []*btcstktypes.EventPowerDistUpdate{slashEvent}) // fp should have be erased from the list newDc.ApplyActiveFinalityProviders(100) @@ -241,12 +424,12 @@ func FuzzProcessAllPowerDistUpdateEvents_PreApprovalWithSlahedFP(f *testing.F) { h.Equal(activatedDel.TotalSat, uint64(msgCreateBTCDelPreApproval.StakingValue)) // simulates the del tx getting activated - eventActive = types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + eventActive = btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: delPreApproval.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_ACTIVE, + NewState: btcstktypes.BTCDelegationStatus_ACTIVE, }) // it will get included in the new vp dist, but will not have voting power after ApplyActiveFinalityProviders - newDc = h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, newDc, []*types.EventPowerDistUpdate{eventActive}) + newDc = h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, newDc, []*btcstktypes.EventPowerDistUpdate{eventActive}) require.Len(t, newDc.FinalityProviders, 1) for _, fp := range newDc.FinalityProviders { @@ -266,12 +449,12 @@ func FuzzProcessAllPowerDistUpdateEvents_ActiveAndJailTogether(f *testing.F) { r := rand.New(rand.NewSource(seed)) h, del, _ := CreateFpAndBtcDel(t, r) - eventActive := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + eventActive := btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: del.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_ACTIVE, + NewState: btcstktypes.BTCDelegationStatus_ACTIVE, }) - eventJailed := types.NewEventPowerDistUpdateWithJailedFP(&del.FpBtcPkList[0]) - events := []*types.EventPowerDistUpdate{eventActive, eventJailed} + eventJailed := btcstktypes.NewEventPowerDistUpdateWithJailedFP(&del.FpBtcPkList[0]) + events := []*btcstktypes.EventPowerDistUpdate{eventActive, eventJailed} newDc := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, ftypes.NewVotingPowerDistCache(), events) for _, fp := range newDc.FinalityProviders { @@ -291,11 +474,11 @@ func FuzzProcessAllPowerDistUpdateEvents_SlashActiveFp(f *testing.F) { r := rand.New(rand.NewSource(seed)) h, del, _ := CreateFpAndBtcDel(t, r) - eventActive := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{ + eventActive := btcstktypes.NewEventPowerDistUpdateWithBTCDel(&btcstktypes.EventBTCDelegationStateUpdate{ StakingTxHash: del.MustGetStakingTxHash().String(), - NewState: types.BTCDelegationStatus_ACTIVE, + NewState: btcstktypes.BTCDelegationStatus_ACTIVE, }) - events := []*types.EventPowerDistUpdate{eventActive} + events := []*btcstktypes.EventPowerDistUpdate{eventActive} newDc := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, ftypes.NewVotingPowerDistCache(), events) for _, fp := range newDc.FinalityProviders { @@ -305,8 +488,8 @@ func FuzzProcessAllPowerDistUpdateEvents_SlashActiveFp(f *testing.F) { require.Equal(t, newDc.TotalVotingPower, del.TotalSat) // afer the fp has some active voting power slash it - eventSlash := types.NewEventPowerDistUpdateWithSlashedFP(&del.FpBtcPkList[0]) - events = []*types.EventPowerDistUpdate{eventSlash} + eventSlash := btcstktypes.NewEventPowerDistUpdateWithSlashedFP(&del.FpBtcPkList[0]) + events = []*btcstktypes.EventPowerDistUpdate{eventSlash} newDc = h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, newDc, events) newDc.ApplyActiveFinalityProviders(100) @@ -324,8 +507,8 @@ func FuzzSlashFinalityProviderEvent(f *testing.F) { defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -378,10 +561,10 @@ func FuzzSlashFinalityProviderEvent(f *testing.F) { h.NoError(err) err = h.BTCStakingKeeper.SlashFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) - require.ErrorIs(t, err, types.ErrFpAlreadySlashed) + require.ErrorIs(t, err, btcstktypes.ErrFpAlreadySlashed) err = h.BTCStakingKeeper.JailFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) - require.ErrorIs(t, err, types.ErrFpAlreadySlashed) + require.ErrorIs(t, err, btcstktypes.ErrFpAlreadySlashed) // at this point, there should be only 1 event that the finality provider is slashed btcTipHeight := btclcKeeper.GetTipInfo(h.Ctx).Height @@ -410,8 +593,8 @@ func FuzzJailFinalityProviderEvents(f *testing.F) { defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -469,7 +652,7 @@ func FuzzJailFinalityProviderEvents(f *testing.F) { h.NoError(err) err = h.BTCStakingKeeper.JailFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) - require.ErrorIs(t, err, types.ErrFpAlreadyJailed) + require.ErrorIs(t, err, btcstktypes.ErrFpAlreadyJailed) // ensure the jailed label is set fpAfterJailing, err := h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) @@ -543,8 +726,8 @@ func FuzzUnjailFinalityProviderEvents(f *testing.F) { defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -595,7 +778,7 @@ func FuzzUnjailFinalityProviderEvents(f *testing.F) { // try unjail fp that is not jailed, should expect error err = h.BTCStakingKeeper.UnjailFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) - require.ErrorIs(t, err, types.ErrFpNotJailed) + require.ErrorIs(t, err, btcstktypes.ErrFpNotJailed) /* Jail the finality provider and execute BeginBlock @@ -649,8 +832,8 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -696,7 +879,7 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { btcDelStateUpdate := events[0].GetBtcDelStateUpdate() require.NotNil(t, btcDelStateUpdate) require.Equal(t, stakingTxHash, btcDelStateUpdate.StakingTxHash) - require.Equal(t, types.BTCDelegationStatus_EXPIRED, btcDelStateUpdate.NewState) + require.Equal(t, btcstktypes.BTCDelegationStatus_EXPIRED, btcDelStateUpdate.NewState) // ensure this finality provider does not have voting power at the current height babylonHeight := datagen.RandomInt(r, 10) + 1 @@ -717,7 +900,7 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { btcDelStateUpdate = events[0].GetBtcDelStateUpdate() require.NotNil(t, btcDelStateUpdate) require.Equal(t, stakingTxHash, btcDelStateUpdate.StakingTxHash) - require.Equal(t, types.BTCDelegationStatus_ACTIVE, btcDelStateUpdate.NewState) + require.Equal(t, btcstktypes.BTCDelegationStatus_ACTIVE, btcDelStateUpdate.NewState) // ensure this finality provider does not have voting power at the current height // due to no timestamped randomness @@ -764,8 +947,8 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -833,7 +1016,7 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { btcDelStateUpdate := events[0].GetBtcDelStateUpdate() require.NotNil(t, btcDelStateUpdate) require.Equal(t, stakingTxHash, btcDelStateUpdate.StakingTxHash) - require.Equal(t, types.BTCDelegationStatus_ACTIVE, btcDelStateUpdate.NewState) + require.Equal(t, btcstktypes.BTCDelegationStatus_ACTIVE, btcDelStateUpdate.NewState) // the BTC delegation will be unbonded at end height - unbonding_time unbondedHeight := activatedDel.EndHeight - h.BTCStakingKeeper.GetParams(h.Ctx).UnbondingTimeBlocks @@ -842,7 +1025,7 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { btcDelStateUpdate = events[0].GetBtcDelStateUpdate() require.NotNil(t, btcDelStateUpdate) require.Equal(t, stakingTxHash, btcDelStateUpdate.StakingTxHash) - require.Equal(t, types.BTCDelegationStatus_EXPIRED, btcDelStateUpdate.NewState) + require.Equal(t, btcstktypes.BTCDelegationStatus_EXPIRED, btcDelStateUpdate.NewState) // ensure this finality provider does not have voting power at the current height // due to no timestamped randomness @@ -887,8 +1070,8 @@ func TestDoNotGenerateDuplicateEventsAfterHavingCovenantQuorum(t *testing.T) { defer ctrl.Finish() // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btclcKeeper := btcstktypes.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := btcstktypes.NewMockBtcCheckpointKeeper(ctrl) h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters @@ -932,7 +1115,7 @@ func TestDoNotGenerateDuplicateEventsAfterHavingCovenantQuorum(t *testing.T) { btcDelStateUpdate := events[0].GetBtcDelStateUpdate() require.NotNil(t, btcDelStateUpdate) require.Equal(t, expectedStakingTxHash, btcDelStateUpdate.StakingTxHash) - require.Equal(t, types.BTCDelegationStatus_EXPIRED, btcDelStateUpdate.NewState) + require.Equal(t, btcstktypes.BTCDelegationStatus_EXPIRED, btcDelStateUpdate.NewState) // ensure this finality provider does not have voting power at the current height babylonHeight := datagen.RandomInt(r, 10) + 1 @@ -958,7 +1141,66 @@ func TestDoNotGenerateDuplicateEventsAfterHavingCovenantQuorum(t *testing.T) { btcDelStateUpdate = events[0].GetBtcDelStateUpdate() require.NotNil(t, btcDelStateUpdate) require.Equal(t, expectedStakingTxHash, btcDelStateUpdate.StakingTxHash) - require.Equal(t, types.BTCDelegationStatus_ACTIVE, btcDelStateUpdate.NewState) + require.Equal(t, btcstktypes.BTCDelegationStatus_ACTIVE, btcDelStateUpdate.NewState) +} + +func AddBtcBlockWithDelegations(t *testing.T, r *rand.Rand, app *babylonApp.BabylonApp, ctx sdk.Context, delInfos ...*datagen.CreateDelegationInfo) (*datagen.BlockWithProofs, []*wire.MsgTx) { + btcLightK := app.BTCLightClientKeeper + msgSrvrBtcLight := btclightclientkeeper.NewMsgServerImpl(btcLightK) + + stkTxs := replay.DelegationInfosToBTCTx(delInfos) + tip := btcLightK.GetTipInfo(ctx) + block := datagen.GenRandomBtcdBlockWithTransactions(r, stkTxs, tip.Header.ToBlockHeader()) + headers := replay.BlocksWithProofsToHeaderBytes([]*datagen.BlockWithProofs{block}) + _, err := msgSrvrBtcLight.InsertHeaders(ctx, &btclctypes.MsgInsertHeaders{ + Signer: datagen.GenRandomAccount().Address, + Headers: headers, + }) + require.NoError(t, err) + + return block, stkTxs +} + +func MaybeProduceBlock(t *testing.T, r *rand.Rand, app *babylonApp.BabylonApp, ctx sdk.Context) sdk.Context { + if r.Int31n(10) > 5 { + return ctx + } + + return ProduceBlock(t, r, app, ctx) +} + +func ProduceBlock(t *testing.T, r *rand.Rand, app *babylonApp.BabylonApp, ctx sdk.Context) sdk.Context { + _, err := app.BeginBlocker(ctx) + require.NoError(t, err) + _, err = app.EndBlocker(ctx) + require.NoError(t, err) + + header := ctx.HeaderInfo() + header.Height += 1 + return ctx.WithHeaderInfo(header) +} + +func AddBtcBlock(t *testing.T, r *rand.Rand, app *babylonApp.BabylonApp, ctx sdk.Context) { + btcLightK := app.BTCLightClientKeeper + msgSrvrBtcLight := btclightclientkeeper.NewMsgServerImpl(btcLightK) + + prevBlockHeader := btcLightK.GetTipInfo(ctx).Header.ToBlockHeader() + dummyGeneralTx := datagen.CreateDummyTx() + dummyGeneralHeaderWithProof := datagen.CreateBlockWithTransaction(r, prevBlockHeader, dummyGeneralTx) + dummyGeneralHeader := dummyGeneralHeaderWithProof.HeaderBytes + generalHeaders := []bbn.BTCHeaderBytes{dummyGeneralHeader} + insertHeaderMsg := &btclctypes.MsgInsertHeaders{ + Signer: datagen.GenRandomAddress().String(), + Headers: generalHeaders, + } + _, err := msgSrvrBtcLight.InsertHeaders(ctx, insertHeaderMsg) + require.NoError(t, err) +} + +func AddNBtcBlock(t *testing.T, r *rand.Rand, app *babylonApp.BabylonApp, ctx sdk.Context, number uint) { + for i := 0; i < int(number); i++ { + AddBtcBlock(t, r, app, ctx) + } } // TestHandleLivenessPanic is to test whether the handle liveness will panic @@ -1004,7 +1246,7 @@ func TestHandleLivenessPanic(t *testing.T) { require.NoError(t, err) // 0. BTCStakingKeeper parameter setting - err = btcStakingKeeper.SetParams(ctx, types.Params{ + err = btcStakingKeeper.SetParams(ctx, btcstktypes.Params{ CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), CovenantQuorum: 1, MinStakingValueSat: 10000, @@ -1034,7 +1276,7 @@ func TestHandleLivenessPanic(t *testing.T) { checkpointingKeeper.SetLastFinalizedEpoch(ctx, 1) // Among externally created FPs, save the FP where i==5 - var targetFp *types.FinalityProvider + var targetFp *btcstktypes.FinalityProvider var targetFpSK *btcec.PrivateKey fpNum := 6 @@ -1124,9 +1366,9 @@ func createDelegationWithFinalityProvider( ctx sdk.Context, r *rand.Rand, fpIndex int, - fpInfo *types.FinalityProvider, // Must be non-nil + fpInfo *btcstktypes.FinalityProvider, // Must be non-nil fpSK *btcec.PrivateKey, // Must be non-nil - btcStakingMsgServer types.MsgServer, + btcStakingMsgServer btcstktypes.MsgServer, btcLcMsgServer btclctypes.MsgServer, finalityMsgServer ftypes.MsgServer, // Use finality related MsgServer type btcStakingKeeper btcstakingkeeper.Keeper, // keeper (passed by value) @@ -1185,7 +1427,7 @@ func createDelegationWithFinalityProvider( dummy1HeaderWithProof := datagen.CreateBlockWithTransaction(r, btcHeader.ToBlockHeader(), dummy1Tx) dummy1Header := dummy1HeaderWithProof.HeaderBytes - txInclusionProof := types.NewInclusionProof( + txInclusionProof := btcstktypes.NewInclusionProof( &btcctypes.TransactionKey{Index: 1, Hash: btcHeader.Hash()}, btcHeaderWithProof.SpvProof.MerkleNodes, ) @@ -1223,7 +1465,7 @@ func createDelegationWithFinalityProvider( require.NoError(t, err) // 4. Delegation creation message sending - msgCreateBTCDel := &types.MsgCreateBTCDelegation{ + msgCreateBTCDel := &btcstktypes.MsgCreateBTCDelegation{ StakerAddr: stakerAddr.String(), FpBtcPkList: []bbn.BIP340PubKey{*finalityFP.BtcPk}, BtcPk: bbn.NewBIP340PubKeyFromBTCPK(delSK.PubKey()), @@ -1273,7 +1515,7 @@ func createDelegationWithFinalityProvider( ) require.NoError(t, err) - msgAddCovenantSig := &types.MsgAddCovenantSigs{ + msgAddCovenantSig := &btcstktypes.MsgAddCovenantSigs{ Signer: msgCreateBTCDel.StakerAddr, Pk: covenantSlashingTxSigs[0].CovPk, StakingTxHash: stakingTxHash.String(), @@ -1289,15 +1531,15 @@ func createAndCommitFinalityProvider( t *testing.T, ctx sdk.Context, r *rand.Rand, - fp *types.FinalityProvider, + fp *btcstktypes.FinalityProvider, fpSK *btcec.PrivateKey, - btcStakingMsgServer types.MsgServer, + btcStakingMsgServer btcstktypes.MsgServer, finalityMsgServer ftypes.MsgServer, ) { - fpMsg := &types.MsgCreateFinalityProvider{ + fpMsg := &btcstktypes.MsgCreateFinalityProvider{ Addr: fp.Addr, Description: fp.Description, - Commission: types.NewCommissionRates(*fp.Commission, fp.CommissionInfo.MaxRate, fp.CommissionInfo.MaxChangeRate), + Commission: btcstktypes.NewCommissionRates(*fp.Commission, fp.CommissionInfo.MaxRate, fp.CommissionInfo.MaxChangeRate), BtcPk: fp.BtcPk, Pop: fp.Pop, } diff --git a/x/finality/types/power_table.go b/x/finality/types/power_table.go index cab2f559f..3979dda6f 100644 --- a/x/finality/types/power_table.go +++ b/x/finality/types/power_table.go @@ -95,7 +95,6 @@ func (dc *VotingPowerDistCache) ApplyActiveFinalityProviders(maxActiveFPs uint32 } totalVotingPower := uint64(0) - for i := uint32(0); i < numActiveFPs; i++ { totalVotingPower += dc.FinalityProviders[i].TotalBondedSat } @@ -140,12 +139,18 @@ func (dc *VotingPowerDistCache) GetInactiveFinalityProviderSet() map[string]*Fin return inactiveFps } +// NewFinalityProviderDistInfo loads the FinalityProviderDistInfo based on the fp data. +// Note: The IsTimestamped property is always set to false, as it is not possible to determine +// the timestamp without the tip height. func NewFinalityProviderDistInfo(fp *bstypes.FinalityProvider) *FinalityProviderDistInfo { return &FinalityProviderDistInfo{ BtcPk: fp.BtcPk, Addr: sdk.MustAccAddressFromBech32(fp.Addr), Commission: fp.Commission, TotalBondedSat: 0, + IsJailed: fp.Jailed, + IsSlashed: fp.IsSlashed(), + IsTimestamped: false, } } diff --git a/x/finality/types/power_table_test.go b/x/finality/types/power_table_test.go index c15a00486..ab38cc0ad 100644 --- a/x/finality/types/power_table_test.go +++ b/x/finality/types/power_table_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/btcsuite/btcd/btcec/v2" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/babylonlabs-io/babylon/testutil/datagen" @@ -261,6 +262,32 @@ func TestSortFinalityProvidersWithZeroedVotingPower(t *testing.T) { } } +func FuzzNewFinalityProviderDistInfo(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 1000) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + + if r.Int31n(10) > 5 { + fp.SlashedBabylonHeight = r.Uint64() + } + if r.Int31n(10) > 5 { + fp.Jailed = true + } + + fpDstInf := types.NewFinalityProviderDistInfo(fp) + require.Equal(t, fpDstInf.BtcPk.MarshalHex(), fp.BtcPk.MarshalHex()) + require.Equal(t, sdk.AccAddress(fpDstInf.Addr).String(), fp.Addr) + require.Equal(t, fpDstInf.Commission.String(), fp.Commission.String()) + require.Equal(t, fpDstInf.TotalBondedSat, uint64(0)) + require.Equal(t, fpDstInf.IsJailed, fp.Jailed) + require.Equal(t, fpDstInf.IsSlashed, fp.IsSlashed()) + require.Equal(t, fpDstInf.IsTimestamped, false) + }) +} + // FuzzSortingDeterminism tests the property of the sorting algorithm that the result should // be deterministic func FuzzSortingDeterminism(f *testing.F) {