Skip to content

Commit

Permalink
feat(ADR-024): Exclude voting power for fps who do not have timestamp…
Browse files Browse the repository at this point in the history
…ed pub rand (#23)

This closes the second part of #8. In particular, this PR:
* Implemented `HasTimestampedPubRand` in the finality keeper to check
whether a pub rand at a given height is BTC-timestamped
* Added expected finality keeper to btcstaking keeper so that
`HasTimestampedPubRand` can be called within the module
* When signing voting power to fps in `btcstaking`'s begin blocker, if
the fp does not have timestamped pub rand, voting power will not be
assigned

Note that this PR introduced circular dependency between the btc staking
module and finality module, which should be addressed by moving the
voting power table to the finality module, tracked by issue #24
  • Loading branch information
gitferry committed Sep 3, 2024
1 parent 3d8bf48 commit 0fd948d
Show file tree
Hide file tree
Showing 27 changed files with 365 additions and 148 deletions.
6 changes: 6 additions & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,9 @@ func (ak *AppKeepers) InitKeepers(
runtime.NewKVStoreService(keys[btcstakingtypes.StoreKey]),
&btclightclientKeeper,
&btcCheckpointKeeper,
// setting the finality keeper as nil for now
// need to set it after finality keeper is initiated
nil,
btcNetParams,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
Expand All @@ -543,6 +546,9 @@ func (ak *AppKeepers) InitKeepers(
)
ak.BTCStakingKeeper = *ak.BTCStakingKeeper.SetHooks(btcstakingtypes.NewMultiBtcStakingHooks(ak.FinalityKeeper.Hooks()))
ak.FinalityKeeper = *ak.FinalityKeeper.SetHooks(finalitytypes.NewMultiFinalityHooks(ak.BTCStakingKeeper.Hooks()))
// TODO this introduces circular dependency between the finality module and
// the btcstaking modules, need refactoring
ak.BTCStakingKeeper.FinalityKeeper = ak.FinalityKeeper

// create evidence keeper with router
evidenceKeeper := evidencekeeper.NewKeeper(
Expand Down
9 changes: 9 additions & 0 deletions proto/babylon/btcstaking/v1/incentive.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ option go_package = "github.com/babylonlabs-io/babylon/x/btcstaking/types";
// VotingPowerDistCache is the cache for voting power distribution of finality providers
// and their BTC delegations at a height
message VotingPowerDistCache {
option (gogoproto.goproto_getters) = false;
// total_voting_power is the total voting power of all the finality providers
// in the cache
uint64 total_voting_power = 1;
// finality_providers is a list of finality providers' voting power information
repeated FinalityProviderDistInfo finality_providers = 2;
// num_active_fps is the number of finality providers that have positive voting power
// as well as timestamped public randomness
uint32 num_active_fps = 3;
}

// FinalityProviderDistInfo is the reward distribution of a finality provider and its BTC delegations
Expand All @@ -30,6 +36,9 @@ message FinalityProviderDistInfo {
uint64 total_voting_power = 4;
// btc_dels is a list of BTC delegations' voting power information under this finality provider
repeated BTCDelDistInfo btc_dels = 5;
// is_timestamped indicates whether the finality provider
// has timestamped public randomness committed
bool is_timestamped = 6;
}

// BTCDelDistInfo contains the information related to reward distribution for a BTC delegation
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/btc_staking_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func (s *BTCStakingTestSuite) Test3CommitPublicRandomnessAndSubmitFinalitySignat
s.Eventually(func() bool {
finalizedBlocks = nonValidatorNode.QueryListBlocks(ftypes.QueriedBlockStatus_FINALIZED)
return len(finalizedBlocks) > 0
}, time.Minute, time.Second*5)
}, time.Minute, time.Second)
s.Equal(activatedHeight, finalizedBlocks[0].Height)
s.Equal(appHash.Bytes(), finalizedBlocks[0].AppHash)

Expand Down
4 changes: 3 additions & 1 deletion testutil/datagen/incentive.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"math/rand"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"

btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types"
bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types"
itypes "github.com/babylonlabs-io/babylon/x/incentive/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

const (
Expand Down Expand Up @@ -105,6 +106,7 @@ func GenRandomFinalityProviderDistInfo(r *rand.Rand) (*bstypes.FinalityProviderD
}
fpDistInfo.BtcDels = append(fpDistInfo.BtcDels, btcDelDistInfo)
fpDistInfo.TotalVotingPower += btcDelDistInfo.VotingPower
fpDistInfo.IsTimestamped = true
}
return fpDistInfo, nil
}
Expand Down
2 changes: 2 additions & 0 deletions testutil/keeper/btcstaking.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func BTCStakingKeeper(
t testing.TB,
btclcKeeper types.BTCLightClientKeeper,
btccKeeper types.BtcCheckpointKeeper,
finalityKeeper types.FinalityKeeper,
) (*keeper.Keeper, sdk.Context) {
storeKey := storetypes.NewKVStoreKey(types.StoreKey)

Expand All @@ -43,6 +44,7 @@ func BTCStakingKeeper(
runtime.NewKVStoreService(storeKey),
btclcKeeper,
btccKeeper,
finalityKeeper,
&chaincfg.SimNetParams,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
Expand Down
2 changes: 1 addition & 1 deletion x/btcstaking/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestGenesis(t *testing.T) {
Params: []*types.Params{&p},
}

k, ctx := keepertest.BTCStakingKeeper(t, nil, nil)
k, ctx := keepertest.BTCStakingKeeper(t, nil, nil, nil)
btcstaking.InitGenesis(ctx, *k, genesisState)
got := btcstaking.ExportGenesis(ctx, *k)
require.NotNil(t, got)
Expand Down
3 changes: 2 additions & 1 deletion x/btcstaking/keeper/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ func benchBeginBlock(b *testing.B, numFPs int, numDelsUnderFP int) {
defer ctrl.Finish()
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(b, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(b, btclcKeeper, btccKeeper, finalityKeeper)
// set all parameters
covenantSKs, _ := h.GenAndApplyParams(r)
changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net)
Expand Down
2 changes: 1 addition & 1 deletion x/btcstaking/keeper/btc_height_index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func FuzzBTCHeightIndex(f *testing.F) {

// mock BTC light client
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, nil)
keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, nil, nil)

// randomise Babylon height and BTC height
babylonHeight := datagen.RandomInt(r, 100)
Expand Down
16 changes: 8 additions & 8 deletions x/btcstaking/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func FuzzActivatedHeight(f *testing.F) {
r := rand.New(rand.NewSource(seed))

// Setup keeper and context
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil)
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil)
ctx = sdk.UnwrapSDKContext(ctx)

// not activated yet
Expand All @@ -53,7 +53,7 @@ func FuzzFinalityProviders(f *testing.F) {
r := rand.New(rand.NewSource(seed))

// Setup keeper and context
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil)
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil)
ctx = sdk.UnwrapSDKContext(ctx)

