diff --git a/CHANGELOG.md b/CHANGELOG.md index 29f982a..c38d8c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * [#93](https://github.com/babylonlabs-io/btc-staker/pull/93) Fix linting config * [#95](https://github.com/babylonlabs-io/btc-staker/pull/95) Bump babylon to v0.16 +* [#96](https://github.com/babylonlabs-io/btc-staker/pull/96) Check allow list +expiration height before creating new delegations ## v0.10.0 diff --git a/babylonclient/babyloncontroller.go b/babylonclient/babyloncontroller.go index 50c4378..04a1a54 100644 --- a/babylonclient/babyloncontroller.go +++ b/babylonclient/babyloncontroller.go @@ -99,18 +99,19 @@ func NewBabylonController( } type StakingTrackerResponse struct { - SlashingPkScript []byte - SlashingRate sdkmath.LegacyDec - MinComissionRate sdkmath.LegacyDec - CovenantPks []*btcec.PublicKey - CovenantQuruomThreshold uint32 - MinSlashingFee btcutil.Amount - MinUnbondingTime uint16 - UnbondingFee btcutil.Amount - MinStakingTime uint16 - MaxStakingTime uint16 - MinStakingValue btcutil.Amount - MaxStakingValue btcutil.Amount + SlashingPkScript []byte + SlashingRate sdkmath.LegacyDec + MinComissionRate sdkmath.LegacyDec + CovenantPks []*btcec.PublicKey + CovenantQuruomThreshold uint32 + MinSlashingFee btcutil.Amount + MinUnbondingTime uint16 + UnbondingFee btcutil.Amount + MinStakingTime uint16 + MaxStakingTime uint16 + MinStakingValue btcutil.Amount + MaxStakingValue btcutil.Amount + AllowListExpirationHeight uint64 } type FinalityProviderInfo struct { @@ -193,6 +194,7 @@ func (bc *BabylonController) Params() (*StakingParams, error) { MaxStakingTime: stakingTrackerParams.MaxStakingTime, MinStakingValue: stakingTrackerParams.MinStakingValue, MaxStakingValue: stakingTrackerParams.MaxStakingValue, + AllowListExpirationHeight: stakingTrackerParams.AllowListExpirationHeight, }, nil } @@ -217,6 +219,18 @@ func (bc *BabylonController) GetKeyAddress() sdk.AccAddress { return addr } +func (bc *BabylonController) GetLatestBlockHeight() (uint64, error) { + ctx, cancel := getQueryContext(bc.cfg.Timeout) + defer cancel() + + status, err := bc.bbnClient.RPCClient.Status(ctx) + if err != nil { + return 0, err + } + + return uint64(status.SyncInfo.LatestBlockHeight), nil +} + func (bc *BabylonController) getTxSigner() string { signer := bc.GetKeyAddress() prefix := bc.cfg.AccountPrefix @@ -501,18 +515,19 @@ func (bc *BabylonController) QueryStakingTracker() (*StakingTrackerResponse, err } return &StakingTrackerResponse{ - SlashingPkScript: response.Params.SlashingPkScript, - SlashingRate: response.Params.SlashingRate, - MinComissionRate: response.Params.MinCommissionRate, - CovenantPks: covenantPks, - MinSlashingFee: btcutil.Amount(response.Params.MinSlashingTxFeeSat), - CovenantQuruomThreshold: response.Params.CovenantQuorum, - MinUnbondingTime: uint16(minUnbondingTimeBlocksU32), - UnbondingFee: btcutil.Amount(response.Params.UnbondingFeeSat), - MinStakingTime: uint16(minStakingTimeBlocksU32), - MaxStakingTime: uint16(maxStakingTimeBlocksU32), - MinStakingValue: btcutil.Amount(response.Params.MinStakingValueSat), - MaxStakingValue: btcutil.Amount(response.Params.MaxStakingValueSat), + SlashingPkScript: response.Params.SlashingPkScript, + SlashingRate: response.Params.SlashingRate, + MinComissionRate: response.Params.MinCommissionRate, + CovenantPks: covenantPks, + MinSlashingFee: btcutil.Amount(response.Params.MinSlashingTxFeeSat), + CovenantQuruomThreshold: response.Params.CovenantQuorum, + MinUnbondingTime: uint16(minUnbondingTimeBlocksU32), + UnbondingFee: btcutil.Amount(response.Params.UnbondingFeeSat), + MinStakingTime: uint16(minStakingTimeBlocksU32), + MaxStakingTime: uint16(maxStakingTimeBlocksU32), + MinStakingValue: btcutil.Amount(response.Params.MinStakingValueSat), + MaxStakingValue: btcutil.Amount(response.Params.MaxStakingValueSat), + AllowListExpirationHeight: response.Params.AllowListExpirationHeight, }, nil } diff --git a/babylonclient/interface.go b/babylonclient/interface.go index a6ee8a9..79f0beb 100644 --- a/babylonclient/interface.go +++ b/babylonclient/interface.go @@ -54,6 +54,9 @@ type StakingParams struct { // Maximum staking value required by babylon MaxStakingValue btcutil.Amount + + // AllowList expiration height + AllowListExpirationHeight uint64 } // SingleKeyCosmosKeyring represents a keyring that supports only one pritvate/public key pair @@ -72,6 +75,7 @@ type BabylonClient interface { QueryHeaderDepth(headerHash *chainhash.Hash) (uint32, error) IsTxAlreadyPartOfDelegation(stakingTxHash *chainhash.Hash) (bool, error) QueryDelegationInfo(stakingTxHash *chainhash.Hash) (*DelegationInfo, error) + GetLatestBlockHeight() (uint64, error) } type MockBabylonClient struct { @@ -159,6 +163,10 @@ func (m *MockBabylonClient) Undelegate( return &pv.RelayerTxResponse{Code: 0}, nil } +func (m *MockBabylonClient) GetLatestBlockHeight() (uint64, error) { + return 0, nil +} + func GetMockClient() *MockBabylonClient { covenantPk, err := btcec.NewPrivateKey() if err != nil { diff --git a/staker/stakerapp.go b/staker/stakerapp.go index 0f455a3..34f1911 100644 --- a/staker/stakerapp.go +++ b/staker/stakerapp.go @@ -1783,6 +1783,21 @@ func (app *App) StakeFunds( return nil, err } + // Allow list is enabled, check if we are past the expiration height and we can + // create new delegations + if params.AllowListExpirationHeight > 0 { + latestBlockHeight, err := app.babylonClient.GetLatestBlockHeight() + + if err != nil { + return nil, fmt.Errorf("failed to get latest block height: %w", err) + } + // we add +1 to account for comet bft lazy execution + if latestBlockHeight <= params.AllowListExpirationHeight+1 { + return nil, fmt.Errorf("allow is enabled, cannot create new delegations. Latest block height %d is before allow list expiration height %d", + latestBlockHeight, params.AllowListExpirationHeight) + } + } + slashingFee := app.getSlashingFee(params.MinSlashingTxFeeSat) if stakingAmount <= slashingFee {