From 54715fb7514ebc5b2841962c79d9db9020ce10f8 Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Fri, 30 Aug 2024 11:30:48 +0200 Subject: [PATCH] Update params validation and genesis command --- cmd/babylond/cmd/flags.go | 20 ++++ cmd/babylond/cmd/genesis.go | 82 +++++++++++++---- cmd/babylond/cmd/testnet.go | 39 ++++++-- x/btcstaking/keeper/keeper_test.go | 4 + x/btcstaking/types/genesis_test.go | 91 +++++++++++++------ x/btcstaking/types/params.go | 63 ++++++++++++- .../types/parsed_message_validator.go | 21 +++++ 7 files changed, 263 insertions(+), 57 deletions(-) diff --git a/cmd/babylond/cmd/flags.go b/cmd/babylond/cmd/flags.go index 27b29c0d6..899f1d1b1 100644 --- a/cmd/babylond/cmd/flags.go +++ b/cmd/babylond/cmd/flags.go @@ -36,6 +36,10 @@ const ( flagVoteExtensionEnableHeight = "vote-extension-enable-height" flagCovenantPks = "covenant-pks" flagCovenantQuorum = "covenant-quorum" + flagMinStakingAmtSat = "min-staking-amount-sat" + flagMaxStakingAmtSat = "max-staking-amount-sat" + flagMinStakingTimeBlocks = "min-staking-time-blocks" + flagMaxStakingTimeBlocks = "max-staking-time-blocks" flagMaxActiveFinalityProviders = "max-active-finality-providers" flagMinUnbondingTime = "min-unbonding-time" flagUnbondingFeeSat = "unbonding-fee-sat" @@ -65,6 +69,10 @@ type GenesisCLIArgs struct { VoteExtensionEnableHeight int64 CovenantPKs []string CovenantQuorum uint32 + MinStakingAmtSat int64 + MaxStakingAmtSat int64 + MinStakingTimeBlocks uint16 + MaxStakingTimeBlocks uint16 SlashingPkScript string MinSlashingTransactionFeeSat int64 SlashingRate math.LegacyDec @@ -92,6 +100,10 @@ func addGenesisFlags(cmd *cobra.Command) { // btcstaking args cmd.Flags().String(flagCovenantPks, strings.Join(btcstypes.DefaultParams().CovenantPksHex(), ","), "Bitcoin staking covenant public keys, comma separated") cmd.Flags().Uint32(flagCovenantQuorum, btcstypes.DefaultParams().CovenantQuorum, "Bitcoin staking covenant quorum") + cmd.Flags().Int64(flagMinStakingAmtSat, 500000, "Minimum staking amount in satoshis") + cmd.Flags().Int64(flagMaxStakingAmtSat, 100000000000, "Maximum staking amount in satoshis") + cmd.Flags().Uint16(flagMinStakingTimeBlocks, 100, "Minimum staking time in blocks") + cmd.Flags().Uint16(flagMaxStakingTimeBlocks, 10000, "Maximum staking time in blocks") cmd.Flags().String(flagSlashingPkScript, hex.EncodeToString(btcstypes.DefaultParams().SlashingPkScript), "Bitcoin staking slashing pk script. Hex encoded.") cmd.Flags().Int64(flagMinSlashingFee, 1000, "Bitcoin staking minimum slashing fee") cmd.Flags().String(flagMinCommissionRate, "0", "Bitcoin staking validator minimum commission rate") @@ -124,6 +136,10 @@ func parseGenesisFlags(cmd *cobra.Command) *GenesisCLIArgs { reporterAddresses, _ := cmd.Flags().GetString(flagAllowedReporterAddresses) covenantPks, _ := cmd.Flags().GetString(flagCovenantPks) covenantQuorum, _ := cmd.Flags().GetUint32(flagCovenantQuorum) + minStakingAmtSat, _ := cmd.Flags().GetInt64(flagMinStakingAmtSat) + maxStakingAmtSat, _ := cmd.Flags().GetInt64(flagMaxStakingAmtSat) + minStakingTimeBlocks, _ := cmd.Flags().GetUint16(flagMinStakingTimeBlocks) + maxStakingTimeBlocks, _ := cmd.Flags().GetUint16(flagMaxStakingTimeBlocks) slashingPkScript, _ := cmd.Flags().GetString(flagSlashingPkScript) minSlashingFee, _ := cmd.Flags().GetInt64(flagMinSlashingFee) minCommissionRate, _ := cmd.Flags().GetString(flagMinCommissionRate) @@ -163,6 +179,10 @@ func parseGenesisFlags(cmd *cobra.Command) *GenesisCLIArgs { AllowedReporterAddresses: allowedReporterAddresses, CovenantPKs: strings.Split(covenantPks, ","), CovenantQuorum: covenantQuorum, + MinStakingAmtSat: minStakingAmtSat, + MaxStakingAmtSat: maxStakingAmtSat, + MinStakingTimeBlocks: minStakingTimeBlocks, + MaxStakingTimeBlocks: maxStakingTimeBlocks, SlashingPkScript: slashingPkScript, MinSlashingTransactionFeeSat: minSlashingFee, MinCommissionRate: math.LegacyMustNewDecFromStr(minCommissionRate), diff --git a/cmd/babylond/cmd/genesis.go b/cmd/babylond/cmd/genesis.go index 57761f460..1a5acab27 100644 --- a/cmd/babylond/cmd/genesis.go +++ b/cmd/babylond/cmd/genesis.go @@ -69,16 +69,37 @@ Example: var genesisParams GenesisParams if network == "testnet" { - genesisParams = TestnetGenesisParams(genesisCliArgs.MaxActiveValidators, - genesisCliArgs.BtcConfirmationDepth, genesisCliArgs.BtcFinalizationTimeout, genesisCliArgs.CheckpointTag, - genesisCliArgs.EpochInterval, genesisCliArgs.BaseBtcHeaderHex, - genesisCliArgs.BaseBtcHeaderHeight, genesisCliArgs.AllowedReporterAddresses, - genesisCliArgs.CovenantPKs, genesisCliArgs.CovenantQuorum, - genesisCliArgs.SlashingPkScript, genesisCliArgs.MinSlashingTransactionFeeSat, - genesisCliArgs.MinCommissionRate, genesisCliArgs.SlashingRate, genesisCliArgs.MaxActiveFinalityProviders, - genesisCliArgs.MinUnbondingTime, genesisCliArgs.UnbondingFeeSat, genesisCliArgs.InflationRateChange, - genesisCliArgs.InflationMin, genesisCliArgs.InflationMax, genesisCliArgs.GoalBonded, - genesisCliArgs.BlocksPerYear, genesisCliArgs.GenesisTime, genesisCliArgs.BlockGasLimit, genesisCliArgs.VoteExtensionEnableHeight) + genesisParams = TestnetGenesisParams( + genesisCliArgs.MaxActiveValidators, + genesisCliArgs.BtcConfirmationDepth, + genesisCliArgs.BtcFinalizationTimeout, + genesisCliArgs.CheckpointTag, + genesisCliArgs.EpochInterval, + genesisCliArgs.BaseBtcHeaderHex, + genesisCliArgs.BaseBtcHeaderHeight, + genesisCliArgs.AllowedReporterAddresses, + genesisCliArgs.CovenantPKs, + genesisCliArgs.CovenantQuorum, + genesisCliArgs.MinStakingAmtSat, + genesisCliArgs.MaxStakingAmtSat, + genesisCliArgs.MinStakingTimeBlocks, + genesisCliArgs.MaxStakingTimeBlocks, + genesisCliArgs.SlashingPkScript, + genesisCliArgs.MinSlashingTransactionFeeSat, + genesisCliArgs.MinCommissionRate, + genesisCliArgs.SlashingRate, + genesisCliArgs.MaxActiveFinalityProviders, + genesisCliArgs.MinUnbondingTime, + genesisCliArgs.UnbondingFeeSat, + genesisCliArgs.InflationRateChange, + genesisCliArgs.InflationMin, + genesisCliArgs.InflationMax, + genesisCliArgs.GoalBonded, + genesisCliArgs.BlocksPerYear, + genesisCliArgs.GenesisTime, + genesisCliArgs.BlockGasLimit, + genesisCliArgs.VoteExtensionEnableHeight, + ) } else if network == "mainnet" { // TODO: mainnet genesis params panic("Mainnet params not implemented.") @@ -239,12 +260,37 @@ type GenesisParams struct { VoteExtensionsEnableHeight int64 } -func TestnetGenesisParams(maxActiveValidators uint32, btcConfirmationDepth uint64, - btcFinalizationTimeout uint64, checkpointTag string, epochInterval uint64, baseBtcHeaderHex string, - baseBtcHeaderHeight uint64, allowedReporters []string, covenantPKs []string, covenantQuorum uint32, slashingPkScriptHex string, minSlashingFee int64, - minCommissionRate sdkmath.LegacyDec, slashingRate sdkmath.LegacyDec, maxActiveFinalityProviders uint32, minUnbondingTime uint16, unbondingFeeSat int64, inflationRateChange float64, - inflationMin float64, inflationMax float64, goalBonded float64, - blocksPerYear uint64, genesisTime time.Time, blockGasLimit int64, voteExtensionEnableHeight int64) GenesisParams { +func TestnetGenesisParams( + maxActiveValidators uint32, + btcConfirmationDepth uint64, + btcFinalizationTimeout uint64, + checkpointTag string, + epochInterval uint64, + baseBtcHeaderHex string, + baseBtcHeaderHeight uint64, + allowedReporters []string, + covenantPKs []string, + covenantQuorum uint32, + minStakingAmtSat int64, + maxStakingAmtSat int64, + minStakingTimeBlocks uint16, + maxStakingTimeBlocks uint16, + slashingPkScriptHex string, + minSlashingFee int64, + minCommissionRate sdkmath.LegacyDec, + slashingRate sdkmath.LegacyDec, + maxActiveFinalityProviders uint32, + minUnbondingTime uint16, + unbondingFeeSat int64, + inflationRateChange float64, + inflationMin float64, + inflationMax float64, + goalBonded float64, + blocksPerYear uint64, + genesisTime time.Time, + blockGasLimit int64, + voteExtensionEnableHeight int64, +) GenesisParams { genParams := GenesisParams{} @@ -345,6 +391,10 @@ func TestnetGenesisParams(maxActiveValidators uint32, btcConfirmationDepth uint6 genParams.BtcstakingParams.CovenantPks = covenantPKsBIP340 genParams.BtcstakingParams.CovenantQuorum = covenantQuorum + genParams.BtcstakingParams.MinStakingValueSat = minStakingAmtSat + genParams.BtcstakingParams.MaxStakingValueSat = maxStakingAmtSat + genParams.BtcstakingParams.MinStakingTimeBlocks = uint32(minStakingTimeBlocks) + genParams.BtcstakingParams.MaxStakingTimeBlocks = uint32(maxStakingTimeBlocks) genParams.BtcstakingParams.SlashingPkScript = slashingPkScript genParams.BtcstakingParams.MinSlashingTxFeeSat = minSlashingFee genParams.BtcstakingParams.MinCommissionRate = minCommissionRate diff --git a/cmd/babylond/cmd/testnet.go b/cmd/babylond/cmd/testnet.go index 392299af4..e0c0d918d 100644 --- a/cmd/babylond/cmd/testnet.go +++ b/cmd/babylond/cmd/testnet.go @@ -92,14 +92,37 @@ Example: return errors.New("base Bitcoin header height should be a uint64") } - genesisParams := TestnetGenesisParams(genesisCliArgs.MaxActiveValidators, - genesisCliArgs.BtcConfirmationDepth, genesisCliArgs.BtcFinalizationTimeout, genesisCliArgs.CheckpointTag, - genesisCliArgs.EpochInterval, genesisCliArgs.BaseBtcHeaderHex, genesisCliArgs.BaseBtcHeaderHeight, - genesisCliArgs.AllowedReporterAddresses, genesisCliArgs.CovenantPKs, genesisCliArgs.CovenantQuorum, - genesisCliArgs.SlashingPkScript, genesisCliArgs.MinSlashingTransactionFeeSat, genesisCliArgs.MinCommissionRate, - genesisCliArgs.SlashingRate, genesisCliArgs.MaxActiveFinalityProviders, genesisCliArgs.MinUnbondingTime, genesisCliArgs.UnbondingFeeSat, genesisCliArgs.InflationRateChange, genesisCliArgs.InflationMin, - genesisCliArgs.InflationMax, genesisCliArgs.GoalBonded, genesisCliArgs.BlocksPerYear, - genesisCliArgs.GenesisTime, genesisCliArgs.BlockGasLimit, genesisCliArgs.VoteExtensionEnableHeight) + genesisParams := TestnetGenesisParams( + genesisCliArgs.MaxActiveValidators, + genesisCliArgs.BtcConfirmationDepth, + genesisCliArgs.BtcFinalizationTimeout, + genesisCliArgs.CheckpointTag, + genesisCliArgs.EpochInterval, + genesisCliArgs.BaseBtcHeaderHex, + genesisCliArgs.BaseBtcHeaderHeight, + genesisCliArgs.AllowedReporterAddresses, + genesisCliArgs.CovenantPKs, + genesisCliArgs.CovenantQuorum, + genesisCliArgs.MinStakingAmtSat, + genesisCliArgs.MaxStakingAmtSat, + genesisCliArgs.MinStakingTimeBlocks, + genesisCliArgs.MaxStakingTimeBlocks, + genesisCliArgs.SlashingPkScript, + genesisCliArgs.MinSlashingTransactionFeeSat, + genesisCliArgs.MinCommissionRate, + genesisCliArgs.SlashingRate, + genesisCliArgs.MaxActiveFinalityProviders, + genesisCliArgs.MinUnbondingTime, + genesisCliArgs.UnbondingFeeSat, + genesisCliArgs.InflationRateChange, + genesisCliArgs.InflationMin, + genesisCliArgs.InflationMax, + genesisCliArgs.GoalBonded, + genesisCliArgs.BlocksPerYear, + genesisCliArgs.GenesisTime, + genesisCliArgs.BlockGasLimit, + genesisCliArgs.VoteExtensionEnableHeight, + ) return InitTestnet( clientCtx, cmd, config, mbm, genBalIterator, outputDir, genesisCliArgs.ChainID, minGasPrices, diff --git a/x/btcstaking/keeper/keeper_test.go b/x/btcstaking/keeper/keeper_test.go index 7c0939308..39bb17bc5 100644 --- a/x/btcstaking/keeper/keeper_test.go +++ b/x/btcstaking/keeper/keeper_test.go @@ -102,6 +102,10 @@ func (h *Helper) GenAndApplyCustomParams( 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"), diff --git a/x/btcstaking/types/genesis_test.go b/x/btcstaking/types/genesis_test.go index 266121668..2d1f4759a 100644 --- a/x/btcstaking/types/genesis_test.go +++ b/x/btcstaking/types/genesis_test.go @@ -12,50 +12,85 @@ import ( func TestGenesisState_Validate(t *testing.T) { tests := []struct { desc string - genState *types.GenesisState + genState func() *types.GenesisState valid bool }{ { - desc: "default is valid", - genState: types.DefaultGenesis(), - valid: true, + desc: "default is valid", + genState: func() *types.GenesisState { + return types.DefaultGenesis() + }, + valid: true, }, { desc: "valid genesis state", - genState: &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.LegacyMustNewDecFromStr("0.1"), - MaxActiveFinalityProviders: 100, - UnbondingFeeSat: types.DefaultParams().UnbondingFeeSat, - }}, + genState: func() *types.GenesisState { + 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, + }, + }, + } }, valid: true, }, { desc: "invalid slashing rate in genesis", - genState: &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, - }, - }}, + genState: func() *types.GenesisState { + 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, + }, + }, + } + }, + valid: false, + }, + { + desc: "min staking time larger than max staking time", + genState: func() *types.GenesisState { + d := types.DefaultGenesis() + d.Params[0].MinStakingTimeBlocks = 1000 + d.Params[0].MaxStakingTimeBlocks = 100 + return d + }, + valid: false, + }, + { + desc: "min staking value larger than max staking value", + genState: func() *types.GenesisState { + d := types.DefaultGenesis() + d.Params[0].MinStakingValueSat = 1000 + d.Params[0].MaxStakingValueSat = 100 + return d + }, valid: false, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - err := tc.genState.Validate() + state := tc.genState() + err := state.Validate() if tc.valid { require.NoError(t, err) } else { diff --git a/x/btcstaking/types/params.go b/x/btcstaking/types/params.go index e1bd1bbdf..43dd89959 100644 --- a/x/btcstaking/types/params.go +++ b/x/btcstaking/types/params.go @@ -59,11 +59,15 @@ func ParamKeyTable() paramtypes.KeyTable { func DefaultParams() Params { _, pks, quorum := DefaultCovenantCommittee() return Params{ - CovenantPks: bbn.NewBIP340PKsFromBTCPKs(pks), - CovenantQuorum: quorum, - SlashingPkScript: defaultSlashingPkScript(), - MinSlashingTxFeeSat: 1000, - MinCommissionRate: sdkmath.LegacyZeroDec(), + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(pks), + CovenantQuorum: quorum, + MinStakingValueSat: 1000, + MaxStakingValueSat: 10000000, + MinStakingTimeBlocks: 10, + MaxStakingTimeBlocks: 10000, + SlashingPkScript: defaultSlashingPkScript(), + 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, @@ -126,6 +130,46 @@ func validateMinUnbondingTime(minUnbondingTimeBlocks uint32) error { return nil } +func validateStakingAmout(minStakingAmt, maxStakingAmt int64) error { + if minStakingAmt <= 0 { + return fmt.Errorf("minimum staking amount has to be positive") + } + + if maxStakingAmt <= 0 { + return fmt.Errorf("maximum staking amount has to be positive") + } + + if minStakingAmt > maxStakingAmt { + return fmt.Errorf("minimum staking amount cannot be greater than maximum staking amount") + } + + return nil +} + +func validateStakingTime(minStakingTime, maxStakingTime uint32) error { + if minStakingTime == 0 { + return fmt.Errorf("minimum staking time has to be positive") + } + + if minStakingTime > math.MaxUint16 { + return fmt.Errorf("minimum staking time cannot be greater than %d", math.MaxUint16) + } + + if maxStakingTime == 0 { + return fmt.Errorf("maximum staking time has to be positive") + } + + if maxStakingTime > math.MaxUint16 { + return fmt.Errorf("maximum staking time cannot be greater than %d", math.MaxUint16) + } + + if minStakingTime > maxStakingTime { + return fmt.Errorf("minimum staking time cannot be greater than maximum staking time") + } + + return nil +} + // Validate validates the set of params func (p Params) Validate() error { if p.CovenantQuorum == 0 { @@ -134,6 +178,15 @@ func (p Params) Validate() error { if p.CovenantQuorum*2 <= uint32(len(p.CovenantPks)) { return fmt.Errorf("covenant quorum size has to be more than 1/2 of the covenant committee size") } + + if err := validateStakingAmout(p.MinStakingValueSat, p.MaxStakingValueSat); err != nil { + return err + } + + if err := validateStakingTime(p.MinStakingTimeBlocks, p.MaxStakingTimeBlocks); err != nil { + return err + } + if err := validateCovenantPks(p.CovenantPks); err != nil { return err } diff --git a/x/btcstaking/types/parsed_message_validator.go b/x/btcstaking/types/parsed_message_validator.go index 3cf375727..050477a97 100644 --- a/x/btcstaking/types/parsed_message_validator.go +++ b/x/btcstaking/types/parsed_message_validator.go @@ -36,6 +36,7 @@ func ValidateParams( // 2. Validate all data related to staking tx: // - it has valid staking output + // - that staking time and value are correct // - slashing tx is relevent to staking tx // - slashing tx signature is valid stakingInfo, err := btcstaking.BuildStakingInfo( @@ -57,6 +58,26 @@ func ValidateParams( return nil, ErrInvalidStakingTx.Wrap("staking tx does not contain expected staking output") } + if uint32(pm.StakingTime) < parameters.MinStakingTimeBlocks || + uint32(pm.StakingTime) > parameters.MaxStakingTimeBlocks { + return nil, ErrInvalidStakingTx.Wrapf( + "staking time %d is out of bounds. Min: %d, Max: %d", + pm.StakingTime, + parameters.MinStakingTimeBlocks, + parameters.MaxStakingTimeBlocks, + ) + } + + if pm.StakingTx.Transaction.TxOut[stakingOutputIdx].Value < parameters.MinStakingValueSat || + pm.StakingTx.Transaction.TxOut[stakingOutputIdx].Value > parameters.MaxStakingValueSat { + return nil, ErrInvalidStakingTx.Wrapf( + "staking value %d is out of bounds. Min: %d, Max: %d", + pm.StakingTx.Transaction.TxOut[stakingOutputIdx].Value, + parameters.MinStakingValueSat, + parameters.MaxStakingValueSat, + ) + } + if err := btcstaking.CheckTransactions( pm.StakingSlashingTx.Transaction, pm.StakingTx.Transaction,