Skip to content

Commit

Permalink
V0.20.0 - CI Tests, Improved Scripts, Register ModuleBasic GRPC (#239)
Browse files Browse the repository at this point in the history
* remove versioning

* refresh scripts, makefile, dockerfile,add basic e2e & pfm-e2e

* bump config comments

* add polytone e2e test

* improve genesis codec customization format, remove RandomGenesisAccount from auth.NewAppModule, add wasm tests to e2e-basic,

* slashing cli

* replace cosmos-sdk with bitsong specific distirbution patch fork

* slashsing codec registry

* docker bump, add v018 cosmos-sdk patch

* print results to new file

* bump correct patch release

* correct sdk patch

* patch v18 slashing logic error

* bump sdk patch

* format cw-orchestrator into e2e test repo, add polytone msgs command

* ci bump

* ci bump

* revert token types to v114 in ictest

* coderabbit bumps

* v19 upgrade handler

* delete replace cosmos-sdk, add v019 upgrade handler & logic

* prep v019 cli helper

* change to v20 upgrade

* format to v020 upgrade

* register governance msg service router

* add legacy proposal context to gov proposal route

* add gov legacy router

* v020 upgrade

* remove v020 from v019

* TokensFromSharesTruncated

* bump comments

* Revert "V019"

* remove bin

* remove v019 cli

* bump cosmos-sdk v0.47.15 - ASA-2024-0012 patch

* take into account multiple slashes

* add changelog

---------

Co-authored-by: hard-nett <[email protected]>
  • Loading branch information
hard-nett and hard-nett authored Dec 18, 2024
1 parent 5960e1c commit e90d994
Show file tree
Hide file tree
Showing 16 changed files with 494 additions and 662 deletions.
28 changes: 27 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,35 @@ Ref: https://keepachangelog.com/en/1.0.0/
-->

# Changelog
## [v0.20.0]

## Added
- v020 upgrade handler

## Fixed
- certain delegators inability to claim rewards after v018 patch

## [v0.19.0]
### Added
- new ci tests for wasm,packetforwardmiddleware & polytone (ibc-callbacks)
- Introduced a Makefile for managing Docker-related tasks, including commands for building various Docker images.
- Added functionality for validating CosmWasm contracts in a new testing framework.
- Enhanced configuration capabilities for the Bitsong application with additional parameters.
- Introduced commands for verifying slashed delegators and calculating discrepancies in delegator rewards.
- Added a new Makefile for managing a local testnet environment for the Bitsong blockchain.
- Introduced a new script for automating the downloading of Polyone contract.
- Improved docker commands

### Fixed
- Registered legacy gov msgs
- Register ModuleBasics GrpcGatewayRoutes

### Removed
- versioning of app go module
- removed randomGenesisAccounts as param on new apps auth module registration

## [v0.18.x]
### Features
### Added
- Interchaintest package support added
- New CI support to build & release docker image
- New CI support to run interchain tests
Expand Down
2 changes: 2 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
v015 "github.com/bitsongofficial/go-bitsong/app/upgrades/v015"
v016 "github.com/bitsongofficial/go-bitsong/app/upgrades/v016"
v018 "github.com/bitsongofficial/go-bitsong/app/upgrades/v018"
v020 "github.com/bitsongofficial/go-bitsong/app/upgrades/v020"

errorsmod "cosmossdk.io/errors"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
Expand Down Expand Up @@ -94,6 +95,7 @@ var (
v015.Upgrade,
v016.Upgrade,
v018.Upgrade,
v020.Upgrade,
}
)

Expand Down
3 changes: 1 addition & 2 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func NewAppKeepers(
stakingKeeper, authtypes.FeeCollectorName, govModAddress,
)
appKeepers.SlashingKeeper = slashingkeeper.NewKeeper(
appCodec, &codec.LegacyAmino{}, keys[slashingtypes.StoreKey], stakingKeeper, govModAddress,
appCodec, cdc, keys[slashingtypes.StoreKey], stakingKeeper, govModAddress,
)

invCheckPeriod := cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod))
Expand Down Expand Up @@ -288,7 +288,6 @@ func NewAppKeepers(
govConfig,
govModAddress,
)

// Set legacy router for backwards compatibility with gov v1beta1
appKeepers.GovKeeper.SetLegacyRouter(govRouter)

Expand Down
4 changes: 1 addition & 3 deletions app/test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ func Setup(t *testing.T) *BitsongApp {
type EmptyAppOptions struct{}

// Get implements AppOptions
func (ao EmptyAppOptions) Get(o string) interface{} {
return nil
}
func (ao EmptyAppOptions) Get(o string) interface{} { return nil }

func setup(t *testing.T, withGenesis bool, opts ...wasmkeeper.Option) (*BitsongApp, GenesisState) {
db := dbm.NewMemDB()
Expand Down
19 changes: 19 additions & 0 deletions app/upgrades/v020/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package v020

import (
"github.com/bitsongofficial/go-bitsong/app/upgrades"
store "github.com/cosmos/cosmos-sdk/store/types"
)

const (
UpgradeName = "v020"
)

var Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
CreateUpgradeHandler: CreateV020UpgradeHandler,
StoreUpgrades: store.StoreUpgrades{
Added: []string{},
Deleted: []string{},
},
}
241 changes: 241 additions & 0 deletions app/upgrades/v020/upgrades.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
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 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)
ctx = sdk.UnwrapSDKContext(ctx)
ctx.Logger().Info(`
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
V020 UPGRADE manually claims delegation rewards for all users.
This will refresh the delegation information to the upgrade block.
This prevents the error from occuring in the future.
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
`)

// 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)

// 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())}
ctx.Logger().Info("No final rewards", finalRewards)
}

// 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, del.GetDelegatorAddr(), valAddr)

// calculate delegation stake in tokens
// we don't store directly, so multiply delegation shares * (tokens per share)
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
stake := validator.TokensFromSharesTruncated(delegation.GetShares())

// save new delegator starting info to kv store
k.DistrKeeper.SetDelegatorStartingInfo(ctx, valAddr, del.GetDelegatorAddr(), distrtypes.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight())))
}
}

// // 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)
// calculate rewards
k.DistrKeeper.CalculateDelegationRewards(ctx, val, del, uint64(ctx.BlockHeight()))
}

ctx.Logger().Info(`
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
Upgrade V020 Patch complete.
All delegation rewards claimed and startingInfo set to this block height
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
`)

// Run migrations
logger.Info(fmt.Sprintf("pre migrate version map: %v", vm))
versionMap, err := mm.RunMigrations(ctx, configurator, vm)
if err != nil {
return nil, err
}
logger.Info(fmt.Sprintf("post migrate version map: %v", versionMap))
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)...)
// Note: It is necessary to truncate so we don't allow withdrawing
// more rewards than owed.
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(50, 3)) // 5.0%
if stake.LTE(currentStake.Add(marginOfErr)) {
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)
}

// calculate the token worth of provided shares, truncated
func CustommTokensFromSharesTruncated(t math.Int, ds math.LegacyDec, shares sdk.Dec) math.LegacyDec {
return (shares.MulInt(t)).QuoTruncate(ds)
}
Binary file removed bin/bitsongd
Binary file not shown.
2 changes: 0 additions & 2 deletions cmd/bitsongd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,6 @@ 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(),
keys.Commands(bitsong.DefaultNodeHome),
Expand Down
Loading

0 comments on commit e90d994

Please sign in to comment.