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

Patch 2: Fix UnbondingQueueKey #132

Merged
merged 12 commits into from
Apr 2, 2024
152 changes: 152 additions & 0 deletions app/fork_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package app

import (
"fmt"
"testing"
"time"

"cosmossdk.io/math"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

sdk "github.com/cosmos/cosmos-sdk/types"
)

var oneEnternityLater = time.Date(9999, 9, 9, 9, 9, 9, 9, time.UTC)

func TestFork(t *testing.T) {
realio := Setup(false, nil)

ctx := realio.BaseApp.NewContext(false, tmproto.Header{Height: int64(ForkHeight)})
stakingKeeper := realio.StakingKeeper

timeKey := time.Date(2024, 4, 1, 1, 1, 1, 1, time.UTC)

duplicativeUnbondingDelegation := stakingtypes.UnbondingDelegation{
DelegatorAddress: "test_del_1",
ValidatorAddress: "test_val_1",
Entries: []stakingtypes.UnbondingDelegationEntry{
stakingtypes.NewUnbondingDelegationEntry(int64(ForkHeight), timeKey, math.OneInt()),
},
}

stakingKeeper.InsertUBDQueue(ctx, duplicativeUnbondingDelegation, timeKey)
stakingKeeper.InsertUBDQueue(ctx, duplicativeUnbondingDelegation, timeKey)

duplicativeRedelegation := stakingtypes.Redelegation{
DelegatorAddress: "test_del_1",
ValidatorSrcAddress: "test_val_1",
ValidatorDstAddress: "test_val_2",
Entries: []stakingtypes.RedelegationEntry{
stakingtypes.NewRedelegationEntry(int64(ForkHeight), timeKey, math.OneInt(), sdk.OneDec()),
},
}
stakingKeeper.InsertRedelegationQueue(ctx, duplicativeRedelegation, timeKey)
stakingKeeper.InsertRedelegationQueue(ctx, duplicativeRedelegation, timeKey)
stakingKeeper.InsertRedelegationQueue(ctx, duplicativeRedelegation, timeKey)

duplicativeVal := stakingtypes.Validator{
OperatorAddress: "test_op",
UnbondingHeight: int64(ForkHeight),
UnbondingTime: timeKey,
}

stakingKeeper.InsertUnbondingValidatorQueue(ctx, duplicativeVal)
stakingKeeper.InsertUnbondingValidatorQueue(ctx, duplicativeVal)

require.True(t, checkDuplicateUBDQueue(ctx, *realio))
require.True(t, checkDuplicateRelegationQueue(ctx, *realio))
require.True(t, checkDuplicateValQueue(ctx, *realio))

realio.ScheduleForkUpgrade(ctx)

require.False(t, checkDuplicateUBDQueue(ctx, *realio))
require.False(t, checkDuplicateRelegationQueue(ctx, *realio))
require.False(t, checkDuplicateValQueue(ctx, *realio))

dvPairs := stakingKeeper.GetUBDQueueTimeSlice(ctx, timeKey)
require.Equal(t, dvPairs[0].DelegatorAddress, duplicativeUnbondingDelegation.DelegatorAddress)
require.Equal(t, dvPairs[0].ValidatorAddress, duplicativeUnbondingDelegation.ValidatorAddress)

triplets := stakingKeeper.GetRedelegationQueueTimeSlice(ctx, timeKey)
require.Equal(t, triplets[0].DelegatorAddress, duplicativeRedelegation.DelegatorAddress)
require.Equal(t, triplets[0].ValidatorDstAddress, duplicativeRedelegation.ValidatorDstAddress)
require.Equal(t, triplets[0].ValidatorSrcAddress, duplicativeRedelegation.ValidatorSrcAddress)

vals := stakingKeeper.GetUnbondingValidators(ctx, timeKey, int64(ForkHeight))
require.Equal(t, vals[0], duplicativeVal.OperatorAddress)

Check failure on line 79 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
}