// Generate random finality providers and add them to kv store
Expand Down Expand Up @@ -118,7 +118,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)
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil)
ctx = sdk.UnwrapSDKContext(ctx)

// Generate random finality providers and add them to kv store
Expand Down Expand Up @@ -174,7 +174,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)
keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil)

// covenant and slashing addr
covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r)
Expand Down Expand Up @@ -276,7 +276,7 @@ func FuzzFinalityProviderPowerAtHeight(f *testing.F) {
r := rand.New(rand.NewSource(seed))

// Setup keeper and context
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil)
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil)

// random finality provider
fp, err := datagen.GenRandomFinalityProvider(r)
Expand Down Expand Up @@ -325,7 +325,7 @@ func FuzzFinalityProviderCurrentVotingPower(f *testing.F) {
r := rand.New(rand.NewSource(seed))

// Setup keeper and context
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil)
keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil)

// random finality provider
fp, err := datagen.GenRandomFinalityProvider(r)
Expand Down Expand Up @@ -377,7 +377,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)
keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil)

// covenant and slashing addr
covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r)
Expand Down Expand Up @@ -496,7 +496,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)
keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil)

// covenant and slashing addr
covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r)
Expand Down
10 changes: 6 additions & 4 deletions x/btcstaking/keeper/incentive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ func FuzzRecordVotingPowerDistCache(f *testing.F) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
covenantSKs, _ := h.GenAndApplyParams(r)
Expand All @@ -42,7 +44,8 @@ func FuzzRecordVotingPowerDistCache(f *testing.F) {
}
}

