From b3ebfaaac80101ffa01ee07fa042300818dd82d9 Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Tue, 22 Oct 2024 19:50:08 +1100 Subject: [PATCH] finality: move voting power dist update to finality module (#216) First step of #24 This PR moves the voting power distribution update algorithm to `x/finality`. This is a major refactoring that includes - moving `max_active_finality_providers` parameter to `x/finality` - moving many functions in `x/btcstaking/power_dist_change.go` and relevant tests to `x/finality` - moving test utilities under `x/btcstaking/keeper_test.go` to a new module `testutil/btcstaking-helper`, and use it for existing tests of `x/btcstaking` and `x/finality` - removing the cyclic dependency between `x/btcstaking` and `x/finality`, as well as their hooks. Now the hooks in these 2 modules are not necessary, as the only dependency between them is that `x/finality` will invoke `x/btcstaking`, but not the other way TODOs in subsequent PRs: - moving some relevant query APIs to `x/finality` - moving the voting power dist cache and voting power table KV stores to `x/finality` --- CHANGELOG.md | 2 + app/keepers/keepers.go | 8 - app/upgrades/v1/mainnet/btcstaking_params.go | 3 +- app/upgrades/v1/mainnet/finality_params.go | 1 + app/upgrades/v1/testnet/btcstaking_params.go | 1 - app/upgrades/v1/testnet/finality_params.go | 1 + cmd/babylond/cmd/genesis.go | 2 +- proto/babylon/btcstaking/v1/params.proto | 4 +- proto/babylon/finality/v1/params.proto | 15 +- test/e2e/configurer/chain/chain.go | 2 +- test/e2e/configurer/chain/commands.go | 8 +- .../btcstaking-helper/keeper.go | 128 ++++-- testutil/keeper/btcstaking.go | 24 +- testutil/keeper/finality.go | 29 +- x/btcstaking/genesis_test.go | 2 +- x/btcstaking/keeper/bench_test.go | 4 +- x/btcstaking/keeper/btc_delegations.go | 2 +- x/btcstaking/keeper/btc_height_index_test.go | 2 +- x/btcstaking/keeper/genesis.go | 2 +- x/btcstaking/keeper/grpc_query_test.go | 19 +- x/btcstaking/keeper/hooks.go | 25 -- x/btcstaking/keeper/incentive.go | 12 +- x/btcstaking/keeper/incentive_test.go | 96 ----- x/btcstaking/keeper/keeper.go | 32 +- x/btcstaking/keeper/msg_server_test.go | 87 ++-- x/btcstaking/keeper/params_test.go | 6 +- x/btcstaking/keeper/power_dist_change.go | 310 +-------------- x/btcstaking/keeper/query_params_test.go | 4 +- .../keeper/voting_power_table_test.go | 298 +------------- x/btcstaking/types/expected_keepers.go | 4 - x/btcstaking/types/genesis_test.go | 38 +- x/btcstaking/types/hooks.go | 26 -- x/btcstaking/types/mocked_keepers.go | 37 -- x/btcstaking/types/params.go | 17 +- x/btcstaking/types/params.pb.go | 121 ++---- .../types/validate_parsed_message_test.go | 25 +- x/checkpointing/types/types.go | 4 +- x/finality/abci.go | 3 +- x/finality/keeper/hooks.go | 41 -- x/finality/keeper/keeper.go | 14 +- x/finality/keeper/liveness.go | 2 +- x/finality/keeper/liveness_test.go | 7 +- x/finality/keeper/msg_server.go | 7 +- x/finality/keeper/power_dist_change.go | 328 +++++++++++++++ .../keeper/power_dist_change_test.go | 105 ++--- x/finality/keeper/power_table_test.go | 375 ++++++++++++++++++ x/finality/keeper/tallying.go | 6 +- x/finality/keeper/tallying_bench_test.go | 2 +- x/finality/keeper/tallying_test.go | 3 +- x/finality/types/expected_keepers.go | 19 +- x/finality/types/hooks.go | 26 -- x/finality/types/mocked_keepers.go | 149 +++++-- x/finality/types/params.go | 35 +- x/finality/types/params.pb.go | 130 +++--- 54 files changed, 1301 insertions(+), 1352 deletions(-) rename x/btcstaking/keeper/keeper_test.go => testutil/btcstaking-helper/keeper.go (84%) delete mode 100644 x/btcstaking/keeper/hooks.go delete mode 100644 x/btcstaking/keeper/incentive_test.go delete mode 100644 x/btcstaking/types/hooks.go delete mode 100644 x/finality/keeper/hooks.go create mode 100644 x/finality/keeper/power_dist_change.go rename x/{btcstaking => finality}/keeper/power_dist_change_test.go (88%) create mode 100644 x/finality/keeper/power_table_test.go delete mode 100644 x/finality/types/hooks.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a4646d52..832569d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### State Machine Breaking * [#224](https://github.com/babylonlabs-io/babylon/pull/224) Make injected checkpoint a standard tx +* [#216](https://github.com/babylonlabs-io/babylon/pull/216) move voting power distribution +update algorithm to `x/finality` * [#207](https://github.com/babylonlabs-io/babylon/pull/207) Rename total voting power to total bonded sat * [#204](https://github.com/babylonlabs-io/babylon/pull/204) Add babylon finality diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 69871feb..a3571400 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -496,9 +496,6 @@ 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, &ak.IncentiveKeeper, btcNetParams, authtypes.NewModuleAddress(govtypes.ModuleName).String(), @@ -513,11 +510,6 @@ func (ak *AppKeepers) InitKeepers( ak.CheckpointingKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - 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( diff --git a/app/upgrades/v1/mainnet/btcstaking_params.go b/app/upgrades/v1/mainnet/btcstaking_params.go index e9459a3e..a9cc6013 100644 --- a/app/upgrades/v1/mainnet/btcstaking_params.go +++ b/app/upgrades/v1/mainnet/btcstaking_params.go @@ -21,6 +21,5 @@ const BtcStakingParamStr = ` "slashing_rate": "0.100000000000000000", "min_unbonding_time_blocks": 0, "unbonding_fee_sat": "1000", - "min_commission_rate": "0.03", - "max_active_finality_providers": 100 + "min_commission_rate": "0.03" }` diff --git a/app/upgrades/v1/mainnet/finality_params.go b/app/upgrades/v1/mainnet/finality_params.go index ea21af3a..1a83b3ac 100644 --- a/app/upgrades/v1/mainnet/finality_params.go +++ b/app/upgrades/v1/mainnet/finality_params.go @@ -3,6 +3,7 @@ package mainnet // TODO Some default parameters. Consider how to switch those depending on network: // mainnet, testnet, devnet etc. const FinalityParamStr = `{ + "max_active_finality_providers": 100, "signed_blocks_window": 100, "finality_sig_timeout": 3, "min_signed_per_window": "0.1", diff --git a/app/upgrades/v1/testnet/btcstaking_params.go b/app/upgrades/v1/testnet/btcstaking_params.go index 526e4270..7ae78e02 100644 --- a/app/upgrades/v1/testnet/btcstaking_params.go +++ b/app/upgrades/v1/testnet/btcstaking_params.go @@ -22,6 +22,5 @@ const BtcStakingParamStr = ` "min_unbonding_time_blocks": 0, "unbonding_fee_sat": "1000", "min_commission_rate": "0.03", - "max_active_finality_providers": 100, "delegation_creation_base_gas_fee": 1000 }` diff --git a/app/upgrades/v1/testnet/finality_params.go b/app/upgrades/v1/testnet/finality_params.go index 13f8e03a..1c7fe933 100644 --- a/app/upgrades/v1/testnet/finality_params.go +++ b/app/upgrades/v1/testnet/finality_params.go @@ -3,6 +3,7 @@ package testnet // TODO Some default parameters. Consider how to switch those depending on network: // mainnet, testnet, devnet etc. const FinalityParamStr = `{ + "max_active_finality_providers": 100, "signed_blocks_window": 100, "finality_sig_timeout": 3, "min_signed_per_window": "0.1", diff --git a/cmd/babylond/cmd/genesis.go b/cmd/babylond/cmd/genesis.go index 08a97d62..b099c2e0 100644 --- a/cmd/babylond/cmd/genesis.go +++ b/cmd/babylond/cmd/genesis.go @@ -412,7 +412,6 @@ func TestnetGenesisParams( genParams.BtcstakingParams.MinSlashingTxFeeSat = minSlashingFee genParams.BtcstakingParams.MinCommissionRate = minCommissionRate genParams.BtcstakingParams.SlashingRate = slashingRate - genParams.BtcstakingParams.MaxActiveFinalityProviders = maxActiveFinalityProviders genParams.BtcstakingParams.MinUnbondingTimeBlocks = uint32(minUnbondingTime) genParams.BtcstakingParams.UnbondingFeeSat = unbondingFeeSat if err := genParams.BtcstakingParams.Validate(); err != nil { @@ -436,6 +435,7 @@ func TestnetGenesisParams( genParams.BlockGasLimit = blockGasLimit genParams.VoteExtensionsEnableHeight = voteExtensionEnableHeight + genParams.FinalityParams.MaxActiveFinalityProviders = maxActiveFinalityProviders genParams.FinalityParams.SignedBlocksWindow = signedBlocksWindow genParams.FinalityParams.MinSignedPerWindow = minSignedPerWindow genParams.FinalityParams.FinalitySigTimeout = finalitySigTimeout diff --git a/proto/babylon/btcstaking/v1/params.proto b/proto/babylon/btcstaking/v1/params.proto index 5341a738..8ceb86c7 100644 --- a/proto/babylon/btcstaking/v1/params.proto +++ b/proto/babylon/btcstaking/v1/params.proto @@ -53,10 +53,8 @@ message Params { (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false ]; - // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol - uint32 max_active_finality_providers = 13; // base gas fee for delegation creation - uint64 delegation_creation_base_gas_fee = 14; + uint64 delegation_creation_base_gas_fee = 13; } // StoredParams attach information about the version of stored parameters diff --git a/proto/babylon/finality/v1/params.proto b/proto/babylon/finality/v1/params.proto index 80a7d722..74502aa4 100644 --- a/proto/babylon/finality/v1/params.proto +++ b/proto/babylon/finality/v1/params.proto @@ -11,14 +11,17 @@ option go_package = "github.com/babylonlabs-io/babylon/x/finality/types"; // Params defines the parameters for the module. message Params { option (gogoproto.goproto_stringer) = false; + + // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol + uint32 max_active_finality_providers = 1; // signed_blocks_window defines the size of the sliding window for tracking finality provider liveness - int64 signed_blocks_window = 1; + int64 signed_blocks_window = 2; // finality_sig_timeout defines how much time (in terms of blocks) finality providers have to cast a finality // vote before being judged as missing their voting turn on the given block - int64 finality_sig_timeout = 2; + int64 finality_sig_timeout = 3; // min_signed_per_window defines the minimum number of blocks that a finality provider is required to sign // within the sliding window to avoid being jailed - bytes min_signed_per_window = 3 [ + bytes min_signed_per_window = 4 [ (cosmos_proto.scalar) = "cosmos.Dec", (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false, @@ -26,12 +29,12 @@ message Params { ]; // min_pub_rand is the minimum number of public randomness each // message should commit - uint64 min_pub_rand = 4; + uint64 min_pub_rand = 5; // jail_duration is the minimum period of time that a finality provider remains jailed - google.protobuf.Duration jail_duration = 5 + google.protobuf.Duration jail_duration = 6 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdduration) = true]; // finality_activation_height is the babylon block height which the finality module will // start to accept finality voting and the minimum allowed value for the public randomness // commit start height. - uint64 finality_activation_height = 6; + uint64 finality_activation_height = 7; } diff --git a/test/e2e/configurer/chain/chain.go b/test/e2e/configurer/chain/chain.go index b93aeb0b..76ac1f72 100644 --- a/test/e2e/configurer/chain/chain.go +++ b/test/e2e/configurer/chain/chain.go @@ -190,7 +190,7 @@ func (c *Config) TxGovVoteFromAllNodes(propID int, option govv1.VoteOption, over // BTCHeaderBytesHexJoined join all the btc headers as byte string hex func (c *Config) BTCHeaderBytesHexJoined() string { - if c.BTCHeaders == nil || len(c.BTCHeaders) == 0 { + if len(c.BTCHeaders) == 0 { return "" } diff --git a/test/e2e/configurer/chain/commands.go b/test/e2e/configurer/chain/commands.go index 691487be..16c993a6 100644 --- a/test/e2e/configurer/chain/commands.go +++ b/test/e2e/configurer/chain/commands.go @@ -263,7 +263,7 @@ func (n *NodeConfig) FinalizeSealedEpochs(startEpoch uint64, lastEpoch uint64) { func (n *NodeConfig) StoreWasmCode(wasmFile, from string) { n.LogActionF("storing wasm code from file %s", wasmFile) cmd := []string{"babylond", "tx", "wasm", "store", wasmFile, fmt.Sprintf("--from=%s", from), "--gas=auto", "--gas-adjustment=1.3"} - n.LogActionF(strings.Join(cmd, " ")) + n.LogActionF("Executing command: %s", strings.Join(cmd, " ")) _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully stored") @@ -272,7 +272,7 @@ func (n *NodeConfig) StoreWasmCode(wasmFile, from string) { func (n *NodeConfig) InstantiateWasmContract(codeId, initMsg, from string) { n.LogActionF("instantiating wasm contract %s with %s", codeId, initMsg) cmd := []string{"babylond", "tx", "wasm", "instantiate", codeId, initMsg, fmt.Sprintf("--from=%s", from), "--no-admin", "--label=contract", "--gas=auto", "--gas-adjustment=1.3"} - n.LogActionF(strings.Join(cmd, " ")) + n.LogActionF("Executing command: %s", strings.Join(cmd, " ")) _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully initialized") @@ -281,7 +281,7 @@ func (n *NodeConfig) InstantiateWasmContract(codeId, initMsg, from string) { func (n *NodeConfig) WasmExecute(contract, execMsg, from string) { n.LogActionF("executing %s on wasm contract %s from %s", execMsg, contract, from) cmd := []string{"babylond", "tx", "wasm", "execute", contract, execMsg, fmt.Sprintf("--from=%s", from)} - n.LogActionF(strings.Join(cmd, " ")) + n.LogActionF("Executing command: %s", strings.Join(cmd, " ")) _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully executed") @@ -291,7 +291,7 @@ func (n *NodeConfig) WasmExecute(contract, execMsg, from string) { func (n *NodeConfig) WithdrawReward(sType, from string) { n.LogActionF("withdraw rewards of type %s for tx signer %s", sType, from) cmd := []string{"babylond", "tx", "incentive", "withdraw-reward", sType, fmt.Sprintf("--from=%s", from)} - n.LogActionF(strings.Join(cmd, " ")) + n.LogActionF("Executing command: %s", strings.Join(cmd, " ")) _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully withdrawn") diff --git a/x/btcstaking/keeper/keeper_test.go b/testutil/btcstaking-helper/keeper.go similarity index 84% rename from x/btcstaking/keeper/keeper_test.go rename to testutil/btcstaking-helper/keeper.go index 1c48318c..4e1953c2 100644 --- a/x/btcstaking/keeper/keeper_test.go +++ b/testutil/btcstaking-helper/keeper.go @@ -1,15 +1,19 @@ -package keeper_test +package testutil import ( "math/rand" "testing" "cosmossdk.io/core/header" + "cosmossdk.io/log" sdkmath "cosmossdk.io/math" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + dbm "github.com/cosmos/cosmos-db" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -21,23 +25,29 @@ import ( btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" "github.com/babylonlabs-io/babylon/x/btcstaking/keeper" "github.com/babylonlabs-io/babylon/x/btcstaking/types" + epochingtypes "github.com/babylonlabs-io/babylon/x/epoching/types" + fkeeper "github.com/babylonlabs-io/babylon/x/finality/keeper" + ftypes "github.com/babylonlabs-io/babylon/x/finality/types" ) var ( - net = &chaincfg.SimNetParams - btcTipHeight = uint32(30) + btcTipHeight = uint32(30) + timestampedEpoch = uint64(10) ) type Helper struct { t testing.TB - Ctx sdk.Context - BTCStakingKeeper *keeper.Keeper + Ctx sdk.Context + BTCStakingKeeper *keeper.Keeper + MsgServer types.MsgServer + + FinalityKeeper *fkeeper.Keeper + FMsgServer ftypes.MsgServer + BTCLightClientKeeper *types.MockBTCLightClientKeeper BTCCheckpointKeeper *types.MockBtcCheckpointKeeper - FinalityKeeper *types.MockFinalityKeeper - BTCStakingHooks *types.MockBtcStakingHooks - MsgServer types.MsgServer + CheckpointingKeeper *ftypes.MockCheckpointingKeeper Net *chaincfg.Params } @@ -50,33 +60,54 @@ func NewHelper( t testing.TB, btclcKeeper *types.MockBTCLightClientKeeper, btccKeeper *types.MockBtcCheckpointKeeper, - finalityKeeper *types.MockFinalityKeeper, ) *Helper { ctrl := gomock.NewController(t) // mock refundable messages - iKeeper := types.NewMockIncentiveKeeper(ctrl) + iKeeper := ftypes.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}) + ckptKeeper := ftypes.NewMockCheckpointingKeeper(ctrl) + ckptKeeper.EXPECT().GetLastFinalizedEpoch(gomock.Any()).Return(timestampedEpoch).AnyTimes() + + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) + + k, _ := keepertest.BTCStakingKeeperWithStore(t, db, stateStore, btclcKeeper, btccKeeper, iKeeper) msgSrvr := keeper.NewMsgServerImpl(*k) - mockedHooks := types.NewMockBtcStakingHooks(ctrl) - mockedHooks.EXPECT().AfterFinalityProviderActivated(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - k.SetHooks(mockedHooks) + fk, ctx := keepertest.FinalityKeeperWithStore(t, db, stateStore, k, iKeeper, ckptKeeper) + fMsgSrvr := fkeeper.NewMsgServerImpl(*fk) + + // set all parameters + err := k.SetParams(ctx, types.DefaultParams()) + require.NoError(t, err) + err = fk.SetParams(ctx, ftypes.DefaultParams()) + require.NoError(t, err) + + ctx = ctx.WithHeaderInfo(header.Info{Height: 1}) return &Helper{ - t: t, - Ctx: ctx, - BTCStakingKeeper: k, + t: t, + Ctx: ctx, + + BTCStakingKeeper: k, + MsgServer: msgSrvr, + + FinalityKeeper: fk, + FMsgServer: fMsgSrvr, + BTCLightClientKeeper: btclcKeeper, BTCCheckpointKeeper: btccKeeper, - MsgServer: msgSrvr, + CheckpointingKeeper: ckptKeeper, Net: &chaincfg.SimNetParams, } } +func (h *Helper) T() testing.TB { + return h.t +} + func (h *Helper) NoError(err error) { require.NoError(h.t, err) } @@ -85,6 +116,13 @@ func (h *Helper) Error(err error, msgAndArgs ...any) { require.Error(h.t, err, msgAndArgs...) } +func (h *Helper) BeginBlocker() { + err := h.BTCStakingKeeper.BeginBlocker(h.Ctx) + h.NoError(err) + err = h.FinalityKeeper.BeginBlocker(h.Ctx) + h.NoError(err) +} + func (h *Helper) GenAndApplyParams(r *rand.Rand) ([]*btcec.PrivateKey, []*btcec.PublicKey) { return h.GenAndApplyCustomParams(r, 100, 0) } @@ -115,19 +153,18 @@ func (h *Helper) GenAndApplyCustomParams( slashingPkScript, err := txscript.PayToAddrScript(slashingAddress) h.NoError(err) err = h.BTCStakingKeeper.SetParams(h.Ctx, types.Params{ - CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), - CovenantQuorum: 3, - MinStakingValueSat: 1000, - MaxStakingValueSat: int64(4 * 10e8), - MinStakingTimeBlocks: 10, - MaxStakingTimeBlocks: 10000, - SlashingPkScript: slashingPkScript, - MinSlashingTxFeeSat: 10, - MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.01"), - SlashingRate: sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2), - MaxActiveFinalityProviders: 100, - MinUnbondingTimeBlocks: minUnbondingTime, - UnbondingFeeSat: 1000, + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), + CovenantQuorum: 3, + MinStakingValueSat: 1000, + MaxStakingValueSat: int64(4 * 10e8), + MinStakingTimeBlocks: 10, + MaxStakingTimeBlocks: 10000, + SlashingPkScript: slashingPkScript, + MinSlashingTxFeeSat: 10, + MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.01"), + SlashingRate: sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2), + MinUnbondingTimeBlocks: minUnbondingTime, + UnbondingFeeSat: 1000, }) h.NoError(err) return covenantSKs, covenantPKs @@ -488,3 +525,30 @@ func (h *Helper) AddInclusionProof( status = updatedDel.GetStatus(btcTipHeight, bcParams.CheckpointFinalizationTimeout, bsParams.CovenantQuorum) require.Equal(h.t, status, types.BTCDelegationStatus_ACTIVE, "the BTC delegation shall be active") } + +func (h *Helper) CommitPubRandList( + r *rand.Rand, + fpSK *btcec.PrivateKey, + fp *types.FinalityProvider, + startHeight uint64, + numPubRand uint64, + timestamped bool, +) *datagen.RandListInfo { + randListInfo, msg, err := datagen.GenRandomMsgCommitPubRandList(r, fpSK, startHeight, numPubRand) + h.NoError(err) + + // if timestamped, use the timestamped epoch, otherwise use the next epoch + var epoch uint64 + if timestamped { + epoch = timestampedEpoch + } else { + epoch = timestampedEpoch + 1 + } + + h.CheckpointingKeeper.EXPECT().GetEpoch(gomock.Any()).Return(&epochingtypes.Epoch{EpochNumber: epoch}).Times(1) + + _, err = h.FMsgServer.CommitPubRandList(h.Ctx, msg) + h.NoError(err) + + return randListInfo +} diff --git a/testutil/keeper/btcstaking.go b/testutil/keeper/btcstaking.go index 85f9641b..c38f00ed 100644 --- a/testutil/keeper/btcstaking.go +++ b/testutil/keeper/btcstaking.go @@ -23,17 +23,16 @@ import ( "github.com/babylonlabs-io/babylon/x/btcstaking/types" ) -func BTCStakingKeeper( +func BTCStakingKeeperWithStore( t testing.TB, + db dbm.DB, + stateStore store.CommitMultiStore, btclcKeeper types.BTCLightClientKeeper, btccKeeper types.BtcCheckpointKeeper, - finalityKeeper types.FinalityKeeper, iKeeper types.IncentiveKeeper, ) (*keeper.Keeper, sdk.Context) { storeKey := storetypes.NewKVStoreKey(types.StoreKey) - db := dbm.NewMemDB() - stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) require.NoError(t, stateStore.LoadLatestVersion()) @@ -45,7 +44,6 @@ func BTCStakingKeeper( runtime.NewKVStoreService(storeKey), btclcKeeper, btccKeeper, - finalityKeeper, iKeeper, &chaincfg.SimNetParams, authtypes.NewModuleAddress(govtypes.ModuleName).String(), @@ -54,10 +52,24 @@ func BTCStakingKeeper( ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) ctx = ctx.WithHeaderInfo(header.Info{}) + return &k, ctx +} + +func BTCStakingKeeper( + t testing.TB, + btclcKeeper types.BTCLightClientKeeper, + btccKeeper types.BtcCheckpointKeeper, + iKeeper types.IncentiveKeeper, +) (*keeper.Keeper, sdk.Context) { + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) + + k, ctx := BTCStakingKeeperWithStore(t, db, stateStore, btclcKeeper, btccKeeper, iKeeper) + // Initialize params if err := k.SetParams(ctx, types.DefaultParams()); err != nil { panic(err) } - return &k, ctx + return k, ctx } diff --git a/testutil/keeper/finality.go b/testutil/keeper/finality.go index bfd83ce3..c2b22e5f 100644 --- a/testutil/keeper/finality.go +++ b/testutil/keeper/finality.go @@ -22,11 +22,16 @@ import ( "github.com/babylonlabs-io/babylon/x/finality/types" ) -func FinalityKeeper(t testing.TB, bsKeeper types.BTCStakingKeeper, iKeeper types.IncentiveKeeper, cKeeper types.CheckpointingKeeper) (*keeper.Keeper, sdk.Context) { +func FinalityKeeperWithStore( + t testing.TB, + db dbm.DB, + stateStore store.CommitMultiStore, + bsKeeper types.BTCStakingKeeper, + iKeeper types.IncentiveKeeper, + ckptKeeper types.CheckpointingKeeper, +) (*keeper.Keeper, sdk.Context) { storeKey := storetypes.NewKVStoreKey(types.StoreKey) - db := dbm.NewMemDB() - stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) require.NoError(t, stateStore.LoadLatestVersion()) @@ -38,13 +43,27 @@ func FinalityKeeper(t testing.TB, bsKeeper types.BTCStakingKeeper, iKeeper types runtime.NewKVStoreService(storeKey), bsKeeper, iKeeper, - cKeeper, + ckptKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) ctx = ctx.WithHeaderInfo(header.Info{}) + return &k, ctx +} + +func FinalityKeeper( + t testing.TB, + bsKeeper types.BTCStakingKeeper, + iKeeper types.IncentiveKeeper, + ckptKeeper types.CheckpointingKeeper, +) (*keeper.Keeper, sdk.Context) { + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) + + k, ctx := FinalityKeeperWithStore(t, db, stateStore, bsKeeper, iKeeper, ckptKeeper) + // Initialize params dParams := types.DefaultParams() dParams.FinalityActivationHeight = 0 @@ -52,5 +71,5 @@ func FinalityKeeper(t testing.TB, bsKeeper types.BTCStakingKeeper, iKeeper types panic(err) } - return &k, ctx + return k, ctx } diff --git a/x/btcstaking/genesis_test.go b/x/btcstaking/genesis_test.go index 9a367272..2ab7d793 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, nil) + k, ctx := keepertest.BTCStakingKeeper(t, nil, nil, nil) btcstaking.InitGenesis(ctx, *k, genesisState) got := btcstaking.ExportGenesis(ctx, *k) require.NotNil(t, got) diff --git a/x/btcstaking/keeper/bench_test.go b/x/btcstaking/keeper/bench_test.go index b032f0b2..7fc14f9a 100644 --- a/x/btcstaking/keeper/bench_test.go +++ b/x/btcstaking/keeper/bench_test.go @@ -10,6 +10,7 @@ import ( "github.com/golang/mock/gomock" + testutil "github.com/babylonlabs-io/babylon/testutil/btcstaking-helper" "github.com/babylonlabs-io/babylon/testutil/datagen" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" bsmodule "github.com/babylonlabs-io/babylon/x/btcstaking" @@ -24,8 +25,7 @@ func benchBeginBlock(b *testing.B, numFPs int, numDelsUnderFP int) { defer ctrl.Finish() btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(b, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(b, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) diff --git a/x/btcstaking/keeper/btc_delegations.go b/x/btcstaking/keeper/btc_delegations.go index e5d01e08..a4c5a4ba 100644 --- a/x/btcstaking/keeper/btc_delegations.go +++ b/x/btcstaking/keeper/btc_delegations.go @@ -39,7 +39,7 @@ func (k Keeper) AddBTCDelegation(ctx sdk.Context, btcDel *types.BTCDelegation) e } // index staking tx hash of this BTC delegation if err := btcDelIndex.Add(stakingTxHash); err != nil { - return types.ErrInvalidStakingTx.Wrapf(err.Error()) + return types.ErrInvalidStakingTx.Wrapf("error adding staking tx hash to BTC delegator index: %s", err.Error()) } // save the index k.setBTCDelegatorDelegationIndex(ctx, &fpBTCPK, btcDel.BtcPk, btcDelIndex) diff --git a/x/btcstaking/keeper/btc_height_index_test.go b/x/btcstaking/keeper/btc_height_index_test.go index 8b30b78d..594fb75e 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, nil) + keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, nil, nil) // randomise Babylon height and BTC height babylonHeight := datagen.RandomInt(r, 100) diff --git a/x/btcstaking/keeper/genesis.go b/x/btcstaking/keeper/genesis.go index 70cd1b2c..d33e7309 100644 --- a/x/btcstaking/keeper/genesis.go +++ b/x/btcstaking/keeper/genesis.go @@ -53,7 +53,7 @@ func (k Keeper) InitGenesis(ctx context.Context, gs types.GenesisState) error { } for _, vpCache := range gs.VpDstCache { - k.setVotingPowerDistCache(ctx, vpCache.BlockHeight, vpCache.VpDistribution) + k.SetVotingPowerDistCache(ctx, vpCache.BlockHeight, vpCache.VpDistribution) } return nil diff --git a/x/btcstaking/keeper/grpc_query_test.go b/x/btcstaking/keeper/grpc_query_test.go index ec784e11..66beb91c 100644 --- a/x/btcstaking/keeper/grpc_query_test.go +++ b/x/btcstaking/keeper/grpc_query_test.go @@ -8,6 +8,7 @@ import ( sdkmath "cosmossdk.io/math" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" @@ -23,13 +24,15 @@ import ( "github.com/babylonlabs-io/babylon/x/btcstaking/types" ) +var net = &chaincfg.SimNetParams + func FuzzActivatedHeight(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) 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, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) ctx = sdk.UnwrapSDKContext(ctx) // not activated yet @@ -54,7 +57,7 @@ func FuzzFinalityProviders(f *testing.F) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) ctx = sdk.UnwrapSDKContext(ctx) // Generate random finality providers and add them to kv store @@ -119,7 +122,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, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) ctx = sdk.UnwrapSDKContext(ctx) // Generate random finality providers and add them to kv store @@ -175,7 +178,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, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil) // covenant and slashing addr covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r) @@ -285,7 +288,7 @@ func FuzzFinalityProviderPowerAtHeight(f *testing.F) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) // random finality provider fp, err := datagen.GenRandomFinalityProvider(r) @@ -334,7 +337,7 @@ func FuzzFinalityProviderCurrentVotingPower(f *testing.F) { r := rand.New(rand.NewSource(seed)) // Setup keeper and context - keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) // random finality provider fp, err := datagen.GenRandomFinalityProvider(r) @@ -386,7 +389,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, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil) // covenant and slashing addr covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r) @@ -508,7 +511,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, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper, nil) // covenant and slashing addr covenantSKs, covenantPKs, covenantQuorum := datagen.GenCovenantCommittee(r) diff --git a/x/btcstaking/keeper/hooks.go b/x/btcstaking/keeper/hooks.go deleted file mode 100644 index b4d93cef..00000000 --- a/x/btcstaking/keeper/hooks.go +++ /dev/null @@ -1,25 +0,0 @@ -package keeper - -import ( - "context" - - bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/babylon/x/finality/types" -) - -var _ types.FinalityHooks = Hooks{} - -// Hooks wrapper struct for BTC staking keeper -type Hooks struct { - k Keeper -} - -// Return the finality hooks -func (k Keeper) Hooks() Hooks { - return Hooks{k} -} - -// AfterSluggishFinalityProviderDetected updates the status of the given finality provider to `sluggish` -func (h Hooks) AfterSluggishFinalityProviderDetected(ctx context.Context, fpPk *bbntypes.BIP340PubKey) error { - return h.k.JailFinalityProvider(ctx, fpPk.MustMarshal()) -} diff --git a/x/btcstaking/keeper/incentive.go b/x/btcstaking/keeper/incentive.go index ac65638b..9219b10f 100644 --- a/x/btcstaking/keeper/incentive.go +++ b/x/btcstaking/keeper/incentive.go @@ -9,12 +9,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) setVotingPowerDistCache(ctx context.Context, height uint64, dc *types.VotingPowerDistCache) { +func (k Keeper) SetVotingPowerDistCache(ctx context.Context, height uint64, dc *types.VotingPowerDistCache) { store := k.votingPowerDistCacheStore(ctx) store.Set(sdk.Uint64ToBigEndian(height), k.cdc.MustMarshal(dc)) } -func (k Keeper) getVotingPowerDistCache(ctx context.Context, height uint64) *types.VotingPowerDistCache { +func (k Keeper) GetVotingPowerDistCache(ctx context.Context, height uint64) *types.VotingPowerDistCache { store := k.votingPowerDistCacheStore(ctx) rdcBytes := store.Get(sdk.Uint64ToBigEndian(height)) if len(rdcBytes) == 0 { @@ -25,14 +25,6 @@ func (k Keeper) getVotingPowerDistCache(ctx context.Context, height uint64) *typ return &dc } -func (k Keeper) GetVotingPowerDistCache(ctx context.Context, height uint64) (*types.VotingPowerDistCache, error) { - dc := k.getVotingPowerDistCache(ctx, height) - if dc == nil { - return nil, types.ErrVotingPowerDistCacheNotFound - } - return dc, nil -} - func (k Keeper) RemoveVotingPowerDistCache(ctx context.Context, height uint64) { store := k.votingPowerDistCacheStore(ctx) store.Delete(sdk.Uint64ToBigEndian(height)) diff --git a/x/btcstaking/keeper/incentive_test.go b/x/btcstaking/keeper/incentive_test.go deleted file mode 100644 index 7fa1cc04..00000000 --- a/x/btcstaking/keeper/incentive_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package keeper_test - -import ( - "math/rand" - "testing" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" - "github.com/babylonlabs-io/babylon/x/btcstaking/types" -) - -func FuzzRecordVotingPowerDistCache(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - 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) - changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) - h.NoError(err) - - // generate a random batch of finality providers - numFpsWithVotingPower := datagen.RandomInt(r, 10) + 2 - numFps := numFpsWithVotingPower + datagen.RandomInt(r, 10) - fpsWithVotingPowerMap := map[string]*types.FinalityProvider{} - for i := uint64(0); i < numFps; i++ { - _, _, fp := h.CreateFinalityProvider(r) - if i < numFpsWithVotingPower { - // these finality providers will receive BTC delegations and have voting power - fpsWithVotingPowerMap[fp.Addr] = fp - } - } - - // 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 { - for j := uint64(0); j < numBTCDels; j++ { - delSK, _, err := datagen.GenRandomBTCKeyPair(r) - h.NoError(err) - stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( - r, - delSK, - fp.BtcPk.MustToBTCPK(), - changeAddress.EncodeAddress(), - int64(stakingValue), - 1000, - 0, - 0, - true, - ) - h.NoError(err) - h.CreateCovenantSigs(r, covenantSKs, delMsg, del) - h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) - } - } - - // record voting power distribution cache - babylonHeight := datagen.RandomInt(r, 10) + 1 - h.Ctx = datagen.WithCtxHeight(h.Ctx, babylonHeight) - h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - require.NoError(t, err) - - // assert voting power distribution cache is correct - dc, err := h.BTCStakingKeeper.GetVotingPowerDistCache(h.Ctx, babylonHeight) - require.NoError(t, err) - require.NotNil(t, dc) - require.Equal(t, dc.TotalBondedSat, numFpsWithVotingPower*numBTCDels*stakingValue) - activeFPs := dc.GetActiveFinalityProviderSet() - for _, fpDistInfo := range activeFPs { - require.Equal(t, fpDistInfo.TotalBondedSat, numBTCDels*stakingValue) - fp, ok := fpsWithVotingPowerMap[fpDistInfo.Addr] - require.True(t, ok) - require.Equal(t, fpDistInfo.Commission, fp.Commission) - require.Len(t, fpDistInfo.BtcDels, int(numBTCDels)) - for _, delDistInfo := range fpDistInfo.BtcDels { - require.Equal(t, delDistInfo.TotalSat, stakingValue) - } - } - }) -} diff --git a/x/btcstaking/keeper/keeper.go b/x/btcstaking/keeper/keeper.go index e9e3dbaa..872d3bd1 100644 --- a/x/btcstaking/keeper/keeper.go +++ b/x/btcstaking/keeper/keeper.go @@ -19,12 +19,9 @@ type ( cdc codec.BinaryCodec storeService corestoretypes.KVStoreService - btclcKeeper types.BTCLightClientKeeper - btccKeeper types.BtcCheckpointKeeper - FinalityKeeper types.FinalityKeeper - iKeeper types.IncentiveKeeper - - hooks types.BtcStakingHooks + btclcKeeper types.BTCLightClientKeeper + btccKeeper types.BtcCheckpointKeeper + iKeeper types.IncentiveKeeper btcNet *chaincfg.Params // the address capable of executing a MsgUpdateParams message. Typically, this @@ -39,7 +36,6 @@ func NewKeeper( btclcKeeper types.BTCLightClientKeeper, btccKeeper types.BtcCheckpointKeeper, - finalityKeeper types.FinalityKeeper, iKeeper types.IncentiveKeeper, btcNet *chaincfg.Params, @@ -49,29 +45,15 @@ func NewKeeper( cdc: cdc, storeService: storeService, - btclcKeeper: btclcKeeper, - btccKeeper: btccKeeper, - FinalityKeeper: finalityKeeper, - iKeeper: iKeeper, - - hooks: nil, + btclcKeeper: btclcKeeper, + btccKeeper: btccKeeper, + iKeeper: iKeeper, btcNet: btcNet, authority: authority, } } -// SetHooks sets the BTC staking hooks -func (k *Keeper) SetHooks(sh types.BtcStakingHooks) *Keeper { - if k.hooks != nil { - panic("cannot set BTC staking hooks twice") - } - - k.hooks = sh - - return k -} - func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } @@ -84,8 +66,6 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) BeginBlocker(ctx context.Context) error { // index BTC height at the current height k.IndexBTCHeight(ctx) - // update voting power distribution - k.UpdatePowerDist(ctx) return nil } diff --git a/x/btcstaking/keeper/msg_server_test.go b/x/btcstaking/keeper/msg_server_test.go index 39d58dc2..29fbea56 100644 --- a/x/btcstaking/keeper/msg_server_test.go +++ b/x/btcstaking/keeper/msg_server_test.go @@ -19,11 +19,11 @@ import ( "google.golang.org/grpc/status" asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" + testutil "github.com/babylonlabs-io/babylon/testutil/btcstaking-helper" "github.com/babylonlabs-io/babylon/testutil/datagen" testhelper "github.com/babylonlabs-io/babylon/testutil/helper" bbn "github.com/babylonlabs-io/babylon/types" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" - "github.com/babylonlabs-io/babylon/x/btcstaking/keeper" "github.com/babylonlabs-io/babylon/x/btcstaking/types" ) @@ -38,8 +38,7 @@ func FuzzMsgCreateFinalityProvider(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters h.GenAndApplyParams(r) @@ -87,17 +86,20 @@ func FuzzMsgEditFinalityProvider(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - h := testhelper.NewHelper(t) - bsKeeper := h.App.BTCStakingKeeper - msgSrvr := keeper.NewMsgServerImpl(bsKeeper) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) + + h.GenAndApplyParams(r) - // generate new finality provider - fp, err := datagen.GenRandomFinalityProvider(r) - require.NoError(t, err) // insert the finality provider - h.AddFinalityProvider(fp) + _, _, fp := h.CreateFinalityProvider(r) // assert the finality providers exist in KVStore - require.True(t, bsKeeper.HasFinalityProvider(h.Ctx, *fp.BtcPk)) + require.True(t, h.BTCStakingKeeper.HasFinalityProvider(h.Ctx, *fp.BtcPk)) // updated commission and description newCommission := datagen.GenRandomCommission(r) @@ -110,9 +112,9 @@ func FuzzMsgEditFinalityProvider(f *testing.F) { Description: newDescription, Commission: &newCommission, } - _, err = msgSrvr.EditFinalityProvider(h.Ctx, msg) + _, err := h.MsgServer.EditFinalityProvider(h.Ctx, msg) h.NoError(err) - editedFp, err := bsKeeper.GetFinalityProvider(h.Ctx, *fp.BtcPk) + editedFp, err := h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, *fp.BtcPk) h.NoError(err) require.Equal(t, newCommission, *editedFp.Commission) require.Equal(t, newDescription, editedFp.Description) @@ -127,10 +129,10 @@ func FuzzMsgEditFinalityProvider(f *testing.F) { Description: newDescription, Commission: &newCommission, } - _, err = msgSrvr.EditFinalityProvider(h.Ctx, msg) - h.EqualError(err, status.Errorf(codes.PermissionDenied, "the signer does not correspond to the finality provider's Babylon address")) + _, err = h.MsgServer.EditFinalityProvider(h.Ctx, msg) + require.Equal(h.T(), err, status.Errorf(codes.PermissionDenied, "the signer does not correspond to the finality provider's Babylon address")) errStatus := status.Convert(err) - require.Equal(t, codes.PermissionDenied, errStatus.Code()) + require.Equal(h.T(), codes.PermissionDenied, errStatus.Code()) }) } @@ -145,8 +147,7 @@ func FuzzCreateBTCDelegation(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters h.GenAndApplyParams(r) @@ -179,22 +180,22 @@ func FuzzCreateBTCDelegation(f *testing.F) { // ensure consistency between the msg and the BTC delegation in DB actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) h.NoError(err) - require.Equal(h.t, msgCreateBTCDel.StakerAddr, actualDel.StakerAddr) - require.Equal(h.t, msgCreateBTCDel.Pop, actualDel.Pop) - require.Equal(h.t, msgCreateBTCDel.StakingTx, actualDel.StakingTx) - require.Equal(h.t, msgCreateBTCDel.SlashingTx, actualDel.SlashingTx) + require.Equal(h.T(), msgCreateBTCDel.StakerAddr, actualDel.StakerAddr) + require.Equal(h.T(), msgCreateBTCDel.Pop, actualDel.Pop) + require.Equal(h.T(), msgCreateBTCDel.StakingTx, actualDel.StakingTx) + require.Equal(h.T(), msgCreateBTCDel.SlashingTx, actualDel.SlashingTx) // ensure the BTC delegation in DB is correctly formatted err = actualDel.ValidateBasic() h.NoError(err) // delegation is not activated by covenant yet - require.False(h.t, actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + require.False(h.T(), actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) if usePreApproval { - require.Zero(h.t, actualDel.StartHeight) - require.Zero(h.t, actualDel.EndHeight) + require.Zero(h.T(), actualDel.StartHeight) + require.Zero(h.T(), actualDel.EndHeight) } else { - require.Positive(h.t, actualDel.StartHeight) - require.Positive(h.t, actualDel.EndHeight) + require.Positive(h.T(), actualDel.StartHeight) + require.Positive(h.T(), actualDel.EndHeight) } }) } @@ -207,8 +208,7 @@ func TestProperVersionInDelegation(t *testing.T) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters h.GenAndApplyParams(r) @@ -284,8 +284,7 @@ func FuzzAddCovenantSigs(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -319,7 +318,7 @@ func FuzzAddCovenantSigs(f *testing.F) { actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) h.NoError(err) // delegation is not activated by covenant yet - require.False(h.t, actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + require.False(h.T(), actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) msgs := h.GenerateCovenantSignaturesMessages(r, covenantSKs, msgCreateBTCDel, actualDel) @@ -340,8 +339,8 @@ func FuzzAddCovenantSigs(f *testing.F) { // ensure the BTC delegation now has voting power actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) h.NoError(err) - require.True(h.t, actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) - require.True(h.t, actualDel.BtcUndelegation.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + require.True(h.T(), actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + require.True(h.T(), actualDel.BtcUndelegation.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) tipHeight := h.BTCLightClientKeeper.GetTipInfo(h.Ctx).Height checkpointTimeout := h.BTCCheckpointKeeper.GetParams(h.Ctx).CheckpointFinalizationTimeout @@ -370,8 +369,7 @@ func FuzzAddBTCDelegationInclusionProof(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -439,8 +437,7 @@ func FuzzBTCUndelegate(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -519,8 +516,7 @@ func FuzzSelectiveSlashing(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -596,8 +592,7 @@ func FuzzSelectiveSlashing_StakingTx(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -682,8 +677,7 @@ func TestDoNotAllowDelegationWithoutFinalityProvider(t *testing.T) { btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set covenant PK to params _, covenantPKs := h.GenAndApplyParams(r) @@ -850,8 +844,7 @@ func TestCorrectUnbondingTimeInDelegation(t *testing.T) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters _, _ = h.GenAndApplyCustomParams(r, tt.finalizationTimeout, tt.minUnbondingTime) @@ -977,7 +970,7 @@ func FuzzDeterminismBtcstakingBeginBlocker(f *testing.F) { // Default params are the same in both apps covQuorum := h.App.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum - maxFinalityProviders := int32(h.App.BTCStakingKeeper.GetParams(h.Ctx).MaxActiveFinalityProviders) + maxFinalityProviders := int32(h.App.FinalityKeeper.GetParams(h.Ctx).MaxActiveFinalityProviders) // Number of finality providers from 10 to maxFinalityProviders + 10 numFinalityProviders := int(r.Int31n(maxFinalityProviders) + 10) diff --git a/x/btcstaking/keeper/params_test.go b/x/btcstaking/keeper/params_test.go index 147bd401..81f4d53a 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, nil) + k, ctx := testkeeper.BTCStakingKeeper(t, 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, nil) + k, ctx := testkeeper.BTCStakingKeeper(t, 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, nil) + k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) numVersionsToGenerate := r.Intn(100) + 1 params0 := k.GetParams(ctx) var generatedParams []*types.Params diff --git a/x/btcstaking/keeper/power_dist_change.go b/x/btcstaking/keeper/power_dist_change.go index 3c2c2875..b0277941 100644 --- a/x/btcstaking/keeper/power_dist_change.go +++ b/x/btcstaking/keeper/power_dist_change.go @@ -2,317 +2,13 @@ package keeper import ( "context" - "fmt" - "sort" "cosmossdk.io/store/prefix" - "github.com/btcsuite/btcd/btcutil" + "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" - - bbn "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/babylon/x/btcstaking/types" ) -/* power distribution update */ - -// UpdatePowerDist updates the voting power table and distribution cache. -// This is triggered upon each `BeginBlock` -func (k Keeper) UpdatePowerDist(ctx context.Context) { - sdkCtx := sdk.UnwrapSDKContext(ctx) - height := uint64(sdkCtx.HeaderInfo().Height) - btcTipHeight := k.GetCurrentBTCHeight(ctx) - - // get the power dist cache in the last height - dc := k.getVotingPowerDistCache(ctx, height-1) - if dc == nil { - // no BTC staker at the prior height - dc = types.NewVotingPowerDistCache() - } - - // get all power distribution update events during the previous tip - // and the current tip - lastBTCTipHeight := k.GetBTCHeightAtBabylonHeight(ctx, height-1) - events := k.GetAllPowerDistUpdateEvents(ctx, lastBTCTipHeight, btcTipHeight) - - // clear all events that have been consumed in this function - defer func() { - for i := lastBTCTipHeight; i <= btcTipHeight; i++ { - k.clearPowerDistUpdateEvents(ctx, i) - } - }() - - // reconcile old voting power distribution cache and new events - // to construct the new distribution - k.Logger(sdkCtx).With( - "eventsLen", len(events), - ).Info("processing voting power update events") - newDc := k.ProcessAllPowerDistUpdateEvents(ctx, dc, events) - - // record voting power and cache for this height - k.recordVotingPowerAndCache(ctx, newDc) - // emit events for finality providers with state updates - k.handleFPStateUpdates(ctx, dc, newDc) - // record metrics - k.recordMetrics(newDc) -} - -// recordVotingPowerAndCache assigns voting power to each active finality provider -// with the following consideration: -// 1. the fp must have timestamped pub rand -// 2. the fp must in the top x ranked by the voting power (x is given by maxActiveFps) -func (k Keeper) recordVotingPowerAndCache(ctx context.Context, newDc *types.VotingPowerDistCache) { - if newDc == nil { - panic("the voting power distribution cache cannot be nil") - } - - babylonTipHeight := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) - - // label fps with whether it has timestamped pub rand so that these fps - // will not be assigned voting power - for _, fpDistInfo := range newDc.FinalityProviders { - // TODO calling HasTimestampedPubRand potentially iterates - // all the pub rand committed by the fpDistInfo, which might slow down - // the process, need optimization - fpDistInfo.IsTimestamped = k.FinalityKeeper.HasTimestampedPubRand(ctx, fpDistInfo.BtcPk, babylonTipHeight) - } - - // apply the finality provider voting power dist info to the new cache - // after which the cache would have active fps that are top N fps ranked - // by voting power with timestamped pub rand - maxActiveFps := k.GetParams(ctx).MaxActiveFinalityProviders - newDc.ApplyActiveFinalityProviders(maxActiveFps) - - // set voting power table for each active finality providers at this height - for i := uint32(0); i < newDc.NumActiveFps; i++ { - fp := newDc.FinalityProviders[i] - k.SetVotingPower(ctx, fp.BtcPk.MustMarshal(), babylonTipHeight, fp.TotalBondedSat) - } - - // set the voting power distribution cache of the current height - k.setVotingPowerDistCache(ctx, babylonTipHeight, newDc) -} - -// handleFPStateUpdates emits events and triggers hooks for finality providers with state updates -func (k Keeper) handleFPStateUpdates(ctx context.Context, prevDc, newDc *types.VotingPowerDistCache) { - sdkCtx := sdk.UnwrapSDKContext(ctx) - - newlyActiveFPs := newDc.FindNewActiveFinalityProviders(prevDc) - for _, fp := range newlyActiveFPs { - if err := k.hooks.AfterFinalityProviderActivated(ctx, fp.BtcPk); err != nil { - panic(fmt.Errorf("failed to execute after finality provider %s activated", fp.BtcPk.MarshalHex())) - } - - statusChangeEvent := types.NewFinalityProviderStatusChangeEvent(fp.BtcPk, types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_ACTIVE) - if err := sdkCtx.EventManager().EmitTypedEvent(statusChangeEvent); err != nil { - panic(fmt.Errorf( - "failed to emit FinalityProviderStatusChangeEvent with status %s: %w", - types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_ACTIVE.String(), err)) - } - - k.Logger(sdkCtx).Info("a new finality provider becomes active", "pk", fp.BtcPk.MarshalHex()) - } - - newlyInactiveFPs := newDc.FindNewInactiveFinalityProviders(prevDc) - for _, fp := range newlyInactiveFPs { - statusChangeEvent := types.NewFinalityProviderStatusChangeEvent(fp.BtcPk, types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_INACTIVE) - if err := sdkCtx.EventManager().EmitTypedEvent(statusChangeEvent); err != nil { - panic(fmt.Errorf( - "failed to emit FinalityProviderStatusChangeEvent with status %s: %w", - types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_INACTIVE.String(), err)) - } - - k.Logger(sdkCtx).Info("a new finality provider becomes inactive", "pk", fp.BtcPk.MarshalHex()) - } -} - -func (k Keeper) recordMetrics(dc *types.VotingPowerDistCache) { - // number of active FPs - numActiveFPs := int(dc.NumActiveFps) - types.RecordActiveFinalityProviders(numActiveFPs) - // number of inactive FPs - numInactiveFPs := len(dc.FinalityProviders) - numActiveFPs - types.RecordInactiveFinalityProviders(numInactiveFPs) - // staked Satoshi - stakedSats := btcutil.Amount(0) - for _, fp := range dc.FinalityProviders { - stakedSats += btcutil.Amount(fp.TotalBondedSat) - } - numStakedBTCs := stakedSats.ToBTC() - types.RecordMetricsKeyStakedBitcoins(float32(numStakedBTCs)) - // TODO: record number of BTC delegations under different status -} - -// ProcessAllPowerDistUpdateEvents processes all events that affect -// voting power distribution and returns a new distribution cache. -// The following events will affect the voting power distribution: -// - newly active BTC delegations -// - newly unbonded BTC delegations -// - slashed finality providers -// - newly jailed finality providers -// - newly unjailed finality providers -func (k Keeper) ProcessAllPowerDistUpdateEvents( - ctx context.Context, - dc *types.VotingPowerDistCache, - events []*types.EventPowerDistUpdate, -) *types.VotingPowerDistCache { - // a map where key is finality provider's BTC PK hex and value is a list - // of BTC delegations that newly become active under this provider - activeBTCDels := map[string][]*types.BTCDelegation{} - // a map where key is unbonded BTC delegation's staking tx hash - unbondedBTCDels := map[string]struct{}{} - // a map where key is slashed finality providers' BTC PK - slashedFPs := map[string]struct{}{} - // a map where key is jailed finality providers' BTC PK - jailedFPs := map[string]struct{}{} - // a map where key is unjailed finality providers' BTC PK - unjailedFPs := map[string]struct{}{} - - /* - filter and classify all events into new/expired BTC delegations and jailed/slashed FPs - */ - sdkCtx := sdk.UnwrapSDKContext(ctx) - for _, event := range events { - switch typedEvent := event.Ev.(type) { - case *types.EventPowerDistUpdate_BtcDelStateUpdate: - delEvent := typedEvent.BtcDelStateUpdate - btcDel, err := k.GetBTCDelegation(ctx, delEvent.StakingTxHash) - if err != nil { - panic(err) // only programming error - } - if delEvent.NewState == types.BTCDelegationStatus_ACTIVE { - // newly active BTC delegation - // add the BTC delegation to each restaked finality provider - for _, fpBTCPK := range btcDel.FpBtcPkList { - fpBTCPKHex := fpBTCPK.MarshalHex() - activeBTCDels[fpBTCPKHex] = append(activeBTCDels[fpBTCPKHex], btcDel) - } - } else if delEvent.NewState == types.BTCDelegationStatus_UNBONDED { - // emit expired event if it is not early unbonding - if !btcDel.IsUnbondedEarly() { - types.EmitExpiredDelegationEvent(sdkCtx, delEvent.StakingTxHash) - } - // add the unbonded BTC delegation to the map - unbondedBTCDels[delEvent.StakingTxHash] = struct{}{} - } - case *types.EventPowerDistUpdate_SlashedFp: - // record slashed fps - types.EmitSlashedFPEvent(sdkCtx, typedEvent.SlashedFp.Pk) - slashedFPs[typedEvent.SlashedFp.Pk.MarshalHex()] = struct{}{} - case *types.EventPowerDistUpdate_JailedFp: - // record jailed fps - types.EmitJailedFPEvent(sdkCtx, typedEvent.JailedFp.Pk) - jailedFPs[typedEvent.JailedFp.Pk.MarshalHex()] = struct{}{} - case *types.EventPowerDistUpdate_UnjailedFp: - // record unjailed fps - unjailedFPs[typedEvent.UnjailedFp.Pk.MarshalHex()] = struct{}{} - } - } - - /* - At this point, there is voting power update. - Then, construct a voting power dist cache by reconciling the previous - cache and all the new events. - */ - // TODO: the algorithm needs to iterate over all BTC delegations so remains - // sub-optimal. Ideally we only need to iterate over all events above rather - // than the entire cache. This is made difficulty since BTC delegations are - // not keyed in the cache. Need to find a way to optimise this. - newDc := types.NewVotingPowerDistCache() - - // iterate over all finality providers and apply all events - for i := range dc.FinalityProviders { - // create a copy of the finality provider - fp := *dc.FinalityProviders[i] - fp.TotalBondedSat = 0 - fp.BtcDels = []*types.BTCDelDistInfo{} - - fpBTCPKHex := fp.BtcPk.MarshalHex() - - // if this finality provider is slashed, continue to avoid - // assigning delegation to it - if _, ok := slashedFPs[fpBTCPKHex]; ok { - fp.IsSlashed = true - continue - } - - // set IsJailed to be true if the fp is jailed - // Note that jailed fp can still accept delegations - // but won't be assigned with voting power - if _, ok := jailedFPs[fpBTCPKHex]; ok { - fp.IsJailed = true - } - - // set IsJailed to be false if the fp is unjailed - if _, ok := unjailedFPs[fpBTCPKHex]; ok { - fp.IsJailed = false - } - - // add all BTC delegations that are not unbonded to the new finality provider - for j := range dc.FinalityProviders[i].BtcDels { - btcDel := *dc.FinalityProviders[i].BtcDels[j] - if _, ok := unbondedBTCDels[btcDel.StakingTxHash]; !ok { - fp.AddBTCDelDistInfo(&btcDel) - } - } - - // process all new BTC delegations under this finality provider - if fpActiveBTCDels, ok := activeBTCDels[fpBTCPKHex]; ok { - // handle new BTC delegations for this finality provider - for _, d := range fpActiveBTCDels { - fp.AddBTCDel(d) - } - // remove the finality provider entry in activeBTCDels map, so that - // after the for loop the rest entries in activeBTCDels belongs to new - // finality providers with new BTC delegations - delete(activeBTCDels, fpBTCPKHex) - } - - // add this finality provider to the new cache if it has voting power - if fp.TotalBondedSat > 0 { - newDc.AddFinalityProviderDistInfo(&fp) - } - } - - /* - process new BTC delegations under new finality providers in activeBTCDels - */ - // sort new finality providers in activeBTCDels to ensure determinism - fpBTCPKHexList := make([]string, 0, len(activeBTCDels)) - for fpBTCPKHex := range activeBTCDels { - fpBTCPKHexList = append(fpBTCPKHexList, fpBTCPKHex) - } - sort.SliceStable(fpBTCPKHexList, func(i, j int) bool { - return fpBTCPKHexList[i] < fpBTCPKHexList[j] - }) - // for each new finality provider, apply the new BTC delegations to the new dist cache - for _, fpBTCPKHex := range fpBTCPKHexList { - // get the finality provider and initialise its dist info - fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(fpBTCPKHex) - if err != nil { - panic(err) // only programming error - } - newFP, err := k.GetFinalityProvider(ctx, *fpBTCPK) - if err != nil { - panic(err) // only programming error - } - fpDistInfo := types.NewFinalityProviderDistInfo(newFP) - - // add each BTC delegation - fpActiveBTCDels := activeBTCDels[fpBTCPKHex] - for _, d := range fpActiveBTCDels { - fpDistInfo.AddBTCDel(d) - } - - // add this finality provider to the new cache if it has voting power - if fpDistInfo.TotalBondedSat > 0 { - newDc.AddFinalityProviderDistInfo(fpDistInfo) - } - } - - return newDc -} - /* voting power distribution update event store */ // addPowerDistUpdateEvent appends an event that affect voting power distribution @@ -337,11 +33,11 @@ func (k Keeper) addPowerDistUpdateEvent( store.Set(sdk.Uint64ToBigEndian(eventIdx), k.cdc.MustMarshal(event)) } -// clearPowerDistUpdateEvents removes all BTC delegation state update events +// ClearPowerDistUpdateEvents removes all BTC delegation state update events // at a given BTC height // This is called after processing all BTC delegation events in `BeginBlocker` // nolint:unused -func (k Keeper) clearPowerDistUpdateEvents(ctx context.Context, btcHeight uint32) { +func (k Keeper) ClearPowerDistUpdateEvents(ctx context.Context, btcHeight uint32) { store := k.powerDistUpdateEventBtcHeightStore(ctx, btcHeight) keys := [][]byte{} diff --git a/x/btcstaking/keeper/query_params_test.go b/x/btcstaking/keeper/query_params_test.go index 2b91a9dd..410a856f 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, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, 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, nil) + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil, nil) // starting with `1` as BTCStakingKeeper creates params with version 0 params1 := types.DefaultParams() diff --git a/x/btcstaking/keeper/voting_power_table_test.go b/x/btcstaking/keeper/voting_power_table_test.go index a23853fa..914c7ce8 100644 --- a/x/btcstaking/keeper/voting_power_table_test.go +++ b/x/btcstaking/keeper/voting_power_table_test.go @@ -2,12 +2,12 @@ package keeper_test import ( "math/rand" - "sort" "testing" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + testutil "github.com/babylonlabs-io/babylon/testutil/btcstaking-helper" "github.com/babylonlabs-io/babylon/testutil/datagen" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" "github.com/babylonlabs-io/babylon/x/btcstaking/types" @@ -24,21 +24,20 @@ func FuzzVotingPowerTable(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) h.NoError(err) - // generate a random batch of finality providers + // generate a random batch of finality providers, and commit pub rand list with timestamp fps := []*types.FinalityProvider{} numFpsWithVotingPower := datagen.RandomInt(r, 10) + 2 numFps := numFpsWithVotingPower + datagen.RandomInt(r, 10) for i := uint64(0); i < numFps; i++ { - _, _, fp := h.CreateFinalityProvider(r) + fpSK, _, fp := h.CreateFinalityProvider(r) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) fps = append(fps, fp) } @@ -74,6 +73,8 @@ func FuzzVotingPowerTable(f *testing.F) { h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) require.NoError(t, err) + err = h.FinalityKeeper.BeginBlocker(h.Ctx) + require.NoError(t, err) for i := uint64(0); i < numFpsWithVotingPower; i++ { power := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fps[i].BtcPk, babylonHeight) @@ -113,6 +114,8 @@ func FuzzVotingPowerTable(f *testing.F) { // index height and record power table err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) require.NoError(t, err) + err = h.FinalityKeeper.BeginBlocker(h.Ctx) + require.NoError(t, err) // check if the slashed finality provider's voting power becomes zero for i := uint64(0); i < numFpsWithVotingPower; i++ { @@ -160,286 +163,3 @@ func FuzzVotingPowerTable(f *testing.F) { require.Equal(t, activatedHeight, activatedHeight2) }) } - -func FuzzVotingPowerTable_ActiveFinalityProviders(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) - - // set all parameters - covenantSKs, _ := h.GenAndApplyParams(r) - changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) - h.NoError(err) - - // generate a random batch of finality providers, each with a BTC delegation with random power - fpsWithMeta := []*types.FinalityProviderDistInfo{} - numFps := datagen.RandomInt(r, 300) + 1 - noTimestampedFps := map[string]bool{} - for i := uint64(0); i < numFps; i++ { - // generate finality provider - _, _, fp := h.CreateFinalityProvider(r) - - // delegate to this finality provider - stakingValue := datagen.RandomInt(r, 100000) + 100000 - delSK, _, err := datagen.GenRandomBTCKeyPair(r) - h.NoError(err) - stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( - r, - delSK, - fp.BtcPk.MustToBTCPK(), - changeAddress.EncodeAddress(), - int64(stakingValue), - 1000, - 0, - 0, - true, - ) - h.NoError(err) - h.CreateCovenantSigs(r, covenantSKs, delMsg, del) - h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) - - // 30 percent not have timestamped randomness, which causes - // zero voting power in the table - fpDistInfo := &types.FinalityProviderDistInfo{BtcPk: fp.BtcPk, TotalBondedSat: stakingValue} - if r.Intn(10) <= 2 { - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), fp.BtcPk, gomock.Any()).Return(false).AnyTimes() - noTimestampedFps[fp.BtcPk.MarshalHex()] = true - fpDistInfo.IsTimestamped = false - } else { - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), fp.BtcPk, gomock.Any()).Return(true).AnyTimes() - fpDistInfo.IsTimestamped = true - } - - // record voting power - fpsWithMeta = append(fpsWithMeta, fpDistInfo) - } - - maxActiveFpsParam := h.BTCStakingKeeper.GetParams(h.Ctx).MaxActiveFinalityProviders - // get a map of expected active finality providers - types.SortFinalityProvidersWithZeroedVotingPower(fpsWithMeta) - expectedActiveFps := fpsWithMeta[:min(uint32(len(fpsWithMeta)-len(noTimestampedFps)), maxActiveFpsParam)] - expectedActiveFpsMap := map[string]uint64{} - for _, fp := range expectedActiveFps { - expectedActiveFpsMap[fp.BtcPk.MarshalHex()] = fp.TotalBondedSat - } - - // record voting power table - babylonHeight := datagen.RandomInt(r, 10) + 1 - h.SetCtxHeight(babylonHeight) - h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - require.NoError(t, err) - - // only finality providers in expectedActiveFpsMap have voting power - for _, fp := range fpsWithMeta { - power := h.BTCStakingKeeper.GetVotingPower(h.Ctx, fp.BtcPk.MustMarshal(), babylonHeight) - if expectedPower, ok := expectedActiveFpsMap[fp.BtcPk.MarshalHex()]; ok { - require.Equal(t, expectedPower, power) - } else { - require.Zero(t, power) - } - } - - // also, get voting power table and assert there is - // min(len(expectedActiveFps), MaxActiveFinalityProviders) active finality providers - powerTable := h.BTCStakingKeeper.GetVotingPowerTable(h.Ctx, babylonHeight) - expectedNumActiveFps := len(expectedActiveFpsMap) - if expectedNumActiveFps > int(maxActiveFpsParam) { - expectedNumActiveFps = int(maxActiveFpsParam) - } - require.Len(t, powerTable, expectedNumActiveFps) - // assert consistency of voting power - for pkHex, expectedPower := range expectedActiveFpsMap { - require.Equal(t, powerTable[pkHex], expectedPower) - } - }) -} - -func FuzzVotingPowerTable_ActiveFinalityProviderRotation(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // mock BTC light client and BTC checkpoint modules - btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) - btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - 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) - // set random number of max number of finality providers - // in order to cover cases that number of finality providers is more or - // less than `MaxActiveFinalityProviders` - bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) - bsParams.MaxActiveFinalityProviders = uint32(datagen.RandomInt(r, 20) + 10) - err := h.BTCStakingKeeper.SetParams(h.Ctx, bsParams) - h.NoError(err) - // change address - changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) - h.NoError(err) - - numFps := datagen.RandomInt(r, 20) + 10 - numActiveFPs := int(min(numFps, uint64(bsParams.MaxActiveFinalityProviders))) - - /* - Generate a random batch of finality providers, each with a BTC delegation - with random voting power. - Then, assert voting power table - */ - fpsWithMeta := []*types.FinalityProviderWithMeta{} - for i := uint64(0); i < numFps; i++ { - // generate finality provider - // generate and insert new finality provider - _, fpPK, fp := h.CreateFinalityProvider(r) - - // create BTC delegation and add covenant signatures to activate it - stakingValue := datagen.RandomInt(r, 100000) + 100000 - delSK, _, err := datagen.GenRandomBTCKeyPair(r) - h.NoError(err) - stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( - r, - delSK, - fpPK, - changeAddress.EncodeAddress(), - int64(stakingValue), - 1000, - 0, - 0, - true, - ) - h.NoError(err) - h.CreateCovenantSigs(r, covenantSKs, delMsg, del) - h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) - - // record voting power - fpsWithMeta = append(fpsWithMeta, &types.FinalityProviderWithMeta{ - BtcPk: fp.BtcPk, - VotingPower: stakingValue, - }) - } - - // record voting power table - babylonHeight := datagen.RandomInt(r, 10) + 1 - h.Ctx = datagen.WithCtxHeight(h.Ctx, babylonHeight) - h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) - - // assert that only top `min(MaxActiveFinalityProviders, numFPs)` finality providers have voting power - sort.SliceStable(fpsWithMeta, func(i, j int) bool { - return fpsWithMeta[i].VotingPower > fpsWithMeta[j].VotingPower - }) - for i := 0; i < numActiveFPs; i++ { - votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) - require.Equal(t, fpsWithMeta[i].VotingPower, votingPower) - } - for i := numActiveFPs; i < int(numFps); i++ { - votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) - require.Zero(t, votingPower) - } - - /* - Delegate more tokens to some existing finality providers - , and create some new finality providers - Then assert voting power table again - */ - // delegate more tokens to some existing finality providers - for i := uint64(0); i < numFps; i++ { - if !datagen.OneInN(r, 2) { - continue - } - - stakingValue := datagen.RandomInt(r, 100000) + 100000 - fpBTCPK := fpsWithMeta[i].BtcPk - delSK, _, err := datagen.GenRandomBTCKeyPair(r) - h.NoError(err) - stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( - r, - delSK, - fpBTCPK.MustToBTCPK(), - changeAddress.EncodeAddress(), - int64(stakingValue), - 1000, - 0, - 0, - true, - ) - h.NoError(err) - h.CreateCovenantSigs(r, covenantSKs, delMsg, del) - h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) - - // accumulate voting power for this finality provider - fpsWithMeta[i].VotingPower += stakingValue - - break - } - // create more finality providers - numNewFps := datagen.RandomInt(r, 20) + 10 - numFps += numNewFps - numActiveFPs = int(min(numFps, uint64(bsParams.MaxActiveFinalityProviders))) - for i := uint64(0); i < numNewFps; i++ { - // generate finality provider - // generate and insert new finality provider - _, fpPK, fp := h.CreateFinalityProvider(r) - - // create BTC delegation and add covenant signatures to activate it - stakingValue := datagen.RandomInt(r, 100000) + 100000 - delSK, _, err := datagen.GenRandomBTCKeyPair(r) - h.NoError(err) - stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( - r, - delSK, - fpPK, - changeAddress.EncodeAddress(), - int64(stakingValue), - 1000, - 0, - 0, - true, - ) - h.NoError(err) - h.CreateCovenantSigs(r, covenantSKs, delMsg, del) - h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) - - // record voting power - fpsWithMeta = append(fpsWithMeta, &types.FinalityProviderWithMeta{ - BtcPk: fp.BtcPk, - VotingPower: stakingValue, - }) - } - - // record voting power table - babylonHeight += 1 - h.Ctx = datagen.WithCtxHeight(h.Ctx, babylonHeight) - h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) - - // again, assert that only top `min(MaxActiveFinalityProviders, numFPs)` finality providers have voting power - sort.SliceStable(fpsWithMeta, func(i, j int) bool { - return fpsWithMeta[i].VotingPower > fpsWithMeta[j].VotingPower - }) - for i := 0; i < numActiveFPs; i++ { - votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) - require.Equal(t, fpsWithMeta[i].VotingPower, votingPower) - } - for i := numActiveFPs; i < int(numFps); i++ { - votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) - require.Zero(t, votingPower) - } - }) -} diff --git a/x/btcstaking/types/expected_keepers.go b/x/btcstaking/types/expected_keepers.go index 72b75cfe..5c805600 100644 --- a/x/btcstaking/types/expected_keepers.go +++ b/x/btcstaking/types/expected_keepers.go @@ -23,10 +23,6 @@ type FinalityKeeper interface { HasTimestampedPubRand(ctx context.Context, fpBtcPK *bbn.BIP340PubKey, height uint64) bool } -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/genesis_test.go b/x/btcstaking/types/genesis_test.go index 2d1f4759..3a2ce6cc 100644 --- a/x/btcstaking/types/genesis_test.go +++ b/x/btcstaking/types/genesis_test.go @@ -28,18 +28,17 @@ func TestGenesisState_Validate(t *testing.T) { return &types.GenesisState{ Params: []*types.Params{ &types.Params{ - CovenantPks: types.DefaultParams().CovenantPks, - CovenantQuorum: types.DefaultParams().CovenantQuorum, - MinStakingValueSat: 1000, - MaxStakingValueSat: 100000000, - MinStakingTimeBlocks: 100, - MaxStakingTimeBlocks: 1000, - SlashingPkScript: types.DefaultParams().SlashingPkScript, - MinSlashingTxFeeSat: 500, - MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.5"), - SlashingRate: sdkmath.LegacyMustNewDecFromStr("0.1"), - MaxActiveFinalityProviders: 100, - UnbondingFeeSat: types.DefaultParams().UnbondingFeeSat, + CovenantPks: types.DefaultParams().CovenantPks, + CovenantQuorum: types.DefaultParams().CovenantQuorum, + MinStakingValueSat: 1000, + MaxStakingValueSat: 100000000, + MinStakingTimeBlocks: 100, + MaxStakingTimeBlocks: 1000, + SlashingPkScript: types.DefaultParams().SlashingPkScript, + MinSlashingTxFeeSat: 500, + MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.5"), + SlashingRate: sdkmath.LegacyMustNewDecFromStr("0.1"), + UnbondingFeeSat: types.DefaultParams().UnbondingFeeSat, }, }, } @@ -52,14 +51,13 @@ func TestGenesisState_Validate(t *testing.T) { return &types.GenesisState{ Params: []*types.Params{ &types.Params{ - CovenantPks: types.DefaultParams().CovenantPks, - CovenantQuorum: types.DefaultParams().CovenantQuorum, - SlashingPkScript: types.DefaultParams().SlashingPkScript, - MinSlashingTxFeeSat: 500, - MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.5"), - SlashingRate: sdkmath.LegacyZeroDec(), // invalid slashing rate - MaxActiveFinalityProviders: 100, - UnbondingFeeSat: types.DefaultParams().UnbondingFeeSat, + CovenantPks: types.DefaultParams().CovenantPks, + CovenantQuorum: types.DefaultParams().CovenantQuorum, + SlashingPkScript: types.DefaultParams().SlashingPkScript, + MinSlashingTxFeeSat: 500, + MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.5"), + SlashingRate: sdkmath.LegacyZeroDec(), // invalid slashing rate + UnbondingFeeSat: types.DefaultParams().UnbondingFeeSat, }, }, } diff --git a/x/btcstaking/types/hooks.go b/x/btcstaking/types/hooks.go deleted file mode 100644 index 89307fe1..00000000 --- a/x/btcstaking/types/hooks.go +++ /dev/null @@ -1,26 +0,0 @@ -package types - -import ( - "context" - - "github.com/babylonlabs-io/babylon/types" -) - -// combine multiple BTC staking hooks, all hook functions are run in array sequence -var _ BtcStakingHooks = &MultiBtcStakingHooks{} - -type MultiBtcStakingHooks []BtcStakingHooks - -func NewMultiBtcStakingHooks(hooks ...BtcStakingHooks) MultiBtcStakingHooks { - return hooks -} - -func (h MultiBtcStakingHooks) AfterFinalityProviderActivated(ctx context.Context, btcPk *types.BIP340PubKey) error { - for i := range h { - if err := h[i].AfterFinalityProviderActivated(ctx, btcPk); err != nil { - return err - } - } - - return nil -} diff --git a/x/btcstaking/types/mocked_keepers.go b/x/btcstaking/types/mocked_keepers.go index ced9e5be..83e4febb 100644 --- a/x/btcstaking/types/mocked_keepers.go +++ b/x/btcstaking/types/mocked_keepers.go @@ -154,43 +154,6 @@ func (mr *MockFinalityKeeperMockRecorder) HasTimestampedPubRand(ctx, fpBtcPK, he return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasTimestampedPubRand", reflect.TypeOf((*MockFinalityKeeper)(nil).HasTimestampedPubRand), ctx, fpBtcPK, height) } -// MockBtcStakingHooks is a mock of BtcStakingHooks interface. -type MockBtcStakingHooks struct { - ctrl *gomock.Controller - recorder *MockBtcStakingHooksMockRecorder -} - -// MockBtcStakingHooksMockRecorder is the mock recorder for MockBtcStakingHooks. -type MockBtcStakingHooksMockRecorder struct { - mock *MockBtcStakingHooks -} - -// NewMockBtcStakingHooks creates a new mock instance. -func NewMockBtcStakingHooks(ctrl *gomock.Controller) *MockBtcStakingHooks { - mock := &MockBtcStakingHooks{ctrl: ctrl} - mock.recorder = &MockBtcStakingHooksMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBtcStakingHooks) EXPECT() *MockBtcStakingHooksMockRecorder { - return m.recorder -} - -// AfterFinalityProviderActivated mocks base method. -func (m *MockBtcStakingHooks) AfterFinalityProviderActivated(ctx context.Context, fpPk *types.BIP340PubKey) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AfterFinalityProviderActivated", ctx, fpPk) - ret0, _ := ret[0].(error) - return ret0 -} - -// AfterFinalityProviderActivated indicates an expected call of AfterFinalityProviderActivated. -func (mr *MockBtcStakingHooksMockRecorder) AfterFinalityProviderActivated(ctx, fpPk interface{}) *gomock.Call { - 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 diff --git a/x/btcstaking/types/params.go b/x/btcstaking/types/params.go index d3a31df6..33b65a77 100644 --- a/x/btcstaking/types/params.go +++ b/x/btcstaking/types/params.go @@ -18,7 +18,6 @@ import ( ) const ( - defaultMaxActiveFinalityProviders uint32 = 100 // TODO: need to determine a proper default value defaultDelegationCreationBaseGasFee = 1000 ) @@ -72,8 +71,7 @@ func DefaultParams() Params { MinSlashingTxFeeSat: 1000, MinCommissionRate: sdkmath.LegacyZeroDec(), // The Default slashing rate is 0.1 i.e., 10% of the total staked BTC will be burned. - SlashingRate: sdkmath.LegacyNewDecWithPrec(1, 1), // 1 * 10^{-1} = 0.1 - MaxActiveFinalityProviders: defaultMaxActiveFinalityProviders, + SlashingRate: sdkmath.LegacyNewDecWithPrec(1, 1), // 1 * 10^{-1} = 0.1 // The default minimum unbonding time is 0, which effectively defaults to checkpoint // finalization timeout. MinUnbondingTimeBlocks: 0, @@ -109,15 +107,6 @@ func validateMinCommissionRate(rate sdkmath.LegacyDec) error { return nil } -// validateMaxActiveFinalityProviders checks if the maximum number of -// active finality providers is at least the default value -func validateMaxActiveFinalityProviders(maxActiveFinalityProviders uint32) error { - if maxActiveFinalityProviders == 0 { - return fmt.Errorf("max finality providers must be positive") - } - return nil -} - // validateCovenantPks checks whether the covenants list contains any duplicates func validateCovenantPks(covenantPks []bbn.BIP340PubKey) error { if ExistsDup(covenantPks) { @@ -206,10 +195,6 @@ func (p Params) Validate() error { return btcstaking.ErrInvalidSlashingRate } - if err := validateMaxActiveFinalityProviders(p.MaxActiveFinalityProviders); err != nil { - return err - } - if err := validateMinUnbondingTime(p.MinUnbondingTimeBlocks); err != nil { return err } diff --git a/x/btcstaking/types/params.pb.go b/x/btcstaking/types/params.pb.go index 5a5ee8dd..806cd720 100644 --- a/x/btcstaking/types/params.pb.go +++ b/x/btcstaking/types/params.pb.go @@ -65,10 +65,8 @@ type Params struct { // can charge their delegators expressed as a decimal (e.g., 0.5 for 50%). Maximal precion // is 2 decimal places MinCommissionRate cosmossdk_io_math.LegacyDec `protobuf:"bytes,12,opt,name=min_commission_rate,json=minCommissionRate,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"min_commission_rate"` - // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol - MaxActiveFinalityProviders uint32 `protobuf:"varint,13,opt,name=max_active_finality_providers,json=maxActiveFinalityProviders,proto3" json:"max_active_finality_providers,omitempty"` // base gas fee for delegation creation - DelegationCreationBaseGasFee uint64 `protobuf:"varint,14,opt,name=delegation_creation_base_gas_fee,json=delegationCreationBaseGasFee,proto3" json:"delegation_creation_base_gas_fee,omitempty"` + DelegationCreationBaseGasFee uint64 `protobuf:"varint,13,opt,name=delegation_creation_base_gas_fee,json=delegationCreationBaseGasFee,proto3" json:"delegation_creation_base_gas_fee,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -166,13 +164,6 @@ func (m *Params) GetUnbondingFeeSat() int64 { return 0 } -func (m *Params) GetMaxActiveFinalityProviders() uint32 { - if m != nil { - return m.MaxActiveFinalityProviders - } - return 0 -} - func (m *Params) GetDelegationCreationBaseGasFee() uint64 { if m != nil { return m.DelegationCreationBaseGasFee @@ -246,49 +237,46 @@ func init() { } var fileDescriptor_8d1392776a3e15b9 = []byte{ - // 659 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xcd, 0x6e, 0xd3, 0x4c, - 0x14, 0x8d, 0xbf, 0xe6, 0x4b, 0xe9, 0x34, 0x6d, 0xa9, 0x69, 0xc1, 0x2d, 0x34, 0x89, 0xca, 0x82, - 0x08, 0x51, 0x9b, 0xd0, 0x22, 0xf1, 0xb3, 0xaa, 0x5b, 0x05, 0x21, 0x10, 0x0a, 0x4e, 0xe9, 0x02, - 0x16, 0xd6, 0xd8, 0xb9, 0x75, 0x47, 0xf6, 0x78, 0x82, 0x67, 0x62, 0x25, 0x6f, 0xc1, 0x92, 0x25, - 0x0f, 0xc1, 0x43, 0x74, 0x59, 0xb1, 0x42, 0x5d, 0x54, 0xa8, 0x5d, 0xf2, 0x12, 0xc8, 0xe3, 0x71, - 0x12, 0x55, 0x5d, 0x74, 0x37, 0x33, 0xe7, 0x9c, 0x7b, 0xcf, 0xf1, 0xf8, 0x0e, 0xda, 0xf4, 0xb0, - 0x37, 0x8a, 0x58, 0x6c, 0x79, 0xc2, 0xe7, 0x02, 0x87, 0x24, 0x0e, 0xac, 0xb4, 0x65, 0xf5, 0x71, - 0x82, 0x29, 0x37, 0xfb, 0x09, 0x13, 0x4c, 0x5f, 0x55, 0x1c, 0x73, 0xc2, 0x31, 0xd3, 0xd6, 0xfa, - 0x4a, 0xc0, 0x02, 0x26, 0x19, 0x56, 0xb6, 0xca, 0xc9, 0xeb, 0x6b, 0x3e, 0xe3, 0x94, 0x71, 0x37, - 0x07, 0xf2, 0x4d, 0x0e, 0x6d, 0xfe, 0xad, 0xa0, 0x4a, 0x47, 0x16, 0xd6, 0xbf, 0xa0, 0xaa, 0xcf, - 0x52, 0x88, 0x71, 0x2c, 0xdc, 0x7e, 0xc8, 0x0d, 0xad, 0x31, 0xd3, 0xac, 0xda, 0x2f, 0xce, 0xce, - 0xeb, 0x3b, 0x01, 0x11, 0xc7, 0x03, 0xcf, 0xf4, 0x19, 0xb5, 0x54, 0xdf, 0x08, 0x7b, 0x7c, 0x8b, - 0xb0, 0x62, 0x6b, 0x89, 0x51, 0x1f, 0xb8, 0x69, 0xbf, 0xed, 0x6c, 0xef, 0x3c, 0xed, 0x0c, 0xbc, - 0x77, 0x30, 0x72, 0xe6, 0x8b, 0x6a, 0x9d, 0x90, 0xeb, 0x8f, 0xd0, 0xd2, 0xb8, 0xf8, 0xd7, 0x01, - 0x4b, 0x06, 0xd4, 0xf8, 0xaf, 0xa1, 0x35, 0x17, 0x9c, 0xc5, 0xe2, 0xf8, 0xa3, 0x3c, 0xd5, 0x5b, - 0x68, 0x95, 0x92, 0xd8, 0x55, 0x99, 0xdc, 0x14, 0x47, 0x03, 0x70, 0x39, 0x16, 0xc6, 0x4c, 0x43, - 0x6b, 0xce, 0x38, 0x3a, 0x25, 0x71, 0x37, 0xc7, 0x0e, 0x33, 0xa8, 0x8b, 0x85, 0x94, 0xe0, 0xe1, - 0x35, 0x92, 0xb2, 0x92, 0xe0, 0xe1, 0x55, 0xc9, 0x73, 0x74, 0x6f, 0xba, 0x8b, 0x20, 0x14, 0x5c, - 0x2f, 0x62, 0x7e, 0xc8, 0x8d, 0xff, 0xa5, 0xad, 0x95, 0x49, 0x9f, 0x03, 0x42, 0xc1, 0x96, 0x98, - 0x94, 0x4d, 0x75, 0x9a, 0x96, 0x55, 0x94, 0x6c, 0xdc, 0x6b, 0x4a, 0xf6, 0x04, 0xe9, 0x3c, 0xc2, - 0xfc, 0x38, 0xd3, 0xf4, 0x43, 0x97, 0xfb, 0x09, 0xe9, 0x0b, 0x63, 0xb6, 0xa1, 0x35, 0xab, 0xce, - 0xed, 0x02, 0xe9, 0x84, 0x5d, 0x79, 0xae, 0xef, 0x28, 0x6f, 0x85, 0x42, 0x0c, 0xdd, 0x23, 0xc8, - 0x03, 0xdd, 0x92, 0x81, 0xee, 0x64, 0xde, 0x14, 0x7a, 0x30, 0x6c, 0x83, 0x4c, 0x74, 0x88, 0x16, - 0xc6, 0x8a, 0x04, 0x0b, 0x30, 0xe6, 0x1a, 0x5a, 0x73, 0xce, 0x6e, 0x9d, 0x9c, 0xd7, 0x4b, 0x67, - 0xe7, 0xf5, 0xfb, 0xf9, 0xad, 0xf3, 0x5e, 0x68, 0x12, 0x66, 0x51, 0x2c, 0x8e, 0xcd, 0xf7, 0x10, - 0x60, 0x7f, 0xb4, 0x0f, 0xfe, 0xaf, 0x9f, 0x5b, 0x48, 0xfd, 0x14, 0xfb, 0xe0, 0x3b, 0xd5, 0xa2, - 0x8e, 0x83, 0x05, 0xe8, 0x2f, 0xd1, 0x5a, 0xe6, 0x66, 0x10, 0x7b, 0x2c, 0xee, 0x5d, 0x0d, 0x8d, - 0x64, 0xe8, 0xbb, 0x94, 0xc4, 0x9f, 0x0a, 0x7c, 0x2a, 0xf6, 0x63, 0xb4, 0x3c, 0x91, 0x15, 0x11, - 0xe6, 0x65, 0x84, 0xa5, 0x31, 0xa0, 0xec, 0x77, 0x51, 0x96, 0xca, 0xf5, 0x19, 0xa5, 0x84, 0x73, - 0xc2, 0xe2, 0x3c, 0x44, 0x55, 0x86, 0x78, 0x78, 0x83, 0x10, 0xce, 0x32, 0x25, 0xf1, 0xde, 0x58, - 0x2e, 0xbd, 0xef, 0xa2, 0x8d, 0xec, 0xba, 0xb0, 0x2f, 0x48, 0x0a, 0xee, 0x11, 0x89, 0x71, 0x44, - 0xc4, 0x28, 0x1b, 0x83, 0x94, 0xf4, 0x20, 0xe1, 0xc6, 0x82, 0xf4, 0xbf, 0x4e, 0xf1, 0x70, 0x57, - 0x72, 0xda, 0x8a, 0xd2, 0x29, 0x18, 0x7a, 0x1b, 0x35, 0x7a, 0x10, 0x41, 0x80, 0x45, 0xe6, 0xc9, - 0x4f, 0x20, 0x5f, 0x78, 0x98, 0x83, 0x1b, 0x60, 0x9e, 0xc5, 0x32, 0x16, 0x1b, 0x5a, 0xb3, 0xec, - 0x3c, 0x98, 0xf0, 0xf6, 0x14, 0xcd, 0xc6, 0x1c, 0xde, 0x60, 0xde, 0x06, 0x78, 0x55, 0xfe, 0xfe, - 0xa3, 0x5e, 0xda, 0x04, 0x54, 0xed, 0x0a, 0x96, 0x40, 0x4f, 0x8d, 0x9c, 0x81, 0x66, 0x53, 0x48, - 0x32, 0xbf, 0x86, 0x26, 0xad, 0x14, 0x5b, 0xfd, 0x35, 0xaa, 0xe4, 0xf3, 0x2e, 0xc7, 0x64, 0xfe, - 0xd9, 0x86, 0x79, 0xed, 0xc0, 0x9b, 0x79, 0x21, 0xbb, 0x9c, 0x7d, 0x21, 0x47, 0x49, 0xec, 0x0f, - 0x27, 0x17, 0x35, 0xed, 0xf4, 0xa2, 0xa6, 0xfd, 0xb9, 0xa8, 0x69, 0xdf, 0x2e, 0x6b, 0xa5, 0xd3, - 0xcb, 0x5a, 0xe9, 0xf7, 0x65, 0xad, 0xf4, 0xf9, 0x06, 0x93, 0x3c, 0x9c, 0x7e, 0x76, 0xe4, 0x58, - 0x7b, 0x15, 0xf9, 0x56, 0x6c, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xb9, 0xce, 0x21, 0xe0, 0x99, - 0x04, 0x00, 0x00, + // 620 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x94, 0xcd, 0x6e, 0xd3, 0x40, + 0x14, 0x85, 0x63, 0x1a, 0x52, 0x3a, 0x4d, 0x29, 0x35, 0x2d, 0xb8, 0x05, 0x1c, 0xab, 0x2c, 0xb0, + 0x10, 0xb5, 0x09, 0x2d, 0x12, 0x3f, 0x3b, 0xb7, 0x2a, 0x42, 0x20, 0x14, 0x9c, 0xd2, 0x05, 0x2c, + 0xac, 0xb1, 0x33, 0xb8, 0x23, 0x7b, 0x3c, 0xc6, 0x33, 0x8e, 0x92, 0xb7, 0x60, 0xc9, 0x92, 0x25, + 0x0f, 0xc0, 0x43, 0x74, 0x59, 0xb1, 0x42, 0x5d, 0x54, 0xa8, 0x79, 0x11, 0xe4, 0xf1, 0x38, 0xb1, + 0xaa, 0x2e, 0xba, 0xf3, 0xcc, 0xb9, 0xe7, 0xde, 0xf3, 0x25, 0xbe, 0x06, 0x9b, 0x3e, 0xf4, 0xc7, + 0x31, 0x4d, 0x6c, 0x9f, 0x07, 0x8c, 0xc3, 0x08, 0x27, 0xa1, 0x3d, 0xec, 0xda, 0x29, 0xcc, 0x20, + 0x61, 0x56, 0x9a, 0x51, 0x4e, 0xd5, 0x35, 0x59, 0x63, 0xcd, 0x6a, 0xac, 0x61, 0x77, 0x63, 0x35, + 0xa4, 0x21, 0x15, 0x15, 0x76, 0xf1, 0x54, 0x16, 0x6f, 0xac, 0x07, 0x94, 0x11, 0xca, 0xbc, 0x52, + 0x28, 0x0f, 0xa5, 0xb4, 0xf9, 0xab, 0x05, 0x5a, 0x3d, 0xd1, 0x58, 0xfd, 0x02, 0xda, 0x01, 0x1d, + 0xa2, 0x04, 0x26, 0xdc, 0x4b, 0x23, 0xa6, 0x29, 0xc6, 0x9c, 0xd9, 0x76, 0x5e, 0x9c, 0x9e, 0x75, + 0x76, 0x42, 0xcc, 0x8f, 0x72, 0xdf, 0x0a, 0x28, 0xb1, 0xe5, 0xdc, 0x18, 0xfa, 0x6c, 0x0b, 0xd3, + 0xea, 0x68, 0xf3, 0x71, 0x8a, 0x98, 0xe5, 0xbc, 0xed, 0x6d, 0xef, 0x3c, 0xed, 0xe5, 0xfe, 0x3b, + 0x34, 0x76, 0x17, 0xab, 0x6e, 0xbd, 0x88, 0xa9, 0x8f, 0xc0, 0xf2, 0xb4, 0xf9, 0xb7, 0x9c, 0x66, + 0x39, 0xd1, 0xae, 0x19, 0x8a, 0xb9, 0xe4, 0xde, 0xac, 0xae, 0x3f, 0x8a, 0x5b, 0xb5, 0x0b, 0xd6, + 0x08, 0x4e, 0x3c, 0xc9, 0xe4, 0x0d, 0x61, 0x9c, 0x23, 0x8f, 0x41, 0xae, 0xcd, 0x19, 0x8a, 0x39, + 0xe7, 0xaa, 0x04, 0x27, 0xfd, 0x52, 0x3b, 0x2c, 0xa4, 0x3e, 0xe4, 0xc2, 0x02, 0x47, 0x97, 0x58, + 0x9a, 0xd2, 0x02, 0x47, 0x17, 0x2d, 0xcf, 0xc1, 0xdd, 0xfa, 0x14, 0x8e, 0x09, 0xf2, 0xfc, 0x98, + 0x06, 0x11, 0xd3, 0xae, 0x8b, 0x58, 0xab, 0xb3, 0x39, 0x07, 0x98, 0x20, 0x47, 0x68, 0xc2, 0x56, + 0x9b, 0x54, 0xb7, 0xb5, 0xa4, 0x6d, 0x3a, 0xab, 0x66, 0x7b, 0x02, 0x54, 0x16, 0x43, 0x76, 0x54, + 0x78, 0xd2, 0xc8, 0x63, 0x41, 0x86, 0x53, 0xae, 0xcd, 0x1b, 0x8a, 0xd9, 0x76, 0x6f, 0x55, 0x4a, + 0x2f, 0xea, 0x8b, 0x7b, 0x75, 0x47, 0x66, 0xab, 0x1c, 0x7c, 0xe4, 0x7d, 0x45, 0x25, 0xd0, 0x0d, + 0x01, 0x74, 0xbb, 0xc8, 0x26, 0xd5, 0x83, 0xd1, 0x3e, 0x12, 0x44, 0x87, 0x60, 0x69, 0xea, 0xc8, + 0x20, 0x47, 0xda, 0x82, 0xa1, 0x98, 0x0b, 0x4e, 0xf7, 0xf8, 0xac, 0xd3, 0x38, 0x3d, 0xeb, 0xdc, + 0x2b, 0xff, 0x75, 0x36, 0x88, 0x2c, 0x4c, 0x6d, 0x02, 0xf9, 0x91, 0xf5, 0x1e, 0x85, 0x30, 0x18, + 0xef, 0xa1, 0xe0, 0xcf, 0xef, 0x2d, 0x20, 0x5f, 0x8a, 0x3d, 0x14, 0xb8, 0xed, 0xaa, 0x8f, 0x0b, + 0x39, 0x52, 0x5f, 0x82, 0xf5, 0x22, 0x4d, 0x9e, 0xf8, 0x34, 0x19, 0x5c, 0x84, 0x06, 0x02, 0xfa, + 0x0e, 0xc1, 0xc9, 0xa7, 0x4a, 0xaf, 0x61, 0x3f, 0x06, 0x2b, 0x33, 0x5b, 0x85, 0xb0, 0x28, 0x10, + 0x96, 0xa7, 0x82, 0x8c, 0xdf, 0x07, 0x05, 0x95, 0x17, 0x50, 0x42, 0x30, 0x63, 0x98, 0x26, 0x25, + 0x44, 0x5b, 0x40, 0x3c, 0xbc, 0x02, 0x84, 0xbb, 0x42, 0x70, 0xb2, 0x3b, 0xb5, 0x8b, 0xec, 0xfb, + 0xc0, 0x18, 0xa0, 0x18, 0x85, 0x90, 0x17, 0x0d, 0x83, 0x0c, 0x95, 0x0f, 0x3e, 0x64, 0xc8, 0x0b, + 0x21, 0x2b, 0x32, 0x69, 0x4b, 0x86, 0x62, 0x36, 0xdd, 0xfb, 0xb3, 0xba, 0x5d, 0x59, 0xe6, 0x40, + 0x86, 0xde, 0x40, 0xb6, 0x8f, 0xd0, 0xab, 0xe6, 0x8f, 0x9f, 0x9d, 0xc6, 0x26, 0x02, 0xed, 0x3e, + 0xa7, 0x19, 0x1a, 0xc8, 0x7d, 0xd1, 0xc0, 0xfc, 0x10, 0x65, 0xc5, 0x30, 0x4d, 0x11, 0xbf, 0x43, + 0x75, 0x54, 0x5f, 0x83, 0x56, 0xb9, 0xac, 0xe2, 0x1d, 0x5f, 0x7c, 0xf6, 0xc0, 0xba, 0x74, 0x5b, + 0xad, 0xb2, 0x91, 0xd3, 0x2c, 0xf0, 0x5c, 0x69, 0x71, 0x3e, 0x1c, 0x9f, 0xeb, 0xca, 0xc9, 0xb9, + 0xae, 0xfc, 0x3b, 0xd7, 0x95, 0xef, 0x13, 0xbd, 0x71, 0x32, 0xd1, 0x1b, 0x7f, 0x27, 0x7a, 0xe3, + 0xf3, 0x15, 0xd6, 0x70, 0x54, 0xff, 0x66, 0x88, 0x9d, 0xf4, 0x5b, 0x62, 0xd1, 0xb7, 0xff, 0x07, + 0x00, 0x00, 0xff, 0xff, 0x80, 0xd8, 0x41, 0xc8, 0x56, 0x04, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -314,11 +302,6 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.DelegationCreationBaseGasFee != 0 { i = encodeVarintParams(dAtA, i, uint64(m.DelegationCreationBaseGasFee)) i-- - dAtA[i] = 0x70 - } - if m.MaxActiveFinalityProviders != 0 { - i = encodeVarintParams(dAtA, i, uint64(m.MaxActiveFinalityProviders)) - i-- dAtA[i] = 0x68 } { @@ -498,9 +481,6 @@ func (m *Params) Size() (n int) { } l = m.MinCommissionRate.Size() n += 1 + l + sovParams(uint64(l)) - if m.MaxActiveFinalityProviders != 0 { - n += 1 + sovParams(uint64(m.MaxActiveFinalityProviders)) - } if m.DelegationCreationBaseGasFee != 0 { n += 1 + sovParams(uint64(m.DelegationCreationBaseGasFee)) } @@ -846,25 +826,6 @@ func (m *Params) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 13: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MaxActiveFinalityProviders", wireType) - } - m.MaxActiveFinalityProviders = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowParams - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.MaxActiveFinalityProviders |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DelegationCreationBaseGasFee", wireType) } diff --git a/x/btcstaking/types/validate_parsed_message_test.go b/x/btcstaking/types/validate_parsed_message_test.go index 44ae1bf2..cbfaa54d 100644 --- a/x/btcstaking/types/validate_parsed_message_test.go +++ b/x/btcstaking/types/validate_parsed_message_test.go @@ -38,19 +38,18 @@ func testStakingParams( require.NoError(t, err) return &types.Params{ - CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), - CovenantQuorum: 3, - MinStakingValueSat: 100000, - MaxStakingValueSat: int64(4 * 10e8), - MinStakingTimeBlocks: 10, - MaxStakingTimeBlocks: 10000, - SlashingPkScript: slashingPkScript, - MinSlashingTxFeeSat: 1000, - MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.01"), - SlashingRate: sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2), - MaxActiveFinalityProviders: 100, - MinUnbondingTimeBlocks: 200, - UnbondingFeeSat: 1000, + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), + CovenantQuorum: 3, + MinStakingValueSat: 100000, + MaxStakingValueSat: int64(4 * 10e8), + MinStakingTimeBlocks: 10, + MaxStakingTimeBlocks: 10000, + SlashingPkScript: slashingPkScript, + MinSlashingTxFeeSat: 1000, + MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.01"), + SlashingRate: sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2), + MinUnbondingTimeBlocks: 200, + UnbondingFeeSat: 1000, } } diff --git a/x/checkpointing/types/types.go b/x/checkpointing/types/types.go index 11672853..aaeba6b2 100644 --- a/x/checkpointing/types/types.go +++ b/x/checkpointing/types/types.go @@ -184,11 +184,11 @@ func (ckpt RawCheckpoint) ValidateBasic() error { } err := ckpt.BlockHash.ValidateBasic() if err != nil { - return ErrInvalidRawCheckpoint.Wrapf(err.Error()) + return ErrInvalidRawCheckpoint.Wrapf("error validating block hash: %s", err.Error()) } err = ckpt.BlsMultiSig.ValidateBasic() if err != nil { - return ErrInvalidRawCheckpoint.Wrapf(err.Error()) + return ErrInvalidRawCheckpoint.Wrapf("error validating BLS multi-signature: %s", err.Error()) } return nil diff --git a/x/finality/abci.go b/x/finality/abci.go index 7d2e20dd..65692983 100644 --- a/x/finality/abci.go +++ b/x/finality/abci.go @@ -14,7 +14,8 @@ import ( func BeginBlocker(ctx context.Context, k keeper.Keeper) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) - return nil + + return k.BeginBlocker(ctx) } func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) { diff --git a/x/finality/keeper/hooks.go b/x/finality/keeper/hooks.go deleted file mode 100644 index ba214000..00000000 --- a/x/finality/keeper/hooks.go +++ /dev/null @@ -1,41 +0,0 @@ -package keeper - -import ( - "context" - "errors" - - "cosmossdk.io/collections" - sdk "github.com/cosmos/cosmos-sdk/types" - - bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/babylon/x/finality/types" -) - -var _ types.BtcStakingHooks = Hooks{} - -// Hooks wrapper struct for finality keeper -type Hooks struct { - k Keeper -} - -// Return the BTC staking hooks -func (k Keeper) Hooks() Hooks { - return Hooks{k} -} - -// AfterFinalityProviderActivated updates the signing info start height or create a new signing info -func (h Hooks) AfterFinalityProviderActivated(ctx context.Context, fpPk *bbntypes.BIP340PubKey) error { - signingInfo, err := h.k.FinalityProviderSigningTracker.Get(ctx, fpPk.MustMarshal()) - sdkCtx := sdk.UnwrapSDKContext(ctx) - if err == nil { - signingInfo.StartHeight = sdkCtx.BlockHeight() - } else if errors.Is(err, collections.ErrNotFound) { - signingInfo = types.NewFinalityProviderSigningInfo( - fpPk, - sdkCtx.BlockHeight(), - 0, - ) - } - - return h.k.FinalityProviderSigningTracker.Set(ctx, fpPk.MustMarshal(), signingInfo) -} diff --git a/x/finality/keeper/keeper.go b/x/finality/keeper/keeper.go index 25e3549e..c23b29fe 100644 --- a/x/finality/keeper/keeper.go +++ b/x/finality/keeper/keeper.go @@ -25,8 +25,6 @@ type ( // should be the x/gov module account. authority string - hooks types.FinalityHooks - // FinalityProviderSigningTracker key: BIP340PubKey bytes | value: FinalityProviderSigningInfo FinalityProviderSigningTracker collections.Map[[]byte, types.FinalityProviderSigningInfo] // FinalityProviderMissedBlockBitmap key: BIP340PubKey bytes | value: byte key for a finality provider's missed block bitmap chunk @@ -68,15 +66,11 @@ func NewKeeper( } } -// SetHooks sets the finality hooks -func (k *Keeper) SetHooks(sh types.FinalityHooks) *Keeper { - if k.hooks != nil { - panic("cannot set finality hooks twice") - } - - k.hooks = sh +func (k Keeper) BeginBlocker(ctx context.Context) error { + // update voting power distribution + k.UpdatePowerDist(ctx) - return k + return nil } func (k Keeper) Logger(ctx sdk.Context) log.Logger { diff --git a/x/finality/keeper/liveness.go b/x/finality/keeper/liveness.go index 31421699..8748e384 100644 --- a/x/finality/keeper/liveness.go +++ b/x/finality/keeper/liveness.go @@ -184,7 +184,7 @@ func (k Keeper) updateSigningInfo( } func (k Keeper) jailSluggishFinalityProvider(ctx context.Context, fpBtcPk *types.BIP340PubKey) error { - err := k.hooks.AfterSluggishFinalityProviderDetected(ctx, fpBtcPk) + err := k.BTCStakingKeeper.JailFinalityProvider(ctx, fpBtcPk.MustMarshal()) if err != nil { return err } diff --git a/x/finality/keeper/liveness_test.go b/x/finality/keeper/liveness_test.go index 5eb1bda5..3b043d74 100644 --- a/x/finality/keeper/liveness_test.go +++ b/x/finality/keeper/liveness_test.go @@ -24,17 +24,14 @@ func FuzzHandleLiveness(f *testing.F) { defer ctrl.Finish() bsKeeper := types.NewMockBTCStakingKeeper(ctrl) - bsKeeper.EXPECT().GetParams(gomock.Any()).Return(bstypes.Params{MaxActiveFinalityProviders: 100}).AnyTimes() + bsKeeper.EXPECT().JailFinalityProvider(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + iKeeper := types.NewMockIncentiveKeeper(ctrl) cKeeper := types.NewMockCheckpointingKeeper(ctrl) fKeeper, ctx := keepertest.FinalityKeeper(t, bsKeeper, iKeeper, cKeeper) blockTime := time.Now() ctx = ctx.WithHeaderInfo(header.Info{Time: blockTime}) - mockedHooks := types.NewMockFinalityHooks(ctrl) - mockedHooks.EXPECT().AfterSluggishFinalityProviderDetected(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - fKeeper.SetHooks(mockedHooks) - params := fKeeper.GetParams(ctx) fpPk, err := datagen.GenRandomBIP340PubKey(r) require.NoError(t, err) diff --git a/x/finality/keeper/msg_server.go b/x/finality/keeper/msg_server.go index 38eecb53..b02329a1 100644 --- a/x/finality/keeper/msg_server.go +++ b/x/finality/keeper/msg_server.go @@ -84,11 +84,11 @@ func (ms msgServer) AddFinalitySig(goCtx context.Context, req *types.MsgAddFinal // corrupt a new finality provider and equivocate a historical block over and over again, making a previous block // unfinalisable forever if fp.IsSlashed() { - return nil, bstypes.ErrFpAlreadySlashed.Wrapf(fmt.Sprintf("finality provider public key: %s", fpPK.MarshalHex())) + return nil, bstypes.ErrFpAlreadySlashed.Wrapf("finality provider public key: %s", fpPK.MarshalHex()) } if fp.IsJailed() { - return nil, bstypes.ErrFpAlreadyJailed.Wrapf(fmt.Sprintf("finality provider public key: %s", fpPK.MarshalHex())) + return nil, bstypes.ErrFpAlreadyJailed.Wrapf("finality provider public key: %s", fpPK.MarshalHex()) } // ensure the finality provider has voting power at this height @@ -285,8 +285,7 @@ func (ms msgServer) UnjailFinalityProvider(ctx context.Context, req *types.MsgUn return nil, err } if !jailingPeriodPassed { - return nil, types.ErrJailingPeriodNotPassed.Wrapf( - fmt.Sprintf("current block time: %v, required %v", curBlockTime, info.JailedUntil)) + return nil, types.ErrJailingPeriodNotPassed.Wrapf("current block time: %v, required %v", curBlockTime, info.JailedUntil) } err = ms.BTCStakingKeeper.UnjailFinalityProvider(ctx, fpPk.MustMarshal()) diff --git a/x/finality/keeper/power_dist_change.go b/x/finality/keeper/power_dist_change.go new file mode 100644 index 00000000..fc63c112 --- /dev/null +++ b/x/finality/keeper/power_dist_change.go @@ -0,0 +1,328 @@ +package keeper + +import ( + "context" + "errors" + "fmt" + "sort" + + "cosmossdk.io/collections" + "github.com/btcsuite/btcd/btcutil" + sdk "github.com/cosmos/cosmos-sdk/types" + + bbn "github.com/babylonlabs-io/babylon/types" + "github.com/babylonlabs-io/babylon/x/btcstaking/types" + ftypes "github.com/babylonlabs-io/babylon/x/finality/types" +) + +/* power distribution update */ + +// UpdatePowerDist updates the voting power table and distribution cache. +// This is triggered upon each `BeginBlock` +func (k Keeper) UpdatePowerDist(ctx context.Context) { + height := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + btcTipHeight := k.BTCStakingKeeper.GetCurrentBTCHeight(ctx) + + // get the power dist cache in the last height + dc := k.BTCStakingKeeper.GetVotingPowerDistCache(ctx, height-1) + if dc == nil { + // no BTC staker at the prior height + dc = types.NewVotingPowerDistCache() + } + + // get all power distribution update events during the previous tip + // and the current tip + lastBTCTipHeight := k.BTCStakingKeeper.GetBTCHeightAtBabylonHeight(ctx, height-1) + events := k.BTCStakingKeeper.GetAllPowerDistUpdateEvents(ctx, lastBTCTipHeight, btcTipHeight) + + // clear all events that have been consumed in this function + defer func() { + for i := lastBTCTipHeight; i <= btcTipHeight; i++ { + k.BTCStakingKeeper.ClearPowerDistUpdateEvents(ctx, i) + } + }() + + // reconcile old voting power distribution cache and new events + // to construct the new distribution + newDc := k.ProcessAllPowerDistUpdateEvents(ctx, dc, events) + + // record voting power and cache for this height + k.recordVotingPowerAndCache(ctx, newDc) + // emit events for finality providers with state updates + k.handleFPStateUpdates(ctx, dc, newDc) + // record metrics + k.recordMetrics(newDc) +} + +// recordVotingPowerAndCache assigns voting power to each active finality provider +// with the following consideration: +// 1. the fp must have timestamped pub rand +// 2. the fp must in the top x ranked by the voting power (x is given by maxActiveFps) +func (k Keeper) recordVotingPowerAndCache(ctx context.Context, newDc *types.VotingPowerDistCache) { + if newDc == nil { + panic("the voting power distribution cache cannot be nil") + } + + babylonTipHeight := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + + // label fps with whether it has timestamped pub rand so that these fps + // will not be assigned voting power + for _, fpDistInfo := range newDc.FinalityProviders { + // TODO calling HasTimestampedPubRand potentially iterates + // all the pub rand committed by the fpDistInfo, which might slow down + // the process, need optimization + fpDistInfo.IsTimestamped = k.HasTimestampedPubRand(ctx, fpDistInfo.BtcPk, babylonTipHeight) + } + + // apply the finality provider voting power dist info to the new cache + // after which the cache would have active fps that are top N fps ranked + // by voting power with timestamped pub rand + maxActiveFps := k.GetParams(ctx).MaxActiveFinalityProviders + newDc.ApplyActiveFinalityProviders(maxActiveFps) + + // set voting power table for each active finality providers at this height + for i := uint32(0); i < newDc.NumActiveFps; i++ { + fp := newDc.FinalityProviders[i] + k.BTCStakingKeeper.SetVotingPower(ctx, fp.BtcPk.MustMarshal(), babylonTipHeight, fp.TotalBondedSat) + } + + // set the voting power distribution cache of the current height + k.BTCStakingKeeper.SetVotingPowerDistCache(ctx, babylonTipHeight, newDc) +} + +// handleFPStateUpdates emits events and triggers hooks for finality providers with state updates +func (k Keeper) handleFPStateUpdates(ctx context.Context, prevDc, newDc *types.VotingPowerDistCache) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + + newlyActiveFPs := newDc.FindNewActiveFinalityProviders(prevDc) + for _, fp := range newlyActiveFPs { + if err := k.handleActivatedFinalityProvider(ctx, fp.BtcPk); err != nil { + panic(fmt.Errorf("failed to execute after finality provider %s activated", fp.BtcPk.MarshalHex())) + } + + statusChangeEvent := types.NewFinalityProviderStatusChangeEvent(fp.BtcPk, types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_ACTIVE) + if err := sdkCtx.EventManager().EmitTypedEvent(statusChangeEvent); err != nil { + panic(fmt.Errorf( + "failed to emit FinalityProviderStatusChangeEvent with status %s: %w", + types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_ACTIVE.String(), err)) + } + + k.Logger(sdkCtx).Info("a new finality provider becomes active", "pk", fp.BtcPk.MarshalHex()) + } + + newlyInactiveFPs := newDc.FindNewInactiveFinalityProviders(prevDc) + for _, fp := range newlyInactiveFPs { + statusChangeEvent := types.NewFinalityProviderStatusChangeEvent(fp.BtcPk, types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_INACTIVE) + if err := sdkCtx.EventManager().EmitTypedEvent(statusChangeEvent); err != nil { + panic(fmt.Errorf( + "failed to emit FinalityProviderStatusChangeEvent with status %s: %w", + types.FinalityProviderStatus_FINALITY_PROVIDER_STATUS_INACTIVE.String(), err)) + } + + k.Logger(sdkCtx).Info("a new finality provider becomes inactive", "pk", fp.BtcPk.MarshalHex()) + } +} + +// handleActivatedFinalityProvider updates the signing info start height or create a new signing info +func (k Keeper) handleActivatedFinalityProvider(ctx context.Context, fpPk *bbn.BIP340PubKey) error { + signingInfo, err := k.FinalityProviderSigningTracker.Get(ctx, fpPk.MustMarshal()) + sdkCtx := sdk.UnwrapSDKContext(ctx) + if err == nil { + signingInfo.StartHeight = sdkCtx.BlockHeight() + } else if errors.Is(err, collections.ErrNotFound) { + signingInfo = ftypes.NewFinalityProviderSigningInfo( + fpPk, + sdkCtx.BlockHeight(), + 0, + ) + } + + return k.FinalityProviderSigningTracker.Set(ctx, fpPk.MustMarshal(), signingInfo) +} + +func (k Keeper) recordMetrics(dc *types.VotingPowerDistCache) { + // number of active FPs + numActiveFPs := int(dc.NumActiveFps) + types.RecordActiveFinalityProviders(numActiveFPs) + // number of inactive FPs + numInactiveFPs := len(dc.FinalityProviders) - numActiveFPs + types.RecordInactiveFinalityProviders(numInactiveFPs) + // staked Satoshi + stakedSats := btcutil.Amount(0) + for _, fp := range dc.FinalityProviders { + stakedSats += btcutil.Amount(fp.TotalBondedSat) + } + numStakedBTCs := stakedSats.ToBTC() + types.RecordMetricsKeyStakedBitcoins(float32(numStakedBTCs)) + // TODO: record number of BTC delegations under different status +} + +// ProcessAllPowerDistUpdateEvents processes all events that affect +// voting power distribution and returns a new distribution cache. +// The following events will affect the voting power distribution: +// - newly active BTC delegations +// - newly unbonded BTC delegations +// - slashed finality providers +// - newly jailed finality providers +// - newly unjailed finality providers +func (k Keeper) ProcessAllPowerDistUpdateEvents( + ctx context.Context, + dc *types.VotingPowerDistCache, + events []*types.EventPowerDistUpdate, +) *types.VotingPowerDistCache { + // a map where key is finality provider's BTC PK hex and value is a list + // of BTC delegations that newly become active under this provider + activeBTCDels := map[string][]*types.BTCDelegation{} + // a map where key is unbonded BTC delegation's staking tx hash + unbondedBTCDels := map[string]struct{}{} + // a map where key is slashed finality providers' BTC PK + slashedFPs := map[string]struct{}{} + // a map where key is jailed finality providers' BTC PK + jailedFPs := map[string]struct{}{} + // a map where key is unjailed finality providers' BTC PK + unjailedFPs := map[string]struct{}{} + + /* + filter and classify all events into new/expired BTC delegations and jailed/slashed FPs + */ + sdkCtx := sdk.UnwrapSDKContext(ctx) + for _, event := range events { + switch typedEvent := event.Ev.(type) { + case *types.EventPowerDistUpdate_BtcDelStateUpdate: + delEvent := typedEvent.BtcDelStateUpdate + btcDel, err := k.BTCStakingKeeper.GetBTCDelegation(ctx, delEvent.StakingTxHash) + if err != nil { + panic(err) // only programming error + } + if delEvent.NewState == types.BTCDelegationStatus_ACTIVE { + // newly active BTC delegation + // add the BTC delegation to each restaked finality provider + for _, fpBTCPK := range btcDel.FpBtcPkList { + fpBTCPKHex := fpBTCPK.MarshalHex() + activeBTCDels[fpBTCPKHex] = append(activeBTCDels[fpBTCPKHex], btcDel) + } + } else if delEvent.NewState == types.BTCDelegationStatus_UNBONDED { + // emit expired event if it is not early unbonding + if !btcDel.IsUnbondedEarly() { + types.EmitExpiredDelegationEvent(sdkCtx, delEvent.StakingTxHash) + } + // add the unbonded BTC delegation to the map + unbondedBTCDels[delEvent.StakingTxHash] = struct{}{} + } + case *types.EventPowerDistUpdate_SlashedFp: + // record slashed fps + types.EmitSlashedFPEvent(sdkCtx, typedEvent.SlashedFp.Pk) + slashedFPs[typedEvent.SlashedFp.Pk.MarshalHex()] = struct{}{} + case *types.EventPowerDistUpdate_JailedFp: + // record jailed fps + types.EmitJailedFPEvent(sdkCtx, typedEvent.JailedFp.Pk) + jailedFPs[typedEvent.JailedFp.Pk.MarshalHex()] = struct{}{} + case *types.EventPowerDistUpdate_UnjailedFp: + // record unjailed fps + unjailedFPs[typedEvent.UnjailedFp.Pk.MarshalHex()] = struct{}{} + } + } + + /* + At this point, there is voting power update. + Then, construct a voting power dist cache by reconciling the previous + cache and all the new events. + */ + // TODO: the algorithm needs to iterate over all BTC delegations so remains + // sub-optimal. Ideally we only need to iterate over all events above rather + // than the entire cache. This is made difficulty since BTC delegations are + // not keyed in the cache. Need to find a way to optimise this. + newDc := types.NewVotingPowerDistCache() + + // iterate over all finality providers and apply all events + for i := range dc.FinalityProviders { + // create a copy of the finality provider + fp := *dc.FinalityProviders[i] + fp.TotalBondedSat = 0 + fp.BtcDels = []*types.BTCDelDistInfo{} + + fpBTCPKHex := fp.BtcPk.MarshalHex() + + // if this finality provider is slashed, continue to avoid + // assigning delegation to it + if _, ok := slashedFPs[fpBTCPKHex]; ok { + fp.IsSlashed = true + continue + } + + // set IsJailed to be true if the fp is jailed + // Note that jailed fp can still accept delegations + // but won't be assigned with voting power + if _, ok := jailedFPs[fpBTCPKHex]; ok { + fp.IsJailed = true + } + + // set IsJailed to be false if the fp is unjailed + if _, ok := unjailedFPs[fpBTCPKHex]; ok { + fp.IsJailed = false + } + + // add all BTC delegations that are not unbonded to the new finality provider + for j := range dc.FinalityProviders[i].BtcDels { + btcDel := *dc.FinalityProviders[i].BtcDels[j] + if _, ok := unbondedBTCDels[btcDel.StakingTxHash]; !ok { + fp.AddBTCDelDistInfo(&btcDel) + } + } + + // process all new BTC delegations under this finality provider + if fpActiveBTCDels, ok := activeBTCDels[fpBTCPKHex]; ok { + // handle new BTC delegations for this finality provider + for _, d := range fpActiveBTCDels { + fp.AddBTCDel(d) + } + // remove the finality provider entry in activeBTCDels map, so that + // after the for loop the rest entries in activeBTCDels belongs to new + // finality providers with new BTC delegations + delete(activeBTCDels, fpBTCPKHex) + } + + // add this finality provider to the new cache if it has voting power + if fp.TotalBondedSat > 0 { + newDc.AddFinalityProviderDistInfo(&fp) + } + } + + /* + process new BTC delegations under new finality providers in activeBTCDels + */ + // sort new finality providers in activeBTCDels to ensure determinism + fpBTCPKHexList := make([]string, 0, len(activeBTCDels)) + for fpBTCPKHex := range activeBTCDels { + fpBTCPKHexList = append(fpBTCPKHexList, fpBTCPKHex) + } + sort.SliceStable(fpBTCPKHexList, func(i, j int) bool { + return fpBTCPKHexList[i] < fpBTCPKHexList[j] + }) + // for each new finality provider, apply the new BTC delegations to the new dist cache + for _, fpBTCPKHex := range fpBTCPKHexList { + // get the finality provider and initialise its dist info + fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(fpBTCPKHex) + if err != nil { + panic(err) // only programming error + } + newFP, err := k.BTCStakingKeeper.GetFinalityProvider(ctx, *fpBTCPK) + if err != nil { + panic(err) // only programming error + } + fpDistInfo := types.NewFinalityProviderDistInfo(newFP) + + // add each BTC delegation + fpActiveBTCDels := activeBTCDels[fpBTCPKHex] + for _, d := range fpActiveBTCDels { + fpDistInfo.AddBTCDel(d) + } + + // add this finality provider to the new cache if it has voting power + if fpDistInfo.TotalBondedSat > 0 { + newDc.AddFinalityProviderDistInfo(fpDistInfo) + } + } + + return newDc +} diff --git a/x/btcstaking/keeper/power_dist_change_test.go b/x/finality/keeper/power_dist_change_test.go similarity index 88% rename from x/btcstaking/keeper/power_dist_change_test.go rename to x/finality/keeper/power_dist_change_test.go index 88022c0a..6420934e 100644 --- a/x/btcstaking/keeper/power_dist_change_test.go +++ b/x/finality/keeper/power_dist_change_test.go @@ -9,6 +9,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + testutil "github.com/babylonlabs-io/babylon/testutil/btcstaking-helper" "github.com/babylonlabs-io/babylon/testutil/datagen" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" "github.com/babylonlabs-io/babylon/x/btcstaking/types" @@ -25,9 +26,7 @@ func FuzzProcessAllPowerDistUpdateEvents_Determinism(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters h.GenAndApplyParams(r) @@ -72,9 +71,9 @@ func FuzzProcessAllPowerDistUpdateEvents_Determinism(f *testing.F) { } } - newDc := h.BTCStakingKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events) + newDc := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events) for i := 0; i < 10; i++ { - newDc2 := h.BTCStakingKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events) + newDc2 := h.FinalityKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events) require.Equal(t, newDc, newDc2) } }) @@ -91,9 +90,7 @@ func FuzzSlashFinalityProviderEvent(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -101,7 +98,8 @@ func FuzzSlashFinalityProviderEvent(f *testing.F) { require.NoError(t, err) // generate and insert new finality provider - _, fpPK, fp := h.CreateFinalityProvider(r) + fpSK, fpPK, fp := h.CreateFinalityProvider(r) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) /* insert new BTC delegation and give it covenant quorum @@ -132,8 +130,7 @@ func FuzzSlashFinalityProviderEvent(f *testing.F) { babylonHeight := datagen.RandomInt(r, 10) + 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider has voting power at this height require.Equal(t, uint64(stakingValue), h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) @@ -162,8 +159,7 @@ func FuzzSlashFinalityProviderEvent(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider does not have voting power anymore require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) }) @@ -180,9 +176,7 @@ func FuzzJailFinalityProviderEvents(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -190,7 +184,8 @@ func FuzzJailFinalityProviderEvents(f *testing.F) { require.NoError(t, err) // generate and insert new finality provider - _, fpPK, fp := h.CreateFinalityProvider(r) + fpSK, fpPK, fp := h.CreateFinalityProvider(r) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) /* insert new BTC delegation and give it covenant quorum @@ -221,8 +216,7 @@ func FuzzJailFinalityProviderEvents(f *testing.F) { babylonHeight := datagen.RandomInt(r, 10) + 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider is not jailed and has voting power at this height fpBeforeJailing, err := h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) @@ -257,8 +251,7 @@ func FuzzJailFinalityProviderEvents(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider does not have voting power anymore require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) @@ -292,8 +285,7 @@ func FuzzJailFinalityProviderEvents(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider is not jailed and has voting power at this height fpAfterJailing, err = h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) @@ -314,9 +306,7 @@ func FuzzUnjailFinalityProviderEvents(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -324,7 +314,8 @@ func FuzzUnjailFinalityProviderEvents(f *testing.F) { require.NoError(t, err) // generate and insert new finality provider - _, fpPK, fp := h.CreateFinalityProvider(r) + fpSK, fpPK, fp := h.CreateFinalityProvider(r) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) /* insert new BTC delegation and give it covenant quorum @@ -355,8 +346,7 @@ func FuzzUnjailFinalityProviderEvents(f *testing.F) { babylonHeight := datagen.RandomInt(r, 10) + 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider is not jailed and has voting power fpBeforeJailing, err := h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, fp.BtcPk.MustMarshal()) @@ -384,8 +374,7 @@ func FuzzUnjailFinalityProviderEvents(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider does not have voting power anymore require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) @@ -405,8 +394,7 @@ func FuzzUnjailFinalityProviderEvents(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() // ensure the finality provider does not have voting power anymore require.Equal(t, uint64(stakingValue), h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) }) @@ -423,8 +411,7 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -432,7 +419,7 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { require.NoError(t, err) // generate and insert new finality provider - _, fpPK, fp := h.CreateFinalityProvider(r) + fpSK, fpPK, fp := h.CreateFinalityProvider(r) // generate and insert new BTC delegation stakingValue := int64(2 * 10e8) @@ -472,8 +459,7 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { babylonHeight := datagen.RandomInt(r, 10) + 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) /* @@ -495,18 +481,15 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Eq(babylonHeight)).Return(false).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) // ensure this finality provider has voting power at the current height after having timestamped pub rand babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Eq(babylonHeight)).Return(true).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) + h.BeginBlocker() require.Equal(t, uint64(stakingValue), h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) // ensure event queue is cleared at BTC tip height @@ -520,8 +503,7 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: unbondedHeight}).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) // ensure the unbonded event is processed and cleared @@ -541,8 +523,7 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -550,7 +531,7 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { require.NoError(t, err) // generate and insert new finality provider - _, fpPK, fp := h.CreateFinalityProvider(r) + fpSK, fpPK, fp := h.CreateFinalityProvider(r) // generate and insert new BTC delegation stakingValue := int64(2 * 10e8) @@ -569,14 +550,13 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { ) h.NoError(err) - btcTip := btclctypes.BTCHeaderInfo{Height: btcTipHeight} + btcTip := btclctypes.BTCHeaderInfo{Height: 30} // TODO: parameterise // ensure this finality provider does not have voting power at the current height babylonHeight := datagen.RandomInt(r, 10) + 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) /* @@ -592,8 +572,7 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) /* @@ -626,18 +605,15 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btcTip).AnyTimes() - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Eq(babylonHeight)).Return(false).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) // ensure this finality provider has voting power at the current height after having timestamped pub rand babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btcTip).AnyTimes() - finalityKeeper.EXPECT().HasTimestampedPubRand(gomock.Any(), gomock.Any(), gomock.Eq(babylonHeight)).Return(true).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) + h.BeginBlocker() require.Equal(t, uint64(stakingValue), h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) // ensure event queue is cleared at BTC tip height @@ -651,8 +627,7 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) { babylonHeight += 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: unbondedHeight}).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) // ensure the unbonded event is processed and cleared @@ -669,8 +644,7 @@ func TestDoNotGenerateDuplicateEventsAfterHavingCovenantQuorum(t *testing.T) { // mock BTC light client and BTC checkpoint modules btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) - finalityKeeper := types.NewMockFinalityKeeper(ctrl) - h := NewHelper(t, btclcKeeper, btccKeeper, finalityKeeper) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) // set all parameters covenantSKs, _ := h.GenAndApplyParams(r) @@ -717,8 +691,7 @@ func TestDoNotGenerateDuplicateEventsAfterHavingCovenantQuorum(t *testing.T) { babylonHeight := datagen.RandomInt(r, 10) + 1 h.SetCtxHeight(babylonHeight) h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(btcTip).AnyTimes() - err = h.BTCStakingKeeper.BeginBlocker(h.Ctx) - h.NoError(err) + h.BeginBlocker() require.Zero(t, h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fp.BtcPk, babylonHeight)) msgs := h.GenerateCovenantSignaturesMessages(r, covenantSKs, msgCreateBTCDel, actualDel) diff --git a/x/finality/keeper/power_table_test.go b/x/finality/keeper/power_table_test.go new file mode 100644 index 00000000..6d1799a5 --- /dev/null +++ b/x/finality/keeper/power_table_test.go @@ -0,0 +1,375 @@ +package keeper_test + +import ( + "math/rand" + "sort" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + testutil "github.com/babylonlabs-io/babylon/testutil/btcstaking-helper" + "github.com/babylonlabs-io/babylon/testutil/datagen" + btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + "github.com/babylonlabs-io/babylon/x/btcstaking/types" +) + +func FuzzRecordVotingPowerDistCache(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + h.NoError(err) + + // generate a random batch of finality providers, and commit + // pub rand list with timestamp + numFpsWithVotingPower := datagen.RandomInt(r, 10) + 2 + numFps := numFpsWithVotingPower + datagen.RandomInt(r, 10) + fpsWithVotingPowerMap := map[string]*types.FinalityProvider{} + for i := uint64(0); i < numFps; i++ { + fpSK, _, fp := h.CreateFinalityProvider(r) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) + if i < numFpsWithVotingPower { + // these finality providers will receive BTC delegations and have voting power + fpsWithVotingPowerMap[fp.Addr] = fp + } + } + + // 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 { + for j := uint64(0); j < numBTCDels; j++ { + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + h.NoError(err) + stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( + r, + delSK, + fp.BtcPk.MustToBTCPK(), + changeAddress.EncodeAddress(), + int64(stakingValue), + 1000, + 0, + 0, + true, + ) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, delMsg, del) + h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) + } + } + + // record voting power distribution cache + babylonHeight := datagen.RandomInt(r, 10) + 1 + h.Ctx = datagen.WithCtxHeight(h.Ctx, babylonHeight) + h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() + h.BeginBlocker() + + // assert voting power distribution cache is correct + dc := h.BTCStakingKeeper.GetVotingPowerDistCache(h.Ctx, babylonHeight) + require.NotNil(t, dc) + require.Equal(t, dc.TotalBondedSat, numFpsWithVotingPower*numBTCDels*stakingValue, dc.String()) + activeFPs := dc.GetActiveFinalityProviderSet() + for _, fpDistInfo := range activeFPs { + require.Equal(t, fpDistInfo.TotalBondedSat, numBTCDels*stakingValue) + fp, ok := fpsWithVotingPowerMap[fpDistInfo.Addr] + require.True(t, ok) + require.Equal(t, fpDistInfo.Commission, fp.Commission) + require.Len(t, fpDistInfo.BtcDels, int(numBTCDels)) + for _, delDistInfo := range fpDistInfo.BtcDels { + require.Equal(t, delDistInfo.TotalSat, stakingValue) + } + } + }) +} + +func FuzzVotingPowerTable_ActiveFinalityProviders(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + h.NoError(err) + + // generate a random batch of finality providers, each with a BTC delegation with random power + fpsWithMeta := []*types.FinalityProviderDistInfo{} + numFps := datagen.RandomInt(r, 300) + 1 + noTimestampedFps := map[string]bool{} + for i := uint64(0); i < numFps; i++ { + // generate finality provider + fpSK, _, fp := h.CreateFinalityProvider(r) + + // delegate to this finality provider + stakingValue := datagen.RandomInt(r, 100000) + 100000 + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + h.NoError(err) + stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( + r, + delSK, + fp.BtcPk.MustToBTCPK(), + changeAddress.EncodeAddress(), + int64(stakingValue), + 1000, + 0, + 0, + true, + ) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, delMsg, del) + h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) + + // 30 percent not have timestamped randomness, which causes + // zero voting power in the table + fpDistInfo := &types.FinalityProviderDistInfo{BtcPk: fp.BtcPk, TotalBondedSat: stakingValue} + if r.Intn(10) <= 2 { + h.CommitPubRandList(r, fpSK, fp, 1, 100, false) + noTimestampedFps[fp.BtcPk.MarshalHex()] = true + fpDistInfo.IsTimestamped = false + } else { + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) + fpDistInfo.IsTimestamped = true + } + + // record voting power + fpsWithMeta = append(fpsWithMeta, fpDistInfo) + } + + maxActiveFpsParam := h.FinalityKeeper.GetParams(h.Ctx).MaxActiveFinalityProviders + // get a map of expected active finality providers + types.SortFinalityProvidersWithZeroedVotingPower(fpsWithMeta) + expectedActiveFps := fpsWithMeta[:min(uint32(len(fpsWithMeta)-len(noTimestampedFps)), maxActiveFpsParam)] + expectedActiveFpsMap := map[string]uint64{} + for _, fp := range expectedActiveFps { + expectedActiveFpsMap[fp.BtcPk.MarshalHex()] = fp.TotalBondedSat + } + + // record voting power table + babylonHeight := datagen.RandomInt(r, 10) + 1 + h.SetCtxHeight(babylonHeight) + h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() + h.BeginBlocker() + + // only finality providers in expectedActiveFpsMap have voting power + for _, fp := range fpsWithMeta { + power := h.BTCStakingKeeper.GetVotingPower(h.Ctx, fp.BtcPk.MustMarshal(), babylonHeight) + if expectedPower, ok := expectedActiveFpsMap[fp.BtcPk.MarshalHex()]; ok { + require.Equal(t, expectedPower, power) + } else { + require.Zero(t, power) + } + } + + // also, get voting power table and assert there is + // min(len(expectedActiveFps), MaxActiveFinalityProviders) active finality providers + powerTable := h.BTCStakingKeeper.GetVotingPowerTable(h.Ctx, babylonHeight) + expectedNumActiveFps := len(expectedActiveFpsMap) + if expectedNumActiveFps > int(maxActiveFpsParam) { + expectedNumActiveFps = int(maxActiveFpsParam) + } + require.Len(t, powerTable, expectedNumActiveFps) + // assert consistency of voting power + for pkHex, expectedPower := range expectedActiveFpsMap { + require.Equal(t, powerTable[pkHex], expectedPower) + } + }) +} + +func FuzzVotingPowerTable_ActiveFinalityProviderRotation(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := testutil.NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + // set random number of max number of finality providers + // in order to cover cases that number of finality providers is more or + // less than `MaxActiveFinalityProviders` + fParams := h.FinalityKeeper.GetParams(h.Ctx) + fParams.MaxActiveFinalityProviders = uint32(datagen.RandomInt(r, 20) + 10) + err := h.FinalityKeeper.SetParams(h.Ctx, fParams) + h.NoError(err) + // change address + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + h.NoError(err) + + numFps := datagen.RandomInt(r, 20) + 10 + numActiveFPs := int(min(numFps, uint64(fParams.MaxActiveFinalityProviders))) + + /* + Generate a random batch of finality providers, each with a BTC delegation + with random voting power. + Then, assert voting power table + */ + fpsWithMeta := []*types.FinalityProviderWithMeta{} + for i := uint64(0); i < numFps; i++ { + // generate finality provider + // generate and insert new finality provider + fpSK, fpPK, fp := h.CreateFinalityProvider(r) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) + + // create BTC delegation and add covenant signatures to activate it + stakingValue := datagen.RandomInt(r, 100000) + 100000 + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + h.NoError(err) + stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( + r, + delSK, + fpPK, + changeAddress.EncodeAddress(), + int64(stakingValue), + 1000, + 0, + 0, + true, + ) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, delMsg, del) + h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) + + // record voting power + fpsWithMeta = append(fpsWithMeta, &types.FinalityProviderWithMeta{ + BtcPk: fp.BtcPk, + VotingPower: stakingValue, + }) + } + + // record voting power table + babylonHeight := datagen.RandomInt(r, 10) + 1 + h.Ctx = datagen.WithCtxHeight(h.Ctx, babylonHeight) + h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() + h.BeginBlocker() + + // assert that only top `min(MaxActiveFinalityProviders, numFPs)` finality providers have voting power + sort.SliceStable(fpsWithMeta, func(i, j int) bool { + return fpsWithMeta[i].VotingPower > fpsWithMeta[j].VotingPower + }) + for i := 0; i < numActiveFPs; i++ { + votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) + require.Equal(t, fpsWithMeta[i].VotingPower, votingPower) + } + for i := numActiveFPs; i < int(numFps); i++ { + votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) + require.Zero(t, votingPower) + } + + /* + Delegate more tokens to some existing finality providers + , and create some new finality providers + Then assert voting power table again + */ + // delegate more tokens to some existing finality providers + for i := uint64(0); i < numFps; i++ { + if !datagen.OneInN(r, 2) { + continue + } + + stakingValue := datagen.RandomInt(r, 100000) + 100000 + fpBTCPK := fpsWithMeta[i].BtcPk + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + h.NoError(err) + stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( + r, + delSK, + fpBTCPK.MustToBTCPK(), + changeAddress.EncodeAddress(), + int64(stakingValue), + 1000, + 0, + 0, + true, + ) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, delMsg, del) + h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) + + // accumulate voting power for this finality provider + fpsWithMeta[i].VotingPower += stakingValue + + break + } + // create more finality providers + numNewFps := datagen.RandomInt(r, 20) + 10 + numFps += numNewFps + numActiveFPs = int(min(numFps, uint64(fParams.MaxActiveFinalityProviders))) + for i := uint64(0); i < numNewFps; i++ { + // generate finality provider + // generate and insert new finality provider + fpSK, fpPK, fp := h.CreateFinalityProvider(r) + h.CommitPubRandList(r, fpSK, fp, 1, 100, true) + + // create BTC delegation and add covenant signatures to activate it + stakingValue := datagen.RandomInt(r, 100000) + 100000 + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + h.NoError(err) + stakingTxHash, delMsg, del, btcHeaderInfo, inclusionProof, _, err := h.CreateDelegation( + r, + delSK, + fpPK, + changeAddress.EncodeAddress(), + int64(stakingValue), + 1000, + 0, + 0, + true, + ) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, delMsg, del) + h.AddInclusionProof(stakingTxHash, btcHeaderInfo, inclusionProof) + + // record voting power + fpsWithMeta = append(fpsWithMeta, &types.FinalityProviderWithMeta{ + BtcPk: fp.BtcPk, + VotingPower: stakingValue, + }) + } + + // record voting power table + babylonHeight += 1 + h.Ctx = datagen.WithCtxHeight(h.Ctx, babylonHeight) + h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Eq(h.Ctx)).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() + h.BeginBlocker() + + // again, assert that only top `min(MaxActiveFinalityProviders, numFPs)` finality providers have voting power + sort.SliceStable(fpsWithMeta, func(i, j int) bool { + return fpsWithMeta[i].VotingPower > fpsWithMeta[j].VotingPower + }) + for i := 0; i < numActiveFPs; i++ { + votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) + require.Equal(t, fpsWithMeta[i].VotingPower, votingPower) + } + for i := numActiveFPs; i < int(numFps); i++ { + votingPower := h.BTCStakingKeeper.GetVotingPower(h.Ctx, *fpsWithMeta[i].BtcPk, babylonHeight) + require.Zero(t, votingPower) + } + }) +} diff --git a/x/finality/keeper/tallying.go b/x/finality/keeper/tallying.go index efc2ef4e..e9de293a 100644 --- a/x/finality/keeper/tallying.go +++ b/x/finality/keeper/tallying.go @@ -84,10 +84,10 @@ func (k Keeper) finalizeBlock(ctx context.Context, block *types.IndexedBlock, vo // set next height to finalise as height+1 k.setNextHeightToFinalize(ctx, block.Height+1) // distribute rewards to BTC staking stakeholders w.r.t. the voting power distribution cache - dc, err := k.BTCStakingKeeper.GetVotingPowerDistCache(ctx, block.Height) - if err != nil { + dc := k.BTCStakingKeeper.GetVotingPowerDistCache(ctx, block.Height) + if dc == nil { // failing to get a voting power distribution cache before distributing reward is a programming error - panic(err) + panic(fmt.Errorf("voting power distribution cache not found at height %d", block.Height)) } // filter out voted finality providers filteredDc := dc.FilterVotedDistCache(voterBTCPKs) diff --git a/x/finality/keeper/tallying_bench_test.go b/x/finality/keeper/tallying_bench_test.go index 52b1532d..12a9261d 100644 --- a/x/finality/keeper/tallying_bench_test.go +++ b/x/finality/keeper/tallying_bench_test.go @@ -44,7 +44,7 @@ func benchmarkTallyBlocks(b *testing.B, numFPs int) { bsKeeper.EXPECT().GetVotingPowerTable(gomock.Any(), gomock.Any()).Return(fpSet).AnyTimes() // TODO: test incentive - bsKeeper.EXPECT().GetVotingPowerDistCache(gomock.Any(), gomock.Any()).Return(bstypes.NewVotingPowerDistCache(), nil).AnyTimes() + bsKeeper.EXPECT().GetVotingPowerDistCache(gomock.Any(), gomock.Any()).Return(bstypes.NewVotingPowerDistCache()).AnyTimes() iKeeper.EXPECT().RewardBTCStaking(gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes() bsKeeper.EXPECT().RemoveVotingPowerDistCache(gomock.Any(), gomock.Any()).Return().AnyTimes() // Start the CPU profiler diff --git a/x/finality/keeper/tallying_test.go b/x/finality/keeper/tallying_test.go index 9a69988d..7a4ca3f7 100644 --- a/x/finality/keeper/tallying_test.go +++ b/x/finality/keeper/tallying_test.go @@ -100,7 +100,6 @@ func FuzzTallying_FinalizingSomeBlocks(f *testing.F) { defer ctrl.Finish() bsKeeper := types.NewMockBTCStakingKeeper(ctrl) - bsKeeper.EXPECT().GetParams(gomock.Any()).Return(bstypes.Params{MaxActiveFinalityProviders: 100}).AnyTimes() iKeeper := types.NewMockIncentiveKeeper(ctrl) cKeeper := types.NewMockCheckpointingKeeper(ctrl) fKeeper, ctx := keepertest.FinalityKeeper(t, bsKeeper, iKeeper, cKeeper) @@ -129,7 +128,7 @@ func FuzzTallying_FinalizingSomeBlocks(f *testing.F) { } } // we don't test incentive in this function - bsKeeper.EXPECT().GetVotingPowerDistCache(gomock.Any(), gomock.Any()).Return(bstypes.NewVotingPowerDistCache(), nil).Times(int(numWithQCs)) + bsKeeper.EXPECT().GetVotingPowerDistCache(gomock.Any(), gomock.Any()).Return(bstypes.NewVotingPowerDistCache()).Times(int(numWithQCs)) iKeeper.EXPECT().RewardBTCStaking(gomock.Any(), gomock.Any(), gomock.Any()).Return().Times(int(numWithQCs)) bsKeeper.EXPECT().RemoveVotingPowerDistCache(gomock.Any(), gomock.Any()).Return().Times(int(numWithQCs)) // add mock queries to GetBTCStakingActivatedHeight diff --git a/x/finality/types/expected_keepers.go b/x/finality/types/expected_keepers.go index e77b37a2..97941ec8 100644 --- a/x/finality/types/expected_keepers.go +++ b/x/finality/types/expected_keepers.go @@ -3,7 +3,6 @@ package types import ( "context" - bbn "github.com/babylonlabs-io/babylon/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" etypes "github.com/babylonlabs-io/babylon/x/epoching/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,14 +10,22 @@ import ( type BTCStakingKeeper interface { GetParams(ctx context.Context) bstypes.Params + GetCurrentBTCHeight(ctx context.Context) uint32 + GetBTCHeightAtBabylonHeight(ctx context.Context, babylonHeight uint64) uint32 GetFinalityProvider(ctx context.Context, fpBTCPK []byte) (*bstypes.FinalityProvider, error) HasFinalityProvider(ctx context.Context, fpBTCPK []byte) bool SlashFinalityProvider(ctx context.Context, fpBTCPK []byte) error + GetBTCDelegation(ctx context.Context, stakingTxHashStr string) (*bstypes.BTCDelegation, error) GetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64) uint64 GetVotingPowerTable(ctx context.Context, height uint64) map[string]uint64 + SetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64, votingPower uint64) GetBTCStakingActivatedHeight(ctx context.Context) (uint64, error) - GetVotingPowerDistCache(ctx context.Context, height uint64) (*bstypes.VotingPowerDistCache, error) + GetVotingPowerDistCache(ctx context.Context, height uint64) *bstypes.VotingPowerDistCache + SetVotingPowerDistCache(ctx context.Context, height uint64, dc *bstypes.VotingPowerDistCache) + GetAllPowerDistUpdateEvents(ctx context.Context, lastBTCTipHeight, btcTipHeight uint32) []*bstypes.EventPowerDistUpdate + ClearPowerDistUpdateEvents(ctx context.Context, btcHeight uint32) RemoveVotingPowerDistCache(ctx context.Context, height uint64) + JailFinalityProvider(ctx context.Context, fpBTCPK []byte) error UnjailFinalityProvider(ctx context.Context, fpBTCPK []byte) error } @@ -33,11 +40,3 @@ type IncentiveKeeper interface { RewardBTCStaking(ctx context.Context, height uint64, filteredDc *bstypes.VotingPowerDistCache) IndexRefundableMsg(ctx context.Context, msg sdk.Msg) } - -type BtcStakingHooks interface { - AfterFinalityProviderActivated(ctx context.Context, btcPk *bbn.BIP340PubKey) error -} - -type FinalityHooks interface { - AfterSluggishFinalityProviderDetected(ctx context.Context, btcPk *bbn.BIP340PubKey) error -} diff --git a/x/finality/types/hooks.go b/x/finality/types/hooks.go deleted file mode 100644 index d87591ab..00000000 --- a/x/finality/types/hooks.go +++ /dev/null @@ -1,26 +0,0 @@ -package types - -import ( - "context" - - "github.com/babylonlabs-io/babylon/types" -) - -// combine multiple finality hooks, all hook functions are run in array sequence -var _ FinalityHooks = &MultiFinalityHooks{} - -type MultiFinalityHooks []FinalityHooks - -func NewMultiFinalityHooks(hooks ...FinalityHooks) MultiFinalityHooks { - return hooks -} - -func (h MultiFinalityHooks) AfterSluggishFinalityProviderDetected(ctx context.Context, btcPk *types.BIP340PubKey) error { - for i := range h { - if err := h[i].AfterSluggishFinalityProviderDetected(ctx, btcPk); err != nil { - return err - } - } - - return nil -} diff --git a/x/finality/types/mocked_keepers.go b/x/finality/types/mocked_keepers.go index 6a4bd092..c88b595b 100644 --- a/x/finality/types/mocked_keepers.go +++ b/x/finality/types/mocked_keepers.go @@ -38,6 +38,61 @@ func (m *MockBTCStakingKeeper) EXPECT() *MockBTCStakingKeeperMockRecorder { return m.recorder } +// ClearPowerDistUpdateEvents mocks base method. +func (m *MockBTCStakingKeeper) ClearPowerDistUpdateEvents(ctx context.Context, btcHeight uint32) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ClearPowerDistUpdateEvents", ctx, btcHeight) +} + +// ClearPowerDistUpdateEvents indicates an expected call of ClearPowerDistUpdateEvents. +func (mr *MockBTCStakingKeeperMockRecorder) ClearPowerDistUpdateEvents(ctx, btcHeight interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearPowerDistUpdateEvents", reflect.TypeOf((*MockBTCStakingKeeper)(nil).ClearPowerDistUpdateEvents), ctx, btcHeight) +} + +// GetAllPowerDistUpdateEvents mocks base method. +func (m *MockBTCStakingKeeper) GetAllPowerDistUpdateEvents(ctx context.Context, lastBTCTipHeight, btcTipHeight uint32) []*types0.EventPowerDistUpdate { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllPowerDistUpdateEvents", ctx, lastBTCTipHeight, btcTipHeight) + ret0, _ := ret[0].([]*types0.EventPowerDistUpdate) + return ret0 +} + +// GetAllPowerDistUpdateEvents indicates an expected call of GetAllPowerDistUpdateEvents. +func (mr *MockBTCStakingKeeperMockRecorder) GetAllPowerDistUpdateEvents(ctx, lastBTCTipHeight, btcTipHeight interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPowerDistUpdateEvents", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetAllPowerDistUpdateEvents), ctx, lastBTCTipHeight, btcTipHeight) +} + +// GetBTCDelegation mocks base method. +func (m *MockBTCStakingKeeper) GetBTCDelegation(ctx context.Context, stakingTxHashStr string) (*types0.BTCDelegation, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBTCDelegation", ctx, stakingTxHashStr) + ret0, _ := ret[0].(*types0.BTCDelegation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBTCDelegation indicates an expected call of GetBTCDelegation. +func (mr *MockBTCStakingKeeperMockRecorder) GetBTCDelegation(ctx, stakingTxHashStr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBTCDelegation", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetBTCDelegation), ctx, stakingTxHashStr) +} + +// GetBTCHeightAtBabylonHeight mocks base method. +func (m *MockBTCStakingKeeper) GetBTCHeightAtBabylonHeight(ctx context.Context, babylonHeight uint64) uint32 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBTCHeightAtBabylonHeight", ctx, babylonHeight) + ret0, _ := ret[0].(uint32) + return ret0 +} + +// GetBTCHeightAtBabylonHeight indicates an expected call of GetBTCHeightAtBabylonHeight. +func (mr *MockBTCStakingKeeperMockRecorder) GetBTCHeightAtBabylonHeight(ctx, babylonHeight interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBTCHeightAtBabylonHeight", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetBTCHeightAtBabylonHeight), ctx, babylonHeight) +} + // GetBTCStakingActivatedHeight mocks base method. func (m *MockBTCStakingKeeper) GetBTCStakingActivatedHeight(ctx context.Context) (uint64, error) { m.ctrl.T.Helper() @@ -53,6 +108,20 @@ func (mr *MockBTCStakingKeeperMockRecorder) GetBTCStakingActivatedHeight(ctx int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBTCStakingActivatedHeight", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetBTCStakingActivatedHeight), ctx) } +// GetCurrentBTCHeight mocks base method. +func (m *MockBTCStakingKeeper) GetCurrentBTCHeight(ctx context.Context) uint32 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCurrentBTCHeight", ctx) + ret0, _ := ret[0].(uint32) + return ret0 +} + +// GetCurrentBTCHeight indicates an expected call of GetCurrentBTCHeight. +func (mr *MockBTCStakingKeeperMockRecorder) GetCurrentBTCHeight(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentBTCHeight", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetCurrentBTCHeight), ctx) +} + // GetFinalityProvider mocks base method. func (m *MockBTCStakingKeeper) GetFinalityProvider(ctx context.Context, fpBTCPK []byte) (*types0.FinalityProvider, error) { m.ctrl.T.Helper() @@ -97,12 +166,11 @@ func (mr *MockBTCStakingKeeperMockRecorder) GetVotingPower(ctx, fpBTCPK, height } // GetVotingPowerDistCache mocks base method. -func (m *MockBTCStakingKeeper) GetVotingPowerDistCache(ctx context.Context, height uint64) (*types0.VotingPowerDistCache, error) { +func (m *MockBTCStakingKeeper) GetVotingPowerDistCache(ctx context.Context, height uint64) *types0.VotingPowerDistCache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVotingPowerDistCache", ctx, height) ret0, _ := ret[0].(*types0.VotingPowerDistCache) - ret1, _ := ret[1].(error) - return ret0, ret1 + return ret0 } // GetVotingPowerDistCache indicates an expected call of GetVotingPowerDistCache. @@ -139,6 +207,20 @@ func (mr *MockBTCStakingKeeperMockRecorder) HasFinalityProvider(ctx, fpBTCPK int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasFinalityProvider", reflect.TypeOf((*MockBTCStakingKeeper)(nil).HasFinalityProvider), ctx, fpBTCPK) } +// JailFinalityProvider mocks base method. +func (m *MockBTCStakingKeeper) JailFinalityProvider(ctx context.Context, fpBTCPK []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "JailFinalityProvider", ctx, fpBTCPK) + ret0, _ := ret[0].(error) + return ret0 +} + +// JailFinalityProvider indicates an expected call of JailFinalityProvider. +func (mr *MockBTCStakingKeeperMockRecorder) JailFinalityProvider(ctx, fpBTCPK interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JailFinalityProvider", reflect.TypeOf((*MockBTCStakingKeeper)(nil).JailFinalityProvider), ctx, fpBTCPK) +} + // RemoveVotingPowerDistCache mocks base method. func (m *MockBTCStakingKeeper) RemoveVotingPowerDistCache(ctx context.Context, height uint64) { m.ctrl.T.Helper() @@ -151,6 +233,30 @@ func (mr *MockBTCStakingKeeperMockRecorder) RemoveVotingPowerDistCache(ctx, heig return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVotingPowerDistCache", reflect.TypeOf((*MockBTCStakingKeeper)(nil).RemoveVotingPowerDistCache), ctx, height) } +// SetVotingPower mocks base method. +func (m *MockBTCStakingKeeper) SetVotingPower(ctx context.Context, fpBTCPK []byte, height, votingPower uint64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetVotingPower", ctx, fpBTCPK, height, votingPower) +} + +// SetVotingPower indicates an expected call of SetVotingPower. +func (mr *MockBTCStakingKeeperMockRecorder) SetVotingPower(ctx, fpBTCPK, height, votingPower interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVotingPower", reflect.TypeOf((*MockBTCStakingKeeper)(nil).SetVotingPower), ctx, fpBTCPK, height, votingPower) +} + +// SetVotingPowerDistCache mocks base method. +func (m *MockBTCStakingKeeper) SetVotingPowerDistCache(ctx context.Context, height uint64, dc *types0.VotingPowerDistCache) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetVotingPowerDistCache", ctx, height, dc) +} + +// SetVotingPowerDistCache indicates an expected call of SetVotingPowerDistCache. +func (mr *MockBTCStakingKeeperMockRecorder) SetVotingPowerDistCache(ctx, height, dc interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVotingPowerDistCache", reflect.TypeOf((*MockBTCStakingKeeper)(nil).SetVotingPowerDistCache), ctx, height, dc) +} + // SlashFinalityProvider mocks base method. func (m *MockBTCStakingKeeper) SlashFinalityProvider(ctx context.Context, fpBTCPK []byte) error { m.ctrl.T.Helper() @@ -277,43 +383,6 @@ func (mr *MockIncentiveKeeperMockRecorder) RewardBTCStaking(ctx, height, filtere return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RewardBTCStaking", reflect.TypeOf((*MockIncentiveKeeper)(nil).RewardBTCStaking), ctx, height, filteredDc) } -// MockBtcStakingHooks is a mock of BtcStakingHooks interface. -type MockBtcStakingHooks struct { - ctrl *gomock.Controller - recorder *MockBtcStakingHooksMockRecorder -} - -// MockBtcStakingHooksMockRecorder is the mock recorder for MockBtcStakingHooks. -type MockBtcStakingHooksMockRecorder struct { - mock *MockBtcStakingHooks -} - -// NewMockBtcStakingHooks creates a new mock instance. -func NewMockBtcStakingHooks(ctrl *gomock.Controller) *MockBtcStakingHooks { - mock := &MockBtcStakingHooks{ctrl: ctrl} - mock.recorder = &MockBtcStakingHooksMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBtcStakingHooks) EXPECT() *MockBtcStakingHooksMockRecorder { - return m.recorder -} - -// AfterFinalityProviderActivated mocks base method. -func (m *MockBtcStakingHooks) AfterFinalityProviderActivated(ctx context.Context, btcPk *types.BIP340PubKey) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AfterFinalityProviderActivated", ctx, btcPk) - ret0, _ := ret[0].(error) - return ret0 -} - -// AfterFinalityProviderActivated indicates an expected call of AfterFinalityProviderActivated. -func (mr *MockBtcStakingHooksMockRecorder) AfterFinalityProviderActivated(ctx, btcPk interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterFinalityProviderActivated", reflect.TypeOf((*MockBtcStakingHooks)(nil).AfterFinalityProviderActivated), ctx, btcPk) -} - // MockFinalityHooks is a mock of FinalityHooks interface. type MockFinalityHooks struct { ctrl *gomock.Controller diff --git a/x/finality/types/params.go b/x/finality/types/params.go index 2575f4f0..591ae302 100644 --- a/x/finality/types/params.go +++ b/x/finality/types/params.go @@ -11,10 +11,11 @@ import ( // Default parameter namespace const ( - DefaultSignedBlocksWindow = int64(100) - DefaultMinPubRand = 100 - DefaultFinalitySigTimeout = 3 - DefaultJailDuration = 24 * 60 * 60 * 1 * time.Second // 1 day + DefaultMaxActiveFinalityProviders = uint32(100) + DefaultSignedBlocksWindow = int64(100) + DefaultMinPubRand = 100 + DefaultFinalitySigTimeout = 3 + DefaultJailDuration = 24 * 60 * 60 * 1 * time.Second // 1 day // For mainnet considering we want 48 hours // at a block time of 10s that would be 17280 blocks // considering the upgrade for Phase-2 will happen at block @@ -33,12 +34,13 @@ var _ paramtypes.ParamSet = (*Params)(nil) // DefaultParams returns a default set of parameters func DefaultParams() Params { return Params{ - FinalitySigTimeout: DefaultFinalitySigTimeout, - SignedBlocksWindow: DefaultSignedBlocksWindow, - MinSignedPerWindow: DefaultMinSignedPerWindow, - MinPubRand: DefaultMinPubRand, - JailDuration: DefaultJailDuration, - FinalityActivationHeight: DefaultFinalityActivationHeight, + MaxActiveFinalityProviders: DefaultMaxActiveFinalityProviders, + FinalitySigTimeout: DefaultFinalitySigTimeout, + SignedBlocksWindow: DefaultSignedBlocksWindow, + MinSignedPerWindow: DefaultMinSignedPerWindow, + MinPubRand: DefaultMinPubRand, + JailDuration: DefaultJailDuration, + FinalityActivationHeight: DefaultFinalityActivationHeight, } } @@ -63,6 +65,10 @@ func (p Params) String() string { // Validate validates the params // finality activation height can be any value, even 0. func (p Params) Validate() error { + if err := validateMaxActiveFinalityProviders(p.MaxActiveFinalityProviders); err != nil { + return err + } + if err := validateSignedBlocksWindow(p.SignedBlocksWindow); err != nil { return err } @@ -82,6 +88,15 @@ func (p Params) Validate() error { return nil } +// validateMaxActiveFinalityProviders checks if the maximum number of +// active finality providers is at least the default value +func validateMaxActiveFinalityProviders(maxActiveFinalityProviders uint32) error { + if maxActiveFinalityProviders == 0 { + return fmt.Errorf("max finality providers must be positive") + } + return nil +} + func validateSignedBlocksWindow(i interface{}) error { v, ok := i.(int64) if !ok { diff --git a/x/finality/types/params.pb.go b/x/finality/types/params.pb.go index 12df641e..17847a72 100644 --- a/x/finality/types/params.pb.go +++ b/x/finality/types/params.pb.go @@ -32,23 +32,25 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Params defines the parameters for the module. type Params struct { + // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol + MaxActiveFinalityProviders uint32 `protobuf:"varint,1,opt,name=max_active_finality_providers,json=maxActiveFinalityProviders,proto3" json:"max_active_finality_providers,omitempty"` // signed_blocks_window defines the size of the sliding window for tracking finality provider liveness - SignedBlocksWindow int64 `protobuf:"varint,1,opt,name=signed_blocks_window,json=signedBlocksWindow,proto3" json:"signed_blocks_window,omitempty"` + SignedBlocksWindow int64 `protobuf:"varint,2,opt,name=signed_blocks_window,json=signedBlocksWindow,proto3" json:"signed_blocks_window,omitempty"` // finality_sig_timeout defines how much time (in terms of blocks) finality providers have to cast a finality // vote before being judged as missing their voting turn on the given block - FinalitySigTimeout int64 `protobuf:"varint,2,opt,name=finality_sig_timeout,json=finalitySigTimeout,proto3" json:"finality_sig_timeout,omitempty"` + FinalitySigTimeout int64 `protobuf:"varint,3,opt,name=finality_sig_timeout,json=finalitySigTimeout,proto3" json:"finality_sig_timeout,omitempty"` // min_signed_per_window defines the minimum number of blocks that a finality provider is required to sign // within the sliding window to avoid being jailed - MinSignedPerWindow cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=min_signed_per_window,json=minSignedPerWindow,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"min_signed_per_window"` + MinSignedPerWindow cosmossdk_io_math.LegacyDec `protobuf:"bytes,4,opt,name=min_signed_per_window,json=minSignedPerWindow,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"min_signed_per_window"` // min_pub_rand is the minimum number of public randomness each // message should commit - MinPubRand uint64 `protobuf:"varint,4,opt,name=min_pub_rand,json=minPubRand,proto3" json:"min_pub_rand,omitempty"` + MinPubRand uint64 `protobuf:"varint,5,opt,name=min_pub_rand,json=minPubRand,proto3" json:"min_pub_rand,omitempty"` // jail_duration is the minimum period of time that a finality provider remains jailed - JailDuration time.Duration `protobuf:"bytes,5,opt,name=jail_duration,json=jailDuration,proto3,stdduration" json:"jail_duration"` + JailDuration time.Duration `protobuf:"bytes,6,opt,name=jail_duration,json=jailDuration,proto3,stdduration" json:"jail_duration"` // finality_activation_height is the babylon block height which the finality module will // start to accept finality voting and the minimum allowed value for the public randomness // commit start height. - FinalityActivationHeight uint64 `protobuf:"varint,6,opt,name=finality_activation_height,json=finalityActivationHeight,proto3" json:"finality_activation_height,omitempty"` + FinalityActivationHeight uint64 `protobuf:"varint,7,opt,name=finality_activation_height,json=finalityActivationHeight,proto3" json:"finality_activation_height,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -83,6 +85,13 @@ func (m *Params) XXX_DiscardUnknown() { var xxx_messageInfo_Params proto.InternalMessageInfo +func (m *Params) GetMaxActiveFinalityProviders() uint32 { + if m != nil { + return m.MaxActiveFinalityProviders + } + return 0 +} + func (m *Params) GetSignedBlocksWindow() int64 { if m != nil { return m.SignedBlocksWindow @@ -125,36 +134,38 @@ func init() { func init() { proto.RegisterFile("babylon/finality/v1/params.proto", fileDescriptor_25539c9a61c72ee9) } var fileDescriptor_25539c9a61c72ee9 = []byte{ - // 450 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x92, 0x31, 0x8f, 0xd3, 0x30, - 0x14, 0xc7, 0x6b, 0x5a, 0x3a, 0x84, 0xde, 0x40, 0x38, 0xa4, 0x5c, 0x91, 0xd2, 0x88, 0xa9, 0x42, - 0xba, 0x98, 0x3b, 0x24, 0x06, 0xc4, 0x42, 0xd5, 0x81, 0xe1, 0x90, 0xaa, 0x1e, 0x12, 0x12, 0x4b, - 0x64, 0x27, 0x3e, 0xf7, 0x71, 0xb1, 0x1d, 0xc5, 0x4e, 0x8f, 0x7e, 0x07, 0x06, 0xc6, 0x1b, 0x19, - 0x19, 0x19, 0xf8, 0x10, 0x37, 0x9e, 0x98, 0x10, 0xc3, 0x81, 0xda, 0x81, 0xaf, 0x81, 0x62, 0xc7, - 0xc7, 0x12, 0xe5, 0xf9, 0xf7, 0x7f, 0xfe, 0xbf, 0xf7, 0x97, 0x83, 0x84, 0x12, 0xba, 0x29, 0x95, - 0xc4, 0x67, 0x20, 0x49, 0x09, 0x66, 0x83, 0xd7, 0x47, 0xb8, 0x22, 0x35, 0x11, 0x3a, 0xad, 0x6a, - 0x65, 0x54, 0xf8, 0xa0, 0x53, 0xa4, 0x5e, 0x91, 0xae, 0x8f, 0xc6, 0xfb, 0x5c, 0x71, 0x65, 0x39, - 0x6e, 0xff, 0x9c, 0x74, 0x7c, 0x9f, 0x08, 0x90, 0x0a, 0xdb, 0x6f, 0x77, 0x74, 0x90, 0x2b, 0x2d, - 0x94, 0xce, 0x9c, 0xd6, 0x15, 0x1d, 0x8a, 0xb9, 0x52, 0xbc, 0x64, 0xd8, 0x56, 0xb4, 0x39, 0xc3, - 0x45, 0x53, 0x13, 0x03, 0x4a, 0x3a, 0xfe, 0xf8, 0x53, 0x3f, 0x18, 0x2e, 0xec, 0x24, 0xe1, 0xd3, - 0x60, 0x5f, 0x03, 0x97, 0xac, 0xc8, 0x68, 0xa9, 0xf2, 0x73, 0x9d, 0x5d, 0x80, 0x2c, 0xd4, 0x45, - 0x84, 0x12, 0x34, 0xed, 0x2f, 0x43, 0xc7, 0x66, 0x16, 0xbd, 0xb3, 0xa4, 0xed, 0xf0, 0xf3, 0x66, - 0x1a, 0x78, 0x66, 0x40, 0x30, 0xd5, 0x98, 0xe8, 0x8e, 0xeb, 0xf0, 0xec, 0x14, 0xf8, 0x5b, 0x47, - 0x42, 0x08, 0x1e, 0x0a, 0x90, 0x59, 0xe7, 0x53, 0xb1, 0xda, 0x9b, 0xf4, 0x13, 0x34, 0x1d, 0xcd, - 0x9e, 0x5f, 0xdd, 0x4c, 0x7a, 0xbf, 0x6e, 0x26, 0x8f, 0xdc, 0x0e, 0xba, 0x38, 0x4f, 0x41, 0x61, - 0x41, 0xcc, 0x2a, 0x3d, 0x61, 0x9c, 0xe4, 0x9b, 0x39, 0xcb, 0x7f, 0x7c, 0x3f, 0x0c, 0xba, 0x15, - 0xe7, 0x2c, 0xff, 0xfa, 0xf7, 0xdb, 0x13, 0xb4, 0x0c, 0x05, 0xc8, 0x53, 0x7b, 0xe7, 0x82, 0xd5, - 0xdd, 0x70, 0x49, 0x30, 0x6a, 0xad, 0xaa, 0x86, 0x66, 0x35, 0x91, 0x45, 0x34, 0x48, 0xd0, 0x74, - 0xb0, 0x0c, 0x04, 0xc8, 0x45, 0x43, 0x97, 0x44, 0x16, 0xe1, 0x9b, 0x60, 0xef, 0x03, 0x81, 0x32, - 0xf3, 0x91, 0x44, 0x77, 0x13, 0x34, 0xbd, 0x77, 0x7c, 0x90, 0xba, 0xcc, 0x52, 0x9f, 0x59, 0x3a, - 0xef, 0x04, 0xb3, 0xbd, 0x76, 0xbe, 0xcb, 0xdf, 0x13, 0xe4, 0x6c, 0x47, 0x6d, 0xbb, 0x87, 0xe1, - 0xcb, 0x60, 0x7c, 0x9b, 0x06, 0xc9, 0x0d, 0xac, 0xed, 0x71, 0xb6, 0x62, 0xc0, 0x57, 0x26, 0x1a, - 0x5a, 0xfb, 0xc8, 0x2b, 0x5e, 0xdd, 0x0a, 0x5e, 0x5b, 0xfe, 0x62, 0x70, 0xf9, 0x65, 0xd2, 0x9b, - 0x9d, 0x5c, 0x6d, 0x63, 0x74, 0xbd, 0x8d, 0xd1, 0x9f, 0x6d, 0x8c, 0x3e, 0xef, 0xe2, 0xde, 0xf5, - 0x2e, 0xee, 0xfd, 0xdc, 0xc5, 0xbd, 0xf7, 0xc7, 0x1c, 0xcc, 0xaa, 0xa1, 0x69, 0xae, 0x04, 0xee, - 0x1e, 0x4b, 0x49, 0xa8, 0x3e, 0x04, 0xe5, 0x4b, 0xfc, 0xf1, 0xff, 0xfb, 0x32, 0x9b, 0x8a, 0x69, - 0x3a, 0xb4, 0x1b, 0x3c, 0xfb, 0x17, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xae, 0x74, 0x01, 0x80, 0x02, - 0x00, 0x00, + // 486 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x92, 0x31, 0x8f, 0xd3, 0x3e, + 0x18, 0xc6, 0xeb, 0x7f, 0xfb, 0x2f, 0x52, 0x68, 0x07, 0xc2, 0x21, 0xe5, 0x8a, 0x48, 0x23, 0xa6, + 0x0a, 0xe9, 0x12, 0xee, 0x90, 0x18, 0x10, 0x4b, 0xab, 0x0a, 0x31, 0x1c, 0x52, 0xd5, 0x43, 0x42, + 0x62, 0xb1, 0x9c, 0xc4, 0xe7, 0xbe, 0x5c, 0x6c, 0x57, 0xb1, 0xd3, 0x6b, 0xbf, 0x05, 0xe3, 0x8d, + 0x8c, 0x8c, 0x0c, 0x7c, 0x88, 0xdb, 0x38, 0x31, 0x21, 0x86, 0x03, 0xb5, 0x03, 0x5f, 0x03, 0xc5, + 0x8e, 0xcb, 0x12, 0xe5, 0xf5, 0xf3, 0x7b, 0xfd, 0xbc, 0x79, 0xf2, 0x7a, 0x51, 0x4a, 0xd2, 0x4d, + 0x21, 0x45, 0x72, 0x0e, 0x82, 0x14, 0xa0, 0x37, 0xc9, 0xea, 0x38, 0x59, 0x92, 0x92, 0x70, 0x15, + 0x2f, 0x4b, 0xa9, 0xa5, 0x7f, 0xbf, 0x21, 0x62, 0x47, 0xc4, 0xab, 0xe3, 0xc1, 0x01, 0x93, 0x4c, + 0x1a, 0x3d, 0xa9, 0xdf, 0x2c, 0x3a, 0xb8, 0x47, 0x38, 0x08, 0x99, 0x98, 0x67, 0x73, 0x74, 0x98, + 0x49, 0xc5, 0xa5, 0xc2, 0x96, 0xb5, 0x45, 0x23, 0x85, 0x4c, 0x4a, 0x56, 0xd0, 0xc4, 0x54, 0x69, + 0x75, 0x9e, 0xe4, 0x55, 0x49, 0x34, 0x48, 0x61, 0xf5, 0xc7, 0xdf, 0xda, 0x5e, 0x77, 0x66, 0x26, + 0xf1, 0xc7, 0xde, 0x23, 0x4e, 0xd6, 0x98, 0x64, 0x1a, 0x56, 0x14, 0xbb, 0x41, 0xea, 0x4b, 0x57, + 0x90, 0xd3, 0x52, 0x05, 0x28, 0x42, 0xa3, 0xfe, 0x7c, 0xc0, 0xc9, 0x7a, 0x6c, 0x98, 0x57, 0x0d, + 0x32, 0x73, 0x84, 0xff, 0xd4, 0x3b, 0x50, 0xc0, 0x04, 0xcd, 0x71, 0x5a, 0xc8, 0xec, 0x42, 0xe1, + 0x4b, 0x10, 0xb9, 0xbc, 0x0c, 0xfe, 0x8b, 0xd0, 0xa8, 0x3d, 0xf7, 0xad, 0x36, 0x31, 0xd2, 0x3b, + 0xa3, 0xd4, 0x1d, 0x7b, 0x27, 0x05, 0x0c, 0x6b, 0xe0, 0x54, 0x56, 0x3a, 0x68, 0xdb, 0x0e, 0xa7, + 0x9d, 0x01, 0x7b, 0x6b, 0x15, 0x1f, 0xbc, 0x07, 0x1c, 0x04, 0x6e, 0x7c, 0x96, 0xb4, 0x74, 0x26, + 0x9d, 0x08, 0x8d, 0x7a, 0x93, 0xe7, 0xd7, 0xb7, 0xc3, 0xd6, 0xcf, 0xdb, 0xe1, 0x43, 0x1b, 0x83, + 0xca, 0x2f, 0x62, 0x90, 0x09, 0x27, 0x7a, 0x11, 0x9f, 0x52, 0x46, 0xb2, 0xcd, 0x94, 0x66, 0xdf, + 0xbf, 0x1e, 0x79, 0x4d, 0x4a, 0x53, 0x9a, 0x7d, 0xfe, 0xf3, 0xe5, 0x09, 0x9a, 0xfb, 0x1c, 0xc4, + 0x99, 0xb9, 0x73, 0x46, 0xcb, 0x66, 0xb8, 0xc8, 0xeb, 0xd5, 0x56, 0xcb, 0x2a, 0xc5, 0x25, 0x11, + 0x79, 0xf0, 0x7f, 0x84, 0x46, 0x9d, 0xb9, 0xc7, 0x41, 0xcc, 0xaa, 0x74, 0x4e, 0x44, 0xee, 0xbf, + 0xf1, 0xfa, 0x1f, 0x08, 0x14, 0xd8, 0xa5, 0x1a, 0x74, 0x23, 0x34, 0xba, 0x7b, 0x72, 0x18, 0xdb, + 0xd8, 0x63, 0x17, 0x7b, 0x3c, 0x6d, 0x80, 0x49, 0xbf, 0x9e, 0xef, 0xea, 0xd7, 0x10, 0x59, 0xdb, + 0x5e, 0xdd, 0xee, 0x44, 0xff, 0xa5, 0x37, 0xd8, 0xa7, 0x61, 0xfe, 0x83, 0x39, 0xc6, 0x0b, 0x0a, + 0x6c, 0xa1, 0x83, 0x3b, 0xc6, 0x3e, 0x70, 0xc4, 0x78, 0x0f, 0xbc, 0x36, 0xfa, 0x8b, 0xce, 0xd5, + 0xa7, 0x61, 0x6b, 0x72, 0x7a, 0xbd, 0x0d, 0xd1, 0xcd, 0x36, 0x44, 0xbf, 0xb7, 0x21, 0xfa, 0xb8, + 0x0b, 0x5b, 0x37, 0xbb, 0xb0, 0xf5, 0x63, 0x17, 0xb6, 0xde, 0x9f, 0x30, 0xd0, 0x8b, 0x2a, 0x8d, + 0x33, 0xc9, 0x93, 0x66, 0xdf, 0x0a, 0x92, 0xaa, 0x23, 0x90, 0xae, 0x4c, 0xd6, 0xff, 0x56, 0x54, + 0x6f, 0x96, 0x54, 0xa5, 0x5d, 0xf3, 0x05, 0xcf, 0xfe, 0x06, 0x00, 0x00, 0xff, 0xff, 0xa2, 0x27, + 0x77, 0xaf, 0xc3, 0x02, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -180,7 +191,7 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.FinalityActivationHeight != 0 { i = encodeVarintParams(dAtA, i, uint64(m.FinalityActivationHeight)) i-- - dAtA[i] = 0x30 + dAtA[i] = 0x38 } n1, err1 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.JailDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.JailDuration):]) if err1 != nil { @@ -189,11 +200,11 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= n1 i = encodeVarintParams(dAtA, i, uint64(n1)) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 if m.MinPubRand != 0 { i = encodeVarintParams(dAtA, i, uint64(m.MinPubRand)) i-- - dAtA[i] = 0x20 + dAtA[i] = 0x28 } { size := m.MinSignedPerWindow.Size() @@ -204,15 +215,20 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintParams(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 if m.FinalitySigTimeout != 0 { i = encodeVarintParams(dAtA, i, uint64(m.FinalitySigTimeout)) i-- - dAtA[i] = 0x10 + dAtA[i] = 0x18 } if m.SignedBlocksWindow != 0 { i = encodeVarintParams(dAtA, i, uint64(m.SignedBlocksWindow)) i-- + dAtA[i] = 0x10 + } + if m.MaxActiveFinalityProviders != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MaxActiveFinalityProviders)) + i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil @@ -235,6 +251,9 @@ func (m *Params) Size() (n int) { } var l int _ = l + if m.MaxActiveFinalityProviders != 0 { + n += 1 + sovParams(uint64(m.MaxActiveFinalityProviders)) + } if m.SignedBlocksWindow != 0 { n += 1 + sovParams(uint64(m.SignedBlocksWindow)) } @@ -290,6 +309,25 @@ func (m *Params) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxActiveFinalityProviders", wireType) + } + m.MaxActiveFinalityProviders = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxActiveFinalityProviders |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SignedBlocksWindow", wireType) } @@ -308,7 +346,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } - case 2: + case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FinalitySigTimeout", wireType) } @@ -327,7 +365,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } - case 3: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MinSignedPerWindow", wireType) } @@ -360,7 +398,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 4: + case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MinPubRand", wireType) } @@ -379,7 +417,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field JailDuration", wireType) } @@ -412,7 +450,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 6: + case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FinalityActivationHeight", wireType) }