func checkDuplicateUBDQueue(ctx sdk.Context, realio RealioNetwork) bool {
ubdIter := realio.StakingKeeper.UBDQueueIterator(ctx, oneEnternityLater)
defer ubdIter.Close()

for ; ubdIter.Valid(); ubdIter.Next() {
timeslice := stakingtypes.DVPairs{}
value := ubdIter.Value()
realio.appCodec.MustUnmarshal(value, &timeslice)
if checkDuplicateUBD(timeslice.Pairs) {
return true
}
}
return false
}

func checkDuplicateUBD(eles []stakingtypes.DVPair) bool {

Check failure on line 97 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

`eles` is a misspelling of `eels` (misspell)
unique_eles := map[string]bool{}

Check failure on line 98 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

`eles` is a misspelling of `eels` (misspell)
for _, ele := range eles {

Check failure on line 99 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

`eles` is a misspelling of `eels` (misspell)
unique_eles[ele.String()] = true

Check failure on line 100 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

`eles` is a misspelling of `eels` (misspell)
}
fmt.Println(eles, "eles")

Check failure on line 102 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

`eles` is a misspelling of `eels` (misspell)

return len(unique_eles) != len(eles)

Check failure on line 104 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

`eles` is a misspelling of `eels` (misspell)
}

func checkDuplicateRelegationQueue(ctx sdk.Context, realio RealioNetwork) bool {
redeIter := realio.StakingKeeper.RedelegationQueueIterator(ctx, oneEnternityLater)
defer redeIter.Close()

for ; redeIter.Valid(); redeIter.Next() {
timeslice := stakingtypes.DVVTriplets{}
value := redeIter.Value()
realio.appCodec.MustUnmarshal(value, &timeslice)
if checkDuplicateRedelegation(timeslice.Triplets) {
return true
}
}
return false
}

func checkDuplicateRedelegation(eles []stakingtypes.DVVTriplet) bool {
unique_eles := map[string]bool{}
for _, ele := range eles {
unique_eles[ele.String()] = true
}

return len(unique_eles) != len(eles)
}

func checkDuplicateValQueue(ctx sdk.Context, realio RealioNetwork) bool {
valsIter := realio.StakingKeeper.ValidatorQueueIterator(ctx, oneEnternityLater, 9999)
defer valsIter.Close()

for ; valsIter.Valid(); valsIter.Next() {
timeslice := stakingtypes.ValAddresses{}
value := valsIter.Value()
realio.appCodec.MustUnmarshal(value, &timeslice)
if checkDuplicateValAddr(timeslice.Addresses) {
return true
}
}
return false
}

Check failure on line 144 in app/fork_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
func checkDuplicateValAddr(eles []string) bool {
unique_eles := map[string]bool{}
for _, ele := range eles {
unique_eles[ele] = true
}

return len(unique_eles) != len(eles)
}
130 changes: 129 additions & 1 deletion app/forks.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package app

