Skip to content

Commit

Permalink
backport: chore(x/incentive): add missing data to genesis (#542) (#600)
Browse files Browse the repository at this point in the history
This is a follow up of
#519 to add some missing
fields in the genesis state
  • Loading branch information
GAtom22 authored Feb 28, 2025
1 parent c7c6c40 commit 909944f
Show file tree
Hide file tree
Showing 10 changed files with 2,652 additions and 245 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ block bank send and still create BTC delegations
- [#527](https://github.com/babylonlabs-io/babylon/pull/527) Create BSL signer on start command with flags.
- [#554](https://github.com/babylonlabs-io/babylon/pull/554) Improve vote extension logs
- [#566](https://github.com/babylonlabs-io/babylon/pull/566) Remove float values in `BeforeValidatorSlashed` hook in `x/epoching` module
- [#542](https://github.com/babylonlabs-io/babylon/pull/542) Add missing data in `InitGenesis` and `ExportGenesis` in `x/incentive` module (follow up of [#519](https://github.com/babylonlabs-io/babylon/pull/519))

### State Machine Breaking

Expand Down
65 changes: 65 additions & 0 deletions proto/babylon/incentive/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package babylon.incentive;
import "gogoproto/gogo.proto";
import "babylon/incentive/params.proto";
import "babylon/incentive/incentive.proto";
import "babylon/incentive/rewards.proto";
import "cosmos_proto/cosmos.proto";

option go_package = "github.com/babylonlabs-io/babylon/x/incentive/types";
Expand All @@ -20,6 +21,25 @@ message GenesisState {
// Withdraw addresses of the delegators
repeated WithdrawAddressEntry withdraw_addresses = 4
[ (gogoproto.nullable) = false ];
// refundable_msg_hashes is the set of hashes of messages that can be refunded
repeated string refundable_msg_hashes = 5;
// finality_providers_current_rewards are the current rewards of finality
// providers by addr
repeated FinalityProviderCurrentRewardsEntry
finality_providers_current_rewards = 6 [ (gogoproto.nullable) = false ];
// finality_providers_historical_rewards are the historical rewards of
// finality providers by addr and period
repeated FinalityProviderHistoricalRewardsEntry
finality_providers_historical_rewards = 7
[ (gogoproto.nullable) = false ];
// btc_delegation_rewards_trackers are the btc delegation rewards trackers
// stored by finality provider and delegator addresses
repeated BTCDelegationRewardsTrackerEntry btc_delegation_rewards_trackers = 8
[ (gogoproto.nullable) = false ];
// btc_delegators_to_fps are all the records of the delegators and the
// finality providers to which it delegated some BTC
repeated BTCDelegatorToFpEntry btc_delegators_to_fps = 9
[ (gogoproto.nullable) = false ];
}

// BTCStakingGaugeEntry represents a gauge for BTC staking rewards at a specific
Expand Down Expand Up @@ -52,6 +72,51 @@ message WithdrawAddressEntry {
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}

// FinalityProviderCurrentRewardsEntry represents a finality provider
// current rewards.
message FinalityProviderCurrentRewardsEntry {
// Address of the finality provider
string address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// The finality provider current rewards
FinalityProviderCurrentRewards rewards = 2;
}

// FinalityProviderHistoricalRewardsEntry represents a finality provider
// historical rewards by address and period.
message FinalityProviderHistoricalRewardsEntry {
// Address of the finality provider
string address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// Period of the historical reward
uint64 period = 2;
// The finality provider historical rewards
FinalityProviderHistoricalRewards rewards = 3;
}

// BTCDelegationRewardsTrackerEntry represents a BTC delegation
// tracker entry based on the finality provider address, the delegator address
// and a BTCDelegationTracker
message BTCDelegationRewardsTrackerEntry {
// Address of the finality provider
string finality_provider_address = 1
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// Address of the delegator
string delegator_address = 2
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// BTC delegation tracking information
BTCDelegationRewardsTracker tracker = 3;
}

// BTCDelegatorToFpEntry holds an entry of a delegator
// and a finality provider to which it delegated
message BTCDelegatorToFpEntry {
// Address of the delegator
string delegator_address = 1
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// Address of the finality provider
string finality_provider_address = 2
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}

// StakeholderType represents the different types of stakeholders.
enum StakeholderType {
option (gogoproto.goproto_enum_prefix) = false;
Expand Down
266 changes: 262 additions & 4 deletions x/incentive/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"encoding/hex"
"fmt"

"cosmossdk.io/store/prefix"
Expand Down Expand Up @@ -49,6 +50,65 @@ func (k Keeper) InitGenesis(ctx context.Context, gs types.GenesisState) error {
}
}

for _, entry := range gs.RefundableMsgHashes {
// hashes are hex encoded for better readability
bz, err := hex.DecodeString(entry)
if err != nil {
return fmt.Errorf("error decoding msg hash: %w", err)
}
if err := k.RefundableMsgKeySet.Set(ctx, bz); err != nil {
return fmt.Errorf("error storing msg hash: %w", err)
}
}

for _, entry := range gs.FinalityProvidersCurrentRewards {
// check that fp address exists
fpAddr := sdk.MustAccAddressFromBech32(entry.Address)
acc := k.accountKeeper.GetAccount(ctx, fpAddr)
if acc == nil {
return fmt.Errorf("finality provider account with address %s does not exist", entry.Address)
}
if err := k.setFinalityProviderCurrentRewards(ctx, fpAddr, *entry.Rewards); err != nil {
return err
}
}

for _, entry := range gs.FinalityProvidersHistoricalRewards {
// check that fp address exists
fpAddr := sdk.MustAccAddressFromBech32(entry.Address)
acc := k.accountKeeper.GetAccount(ctx, fpAddr)
if acc == nil {
return fmt.Errorf("finality provider account with address %s does not exist", entry.Address)
}
if err := k.setFinalityProviderHistoricalRewards(ctx, fpAddr, entry.Period, *entry.Rewards); err != nil {
return err
}
}

for _, entry := range gs.BtcDelegationRewardsTrackers {
// check that fp and delegator accounts exist
fpAddr := sdk.MustAccAddressFromBech32(entry.FinalityProviderAddress)
acc := k.accountKeeper.GetAccount(ctx, fpAddr)
if acc == nil {
return fmt.Errorf("finality provider account with address %s does not exist", entry.FinalityProviderAddress)
}
delAddr := sdk.MustAccAddressFromBech32(entry.DelegatorAddress)
acc = k.accountKeeper.GetAccount(ctx, delAddr)
if acc == nil {
return fmt.Errorf("delegator account with address %s does not exist", entry.DelegatorAddress)
}
// This function also calls setBTCDelegatorToFP() which stores the BTC delegator to FPs mapping.
// Additionally, the GenesisState.Validate() function checks that the delegator <> FP relationships
// match between the entries in BtcDelegationRewardsTrackers and BtcDelegatorsToFps.
// So this function call, stores all the corresponding BTCDelegationRewardsTrackers and the BTCDelegatorToFPs
if err := k.setBTCDelegationRewardsTracker(ctx, fpAddr, delAddr, *entry.Tracker); err != nil {
return err
}
}

// NOTE: no need to store the entries on gs.BtcDelegatorsToFps because these are stored with the setBTCDelegationRewardsTracker
// call in the lines above

return k.SetParams(ctx, gs.Params)
}

Expand All @@ -67,11 +127,42 @@ func (k Keeper) ExportGenesis(ctx context.Context) (*types.GenesisState, error)
if err != nil {
return nil, err
}

rmh, err := k.refundableMsgHashes(ctx)
if err != nil {
return nil, err
}

fpCurrentRwd, err := k.finalityProvidersCurrentRewards(ctx)
if err != nil {
return nil, err
}

fpHistRwd, err := k.finalityProvidersHistoricalRewards(ctx)
if err != nil {
return nil, err
}

bdrt, err := k.btcDelegationRewardsTrackers(ctx)
if err != nil {
return nil, err
}

d2fp, err := k.btcDelegatorsToFps(ctx)
if err != nil {
return nil, err
}

return &types.GenesisState{
Params: k.GetParams(ctx),
BtcStakingGauges: bsg,
RewardGauges: rg,
WithdrawAddresses: wa,
Params: k.GetParams(ctx),
BtcStakingGauges: bsg,
RewardGauges: rg,
WithdrawAddresses: wa,
RefundableMsgHashes: rmh,
FinalityProvidersCurrentRewards: fpCurrentRwd,
FinalityProvidersHistoricalRewards: fpHistRwd,
BtcDelegationRewardsTrackers: bdrt,
BtcDelegatorsToFps: d2fp,
}, nil
}

Expand Down Expand Up @@ -164,3 +255,170 @@ func (k Keeper) withdrawAddresses(ctx context.Context) ([]types.WithdrawAddressE

return entries, nil
}

// refundableMsgHashes loads all refundable msg hashes stored.
// It encodes the hashes as hex strings to be human readable on exporting the genesis
// This function has high resource consumption and should be only used on export genesis.
func (k Keeper) refundableMsgHashes(ctx context.Context) ([]string, error) {
hashes := make([]string, 0)
iterator, err := k.RefundableMsgKeySet.Iterate(ctx, nil)
if err != nil {
return nil, err
}
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
key, err := iterator.Key()
if err != nil {
return nil, err
}

// encode hash as a hex string
hashStr := hex.EncodeToString(key)
hashes = append(hashes, hashStr)
}

return hashes, nil
}

// finalityProvidersCurrentRewards loads all finality providers current rewards stored.
// This function has high resource consumption and should be only used on export genesis.
func (k Keeper) finalityProvidersCurrentRewards(ctx context.Context) ([]types.FinalityProviderCurrentRewardsEntry, error) {
entries := make([]types.FinalityProviderCurrentRewardsEntry, 0)

iter, err := k.finalityProviderCurrentRewards.Iterate(ctx, nil)
if err != nil {
return nil, err
}
defer iter.Close()

for ; iter.Valid(); iter.Next() {
key, err := iter.Key()
if err != nil {
return nil, err
}
addr := sdk.AccAddress(key)
currRwd, err := iter.Value()
if err != nil {
return nil, err
}
entry := types.FinalityProviderCurrentRewardsEntry{
Address: addr.String(),
Rewards: &currRwd,
}
if err := entry.Validate(); err != nil {
return nil, err
}
entries = append(entries, entry)
}

return entries, nil
}

// finalityProvidersHistoricalRewards loads all finality providers historical rewards stored.
// This function has high resource consumption and should be only used on export genesis.
func (k Keeper) finalityProvidersHistoricalRewards(ctx context.Context) ([]types.FinalityProviderHistoricalRewardsEntry, error) {
entries := make([]types.FinalityProviderHistoricalRewardsEntry, 0)

iter, err := k.finalityProviderHistoricalRewards.Iterate(ctx, nil)
if err != nil {
return nil, err
}
defer iter.Close()

for ; iter.Valid(); iter.Next() {
key, err := iter.Key()
if err != nil {
return nil, err
}
addr := sdk.AccAddress(key.K1())
period := key.K2()

histRwd, err := iter.Value()
if err != nil {
return nil, err
}
entry := types.FinalityProviderHistoricalRewardsEntry{
Address: addr.String(),
Period: period,
Rewards: &histRwd,
}
if err := entry.Validate(); err != nil {
return nil, err
}
entries = append(entries, entry)
}

return entries, nil
}

// btcDelegationRewardsTrackers loads all BTC delegation rewards trackers stored.
// This function has high resource consumption and should be only used on export genesis.
func (k Keeper) btcDelegationRewardsTrackers(ctx context.Context) ([]types.BTCDelegationRewardsTrackerEntry, error) {
entries := make([]types.BTCDelegationRewardsTrackerEntry, 0)

iter, err := k.btcDelegationRewardsTracker.Iterate(ctx, nil)
if err != nil {
return nil, err
}
defer iter.Close()

for ; iter.Valid(); iter.Next() {
key, err := iter.Key()
if err != nil {
return nil, err
}
fpAddr := sdk.AccAddress(key.K1())
delAddr := sdk.AccAddress(key.K2())
tracker, err := iter.Value()
if err != nil {
return nil, err
}
entry := types.BTCDelegationRewardsTrackerEntry{
FinalityProviderAddress: fpAddr.String(),
DelegatorAddress: delAddr.String(),
Tracker: &tracker,
}
if err := entry.Validate(); err != nil {
return nil, err
}
entries = append(entries, entry)
}

return entries, nil
}

// btcDelegatorsToFps loads all BTC delegators to finality providers relationships stored.
// This function has high resource consumption and should be only used on export genesis.
func (k Keeper) btcDelegatorsToFps(ctx context.Context) ([]types.BTCDelegatorToFpEntry, error) {
// address length is 20 bytes
addrLen := 20
entries := make([]types.BTCDelegatorToFpEntry, 0)
storeAdaptor := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
store := prefix.NewStore(storeAdaptor, types.BTCDelegatorToFPKey)
iter := store.Iterator(nil, nil)
defer iter.Close()

for ; iter.Valid(); iter.Next() {
key := iter.Key()
if len(key) < addrLen*2 { // key is composed of delAddr + fpAddr
return nil, fmt.Errorf("invalid key length: %d", len(key))
}

delAddr := sdk.AccAddress(key[:addrLen]) // First 20 bytes for delegator
fpAddr := sdk.AccAddress(key[addrLen:]) // Remaining 20 bytes for finality provider

entry := types.BTCDelegatorToFpEntry{
DelegatorAddress: delAddr.String(),
FinalityProviderAddress: fpAddr.String(),
}

if err := entry.Validate(); err != nil {
return nil, err
}

entries = append(entries, entry)
}

return entries, nil
}
Loading

0 comments on commit 909944f

Please sign in to comment.