// for the first numFpsWithVotingPower finality providers, generate a random number of BTC delegations and add covenant signatures to activate them
// for the first numFpsWithVotingPower finality providers, generate a random number of BTC
// delegations and add covenant signatures to activate them
numBTCDels := datagen.RandomInt(r, 10) + 1
stakingValue := datagen.RandomInt(r, 100000) + 100000
for _, fp := range fpsWithVotingPowerMap {
Expand Down Expand Up @@ -70,8 +73,7 @@ func FuzzRecordVotingPowerDistCache(f *testing.F) {
require.NoError(t, err)
require.NotNil(t, dc)
require.Equal(t, dc.TotalVotingPower, numFpsWithVotingPower*numBTCDels*stakingValue)
maxNumFps := h.BTCStakingKeeper.GetParams(h.Ctx).MaxActiveFinalityProviders
activeFPs := dc.GetActiveFinalityProviderSet(maxNumFps)
activeFPs := dc.GetActiveFinalityProviderSet()
for _, fpDistInfo := range activeFPs {
require.Equal(t, fpDistInfo.TotalVotingPower, numBTCDels*stakingValue)
fp, ok := fpsWithVotingPowerMap[fpDistInfo.Addr]
Expand Down
11 changes: 7 additions & 4 deletions x/btcstaking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ type (
cdc codec.BinaryCodec
storeService corestoretypes.KVStoreService

btclcKeeper types.BTCLightClientKeeper
btccKeeper types.BtcCheckpointKeeper
btclcKeeper types.BTCLightClientKeeper
btccKeeper types.BtcCheckpointKeeper
FinalityKeeper types.FinalityKeeper

hooks types.BtcStakingHooks

Expand All @@ -37,6 +38,7 @@ func NewKeeper(

btclcKeeper types.BTCLightClientKeeper,
btccKeeper types.BtcCheckpointKeeper,
finalityKeeper types.FinalityKeeper,

btcNet *chaincfg.Params,
authority string,
Expand All @@ -45,8 +47,9 @@ func NewKeeper(
cdc: cdc,
storeService: storeService,

btclcKeeper: btclcKeeper,
btccKeeper: btccKeeper,
btclcKeeper: btclcKeeper,
btccKeeper: btccKeeper,
FinalityKeeper: finalityKeeper,

hooks: nil,

Expand Down
10 changes: 8 additions & 2 deletions x/btcstaking/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,19 @@ type Helper struct {
BTCStakingKeeper *keeper.Keeper
BTCLightClientKeeper *types.MockBTCLightClientKeeper
BTCCheckpointKeeper *types.MockBtcCheckpointKeeper
FinalityKeeper *types.MockFinalityKeeper
BTCStakingHooks *types.MockBtcStakingHooks
MsgServer types.MsgServer
Net *chaincfg.Params
}

func NewHelper(t testing.TB, btclcKeeper *types.MockBTCLightClientKeeper, btccKeeper *types.MockBtcCheckpointKeeper) *Helper {
k, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper)
func NewHelper(
t testing.TB,
btclcKeeper *types.MockBTCLightClientKeeper,
btccKeeper *types.MockBtcCheckpointKeeper,
finalityKeeper *types.MockFinalityKeeper,
) *Helper {
k, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper, finalityKeeper)
ctx = ctx.WithHeaderInfo(header.Info{Height: 1})
msgSrvr := keeper.NewMsgServerImpl(*k)

Expand Down
30 changes: 20 additions & 10 deletions x/btcstaking/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ func FuzzMsgCreateFinalityProvider(f *testing.F) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
h.GenAndApplyParams(r)
Expand Down Expand Up @@ -143,7 +144,8 @@ func FuzzCreateBTCDelegation(f *testing.F) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
h.GenAndApplyParams(r)
Expand Down Expand Up @@ -187,7 +189,8 @@ func TestProperVersionInDelegation(t *testing.T) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
h.GenAndApplyParams(r)
Expand Down Expand Up @@ -254,7 +257,8 @@ func FuzzAddCovenantSigs(f *testing.F) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
covenantSKs, _ := h.GenAndApplyParams(r)
Expand Down Expand Up @@ -318,7 +322,8 @@ func FuzzBTCUndelegate(f *testing.F) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
covenantSKs, _ := h.GenAndApplyParams(r)
Expand Down Expand Up @@ -390,7 +395,8 @@ func FuzzSelectiveSlashing(f *testing.F) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
covenantSKs, _ := h.GenAndApplyParams(r)
Expand Down Expand Up @@ -456,7 +462,8 @@ func FuzzSelectiveSlashing_StakingTx(f *testing.F) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
covenantSKs, _ := h.GenAndApplyParams(r)
Expand Down Expand Up @@ -532,7 +539,8 @@ func TestDoNotAllowDelegationWithoutFinalityProvider(t *testing.T) {
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes()
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set covenant PK to params
_, covenantPKs := h.GenAndApplyParams(r)
Expand Down Expand Up @@ -699,7 +707,8 @@ func TestCorrectUnbondingTimeInDelegation(t *testing.T) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters
_, _ = h.GenAndApplyCustomParams(r, tt.finalizationTimeout, tt.minUnbondingTime)
Expand Down Expand Up @@ -771,7 +780,8 @@ func TestMinimalUnbondingRate(t *testing.T) {
// mock BTC light client and BTC checkpoint modules
btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper)
finalityKeeper := types.NewMockFinalityKeeper(ctrl)
h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper)

// set all parameters, by default minimal unbonding value is 80% of staking value
_, _ = h.GenAndApplyParams(r)
Expand Down
Loading

0 comments on commit 0fd948d

Please sign in to comment.