import (
"sort"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

var ForkHeight = 5989487

// ScheduleForkUpgrade executes any necessary fork logic for based upon the current
// block height and chain ID (mainnet or testnet). It sets an upgrade plan once
// the chain reaches the pre-defined upgrade height.
Expand All @@ -12,7 +18,14 @@
//
// 1. Release a non-breaking patch version so that the chain can set the scheduled upgrade plan at upgrade-height.
// 2. Release the software defined in the upgrade-info
func (app *RealioNetwork) ScheduleForkUpgrade(_ sdk.Context) {
func (app *RealioNetwork) ScheduleForkUpgrade(ctx sdk.Context) {
if ctx.BlockHeight() == 5989487 {

// remove duplicate UnbondingQueueKey
removeDuplicateValueUnbondingQueueKey(app, ctx)
removeDuplicateValueRedelegationQueueKey(app, ctx)
removeDuplicateUnbondingValidator(app, ctx)
}
// NOTE: there are no testnet forks for the existing versions
// if !types.IsMainnet(ctx.ChainID()) {
// return
Expand Down Expand Up @@ -43,3 +56,118 @@
// )
//}
}

func removeDuplicateValueRedelegationQueueKey(app *RealioNetwork, ctx sdk.Context) {
// Get Staking keeper, codec and staking store
sk := app.StakingKeeper
cdc := app.AppCodec()
store := ctx.KVStore(app.keys[stakingtypes.ModuleName])

// remove duplicate UnbondingQueueKey
ubdTime := sk.UnbondingTime(ctx)
currTime := ctx.BlockTime()

redelegationTimesliceIterator := sk.RedelegationQueueIterator(ctx, currTime.Add(ubdTime)) // make sure to iterate all queue
defer redelegationTimesliceIterator.Close()

for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() {
timeslice := stakingtypes.DVVTriplets{}
value := redelegationTimesliceIterator.Value()
cdc.MustUnmarshal(value, &timeslice)

triplets := removeDuplicateDVVTriplets(timeslice.Triplets)
bz := cdc.MustMarshal(&stakingtypes.DVVTriplets{Triplets: triplets})

store.Set(redelegationTimesliceIterator.Key(), bz)
}

Check failure on line 83 in app/forks.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
}

func removeDuplicateDVVTriplets(triplets []stakingtypes.DVVTriplet) []stakingtypes.DVVTriplet {
var list []stakingtypes.DVVTriplet
for _, item := range triplets {
if !containsDVVTriplets(list, item) {
list = append(list, item)
}
}
return list
}

func containsDVVTriplets(s []stakingtypes.DVVTriplet, e stakingtypes.DVVTriplet) bool {
for _, a := range s {
if a.DelegatorAddress == e.DelegatorAddress &&
a.ValidatorSrcAddress == e.ValidatorSrcAddress &&
a.ValidatorDstAddress == e.ValidatorDstAddress {
return true
}
}
return false
}

func removeDuplicateUnbondingValidator(app *RealioNetwork, ctx sdk.Context) {
valIter := app.StakingKeeper.ValidatorQueueIterator(ctx, time.Date(9999, 9, 9, 9, 9, 9, 9, time.UTC), 99999999999999)
defer valIter.Close()

for ; valIter.Valid(); valIter.Next() {
addrs := stakingtypes.ValAddresses{}
app.appCodec.MustUnmarshal(valIter.Value(), &addrs)

vals := map[string]bool{}
for _, valAddr := range addrs.Addresses {
vals[valAddr] = true
}

unique_addrs := []string{}

Check warning on line 120 in app/forks.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; var unique_addrs should be uniqueAddrs (revive)
for valAddr, _ := range vals {

Check failure on line 121 in app/forks.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
unique_addrs = append(unique_addrs, valAddr)
}
sort.Strings(unique_addrs)

ctx.KVStore(app.GetKey(stakingtypes.StoreKey)).Set(valIter.Key(), app.appCodec.MustMarshal(&stakingtypes.ValAddresses{Addresses: unique_addrs}))
}
}

func removeDuplicateValueUnbondingQueueKey(app *RealioNetwork, ctx sdk.Context) {
// Get Staking keeper, codec and staking store
sk := app.StakingKeeper
cdc := app.AppCodec()
store := ctx.KVStore(app.keys[stakingtypes.ModuleName])

// remove duplicate UnbondingQueueKey
ubdTime := sk.UnbondingTime(ctx)
currTime := ctx.BlockTime()

unbondingTimesliceIterator := sk.UBDQueueIterator(ctx, currTime.Add(ubdTime)) // make sure to iterate all queue
defer unbondingTimesliceIterator.Close()

for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() {
timeslice := stakingtypes.DVPairs{}
value := unbondingTimesliceIterator.Value()
cdc.MustUnmarshal(value, &timeslice)

dvPairs := removeDuplicatesDVPairs(timeslice.Pairs)
bz := cdc.MustMarshal(&stakingtypes.DVPairs{Pairs: dvPairs})

store.Set(unbondingTimesliceIterator.Key(), bz)
}
}

func removeDuplicatesDVPairs(dvPairs []stakingtypes.DVPair) []stakingtypes.DVPair {
var list []stakingtypes.DVPair
for _, item := range dvPairs {
if !containsDVPairs(list, item) {
list = append(list, item)
}
}
return list
}

func containsDVPairs(s []stakingtypes.DVPair, e stakingtypes.DVPair) bool {
for _, a := range s {
if a.DelegatorAddress == e.DelegatorAddress &&
a.ValidatorAddress == e.ValidatorAddress {
return true
}
}
return false
}
Loading