Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hard nett/v020 rc #245

Merged
merged 4 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions app/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,28 @@ func NewDefaultGenesisStateWithCodec(cdc codec.JSONCodec) GenesisState {
// stakingGenesisState returns the default genesis state for the staking module, replacing the
// bond denom from stake to ubtsg
// func stakingGenesisState() *stakingtypes.GenesisState {
// historicalEntries := uint32(0)
// return &stakingtypes.GenesisState{
// Params: stakingtypes.NewParams(
// stakingtypes.DefaultUnbondingTime,
// stakingtypes.DefaultMaxValidators,
// stakingtypes.DefaultMaxEntries,
// 0,
// historicalEntries,
// "ubtsg", sdk.ZeroDec(),
// ),
// }
// }

// func govGenesisState() *govtypes.GenesisState {
// return govtypes.NewGenesisState(
// 1,
// govtypes.NewDepositParams(
// sdk.NewCoins(sdk.NewCoin("ubtsg", govtypes.DefaultMinDepositTokens)),
// govtypes.DefaultPeriod,
// func govGenesisState() *govv1beta1.GenesisState {
// startingPropId := uint64(1)
// return govv1beta1.NewGenesisState(
// startingPropId,
// govv1beta1.NewDepositParams(
// sdk.NewCoins(sdk.NewCoin("ubtsg", govv1beta1.DefaultMinDepositTokens)),
// govv1beta1.DefaultPeriod,
// ),
// govtypes.NewVotingParams(govtypes.DefaultPeriod),
// govtypes.NewTallyParams(govtypes.DefaultQuorum, govtypes.DefaultThreshold, govtypes.DefaultVetoThreshold),
// govv1beta1.NewVotingParams(govv1beta1.DefaultPeriod),
// govv1beta1.NewTallyParams(govv1beta1.DefaultQuorum, govv1beta1.DefaultThreshold, govv1beta1.DefaultVetoThreshold),
// )
// }

Expand Down
4 changes: 2 additions & 2 deletions app/upgrades/v020/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (
)

const (
UpgradeName = "v18"
UpgradeName = "v020"
)

var Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
CreateUpgradeHandler: CreateV182UpgradeHandler,
CreateUpgradeHandler: CreateV020UpgradeHandler,
StoreUpgrades: store.StoreUpgrades{
Added: []string{},
Deleted: []string{},
Expand Down
219 changes: 204 additions & 15 deletions app/upgrades/v020/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package v020
import (
"fmt"

"cosmossdk.io/math"
"github.com/bitsongofficial/go-bitsong/app/keepers"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
)

func CreateV182UpgradeHandler(mm *module.Manager, configurator module.Configurator, k *keepers.AppKeepers) upgradetypes.UpgradeHandler {
func CreateV020UpgradeHandler(mm *module.Manager, configurator module.Configurator, k *keepers.AppKeepers) upgradetypes.UpgradeHandler {
return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
logger := ctx.Logger().With("upgrade", UpgradeName)

Expand All @@ -18,28 +21,97 @@ func CreateV182UpgradeHandler(mm *module.Manager, configurator module.Configurat
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
V0182 UPGRADE manually claims delegation rewards for all users.
This will refresh the delegation information to the upgrade block.
This prevent the error from occuring in the future.
This prevents the error from occuring in the future.
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
`)

// manually claim rewards for all delegators slashed in validator
for _, val := range k.StakingKeeper.GetAllValidators(ctx) {
// get delegations
val := sdk.ValAddress(val.GetOperator())
delegation := k.StakingKeeper.GetValidatorDelegations(ctx, val)
// manually claim rewards by calling keeper functions
for _, validator := range k.StakingKeeper.GetAllValidators(ctx) {
for _, del := range k.StakingKeeper.GetValidatorDelegations(ctx, validator.GetOperator()) {
valAddr := del.GetValidatorAddr()
val := k.StakingKeeper.Validator(ctx, valAddr)

for _, del := range delegation {
k.DistrKeeper.WithdrawDelegationRewards(ctx, del.GetDelegatorAddr(), val)
// check existence of delegator starting info
if !k.DistrKeeper.HasDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr()) {
return nil, distrtypes.ErrEmptyDelegationDistInfo
}

// end current period and calculate rewards
endingPeriod := k.DistrKeeper.IncrementValidatorPeriod(ctx, val)
rewardsRaw := customCalculateDelegationRewards(ctx, k, val, del, endingPeriod)
outstanding := k.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, del.GetValidatorAddr())

// defensive edge case may happen on the very final digits
// of the decCoins due to operation order of the distribution mechanism.
rewards := rewardsRaw.Intersect(outstanding)
if !rewards.IsEqual(rewardsRaw) {
logger := k.DistrKeeper.Logger(ctx)
logger.Info(
"rounding error withdrawing rewards from validator",
"delegator", del.GetDelegatorAddr().String(),
"validator", val.GetOperator().String(),
"got", rewards.String(),
"expected", rewardsRaw.String(),
)
}

// truncate reward dec coins, return remainder to community pool
finalRewards, remainder := rewards.TruncateDecimal()

// add coins to user account
if !finalRewards.IsZero() {
withdrawAddr := k.DistrKeeper.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr())
err := k.BankKeeper.SendCoinsFromModuleToAccount(ctx, distrtypes.ModuleName, withdrawAddr, finalRewards)
if err != nil {
return nil, err
}
}

// update the outstanding rewards and the community pool only if the
// transaction was successful
k.DistrKeeper.SetValidatorOutstandingRewards(ctx, del.GetValidatorAddr(), distrtypes.ValidatorOutstandingRewards{Rewards: outstanding.Sub(rewards)})
feePool := k.DistrKeeper.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(remainder...)
k.DistrKeeper.SetFeePool(ctx, feePool)

// decrement reference count of starting period
startingInfo := k.DistrKeeper.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
startingPeriod := startingInfo.PreviousPeriod
customDecrementReferenceCount(ctx, k, del.GetValidatorAddr(), startingPeriod)

// remove delegator starting info
k.DistrKeeper.DeleteDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())

if finalRewards.IsZero() {
baseDenom, _ := sdk.GetBaseDenom()
if baseDenom == "" {
baseDenom = sdk.DefaultBondDenom
}

// Note, we do not call the NewCoins constructor as we do not want the zero
// coin removed.
finalRewards = sdk.Coins{sdk.NewCoin(baseDenom, math.ZeroInt())}
}

// reinitialize the delegation
// period has already been incremented - we want to store the period ended by this delegation action
previousPeriod := k.DistrKeeper.GetValidatorCurrentRewards(ctx, valAddr).Period - 1

// increment reference count for the period we're going to track
incrementReferenceCount(ctx, k, valAddr, previousPeriod)
validator := k.StakingKeeper.Validator(ctx, valAddr)
delegation := k.StakingKeeper.Delegation(ctx, sdk.AccAddress(del.DelegatorAddress), valAddr)

stake := validator.TokensFromSharesTruncated(delegation.GetShares())
k.DistrKeeper.SetDelegatorStartingInfo(ctx, valAddr, sdk.AccAddress(del.DelegatorAddress), distrtypes.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight())))
}
}
// confirm calculations work as expected by checking rewards for every delgation.
// This upgrade fails if any delegators still are impacted by v018 upgrade error.

// confirm patch has been applied by querying rewards again for each delegation
for _, del := range k.StakingKeeper.GetAllDelegations(ctx) {
valAddr := del.GetValidatorAddr()
val := k.StakingKeeper.Validator(ctx, valAddr)
// manually claim reward
k.DistrKeeper.WithdrawDelegationRewards(ctx, del.GetDelegatorAddr(), valAddr)
// calculate rewards
k.DistrKeeper.CalculateDelegationRewards(ctx, val, del, uint64(ctx.BlockHeight()))
}
Expand All @@ -48,8 +120,7 @@ func CreateV182UpgradeHandler(mm *module.Manager, configurator module.Configurat
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
Upgrade V018 Patch complete.
All delegation rewards have been claimed.
Nodes are now able to regress upstream to the main cosmos-sdk module :)
All delegation rewards claimed and startingInfo set to this block height
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
`)
Expand All @@ -65,3 +136,121 @@ func CreateV182UpgradeHandler(mm *module.Manager, configurator module.Configurat
return versionMap, err
}
}

func customCalculateDelegationRewards(ctx sdk.Context, k *keepers.AppKeepers, val stakingtypes.ValidatorI, del stakingtypes.DelegationI, endingPeriod uint64) (rewards sdk.DecCoins) {
// fetch starting info for delegation
startingInfo := k.DistrKeeper.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr())
if startingInfo.Height == uint64(ctx.BlockHeight()) {
// started this height, no rewards yet
return
}

startingPeriod := startingInfo.PreviousPeriod
stake := startingInfo.Stake
startingHeight := startingInfo.Height
endingHeight := uint64(ctx.BlockHeight())
if endingHeight > startingHeight {
k.DistrKeeper.IterateValidatorSlashEventsBetween(ctx, del.GetValidatorAddr(), startingHeight, endingHeight,
func(height uint64, event distrtypes.ValidatorSlashEvent) (stop bool) {
endingPeriod := event.ValidatorPeriod
if endingPeriod > startingPeriod {
rewards = rewards.Add(customCalculateDelegationRewardsBetween(ctx, k, val, startingPeriod, endingPeriod, stake)...)
stake = stake.MulTruncate(math.LegacyOneDec().Sub(event.Fraction))
startingPeriod = endingPeriod
}
return false
},
)
}
currentStake := val.TokensFromShares(del.GetShares())

if stake.GT(currentStake) {
marginOfErr := currentStake.Mul(sdk.NewDecWithPrec(12, 3)) // 1.2%
if stake.LTE(currentStake.Add(marginOfErr)) {
stake = currentStake
} else {
// ok := CalculateRewardsForSlashedDelegators(ctx, k, val, del, currentStake, SLASHED_DELEGATORS)
// if ok {
// stake = currentStake
// } else {
// }
panic(fmt.Sprintln("current stake is not delgator from slashed validator, and is more than maximum margin of error"))
}
}
// calculate rewards for final period
rewards = rewards.Add(customCalculateDelegationRewardsBetween(ctx, k, val, startingPeriod, endingPeriod, stake)...)
return rewards
}

func customCalculateDelegationRewardsBetween(ctx sdk.Context, k *keepers.AppKeepers, val stakingtypes.ValidatorI,
startingPeriod, endingPeriod uint64, stake sdk.Dec,
) (rewards sdk.DecCoins) {
// sanity check
if startingPeriod > endingPeriod {
panic("startingPeriod cannot be greater than endingPeriod")
}

// sanity check
if stake.IsNegative() {
panic("stake should not be negative")
}

// return staking * (ending - starting)
starting := k.DistrKeeper.GetValidatorHistoricalRewards(ctx, val.GetOperator(), startingPeriod)
ending := k.DistrKeeper.GetValidatorHistoricalRewards(ctx, val.GetOperator(), endingPeriod)
difference := ending.CumulativeRewardRatio.Sub(starting.CumulativeRewardRatio)
if difference.IsAnyNegative() {
panic("negative rewards should not be possible")
}
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
rewards = difference.MulDecTruncate(stake)
return
}

// decrement the reference count for a historical rewards value, and delete if zero references remain
func customDecrementReferenceCount(ctx sdk.Context, k *keepers.AppKeepers, valAddr sdk.ValAddress, period uint64) {
historical := k.DistrKeeper.GetValidatorHistoricalRewards(ctx, valAddr, period)
if historical.ReferenceCount == 0 {
panic("cannot set negative reference count")
}
historical.ReferenceCount--
if historical.ReferenceCount == 0 {
k.DistrKeeper.DeleteValidatorHistoricalReward(ctx, valAddr, period)
} else {
k.DistrKeeper.SetValidatorHistoricalRewards(ctx, valAddr, period, historical)
}
}

// increment the reference count for a historical rewards value
func incrementReferenceCount(ctx sdk.Context, k *keepers.AppKeepers, valAddr sdk.ValAddress, period uint64) {
historical := k.DistrKeeper.GetValidatorHistoricalRewards(ctx, valAddr, period)
if historical.ReferenceCount > 2 {
panic("reference count should never exceed 2")
}
historical.ReferenceCount++
k.DistrKeeper.SetValidatorHistoricalRewards(ctx, valAddr, period, historical)
}

// func CalculateRewardsForSlashedDelegators(
// ctx sdk.Context,
// k *keepers.AppKeepers,
// val stakingtypes.ValidatorI,
// del stakingtypes.DelegationI,
// currentStake math.LegacyDec,
// list []string,
// ) bool {
// valAddr := del.GetValidatorAddr().String()
// delAddr := del.GetDelegatorAddr().String()
// for _, sv := range SLASHED_VALIDATORS {
// if valAddr == sv {
// return true
// }
// }
// for _, sv := range list {
// if delAddr == sv {
// return true
// }
// }

// return false
// }
1 change: 1 addition & 0 deletions cmd/bitsongd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
PrepareGenesisCmd(bitsong.DefaultNodeHome, bitsong.ModuleBasics),
genesisCommand(encodingConfig),
InitFromStateCmd(bitsong.DefaultNodeHome),
V019(bitsong.DefaultNodeHome),
VerifySlashedDelegatorsV018(bitsong.DefaultNodeHome),
queryCommand(),
txCommand(),
Expand Down
2 changes: 1 addition & 1 deletion cmd/bitsongd/cmd/v18-slash.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func rewardVerification(_ *tmcfg.Config, cliCtx client.Context, genParams V018St
return fmt.Errorf("failed to convert state export: %w", err)
}

fmt.Println("Veification Complete")
fmt.Println("Verification Complete")

return nil
}
Expand Down
Loading
Loading