Skip to content

Commit

Permalink
finality: move voting power dist update to finality module (#216)
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
SebastianElvis authored Oct 22, 2024
1 parent b1a4b48 commit a2dd350
Show file tree
Hide file tree
Showing 54 changed files with 1,292 additions and 1,339 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

### State Machine Breaking

* [#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

Expand Down
8 changes: 0 additions & 8 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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(
Expand Down
3 changes: 1 addition & 2 deletions app/upgrades/v1/mainnet/btcstaking_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}`
1 change: 1 addition & 0 deletions app/upgrades/v1/mainnet/finality_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 0 additions & 1 deletion app/upgrades/v1/testnet/btcstaking_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}`
1 change: 1 addition & 0 deletions app/upgrades/v1/testnet/finality_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion cmd/babylond/cmd/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,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 {
Expand All @@ -434,6 +433,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
Expand Down
4 changes: 1 addition & 3 deletions proto/babylon/btcstaking/v1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 8 additions & 5 deletions proto/babylon/finality/v1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,26 @@ 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,
(amino.dont_omitempty) = true
];
// 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];
}
2 changes: 1 addition & 1 deletion test/e2e/configurer/chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 ""
}

Expand Down
8 changes: 4 additions & 4 deletions test/e2e/configurer/chain/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -21,57 +25,84 @@ 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
}

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)
}
Expand All @@ -80,6 +111,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)
}
Expand Down Expand Up @@ -110,19 +148,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
Expand Down Expand Up @@ -470,3 +507,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
}
Loading

0 comments on commit a2dd350

Please sign in to comment.