Skip to content

Commit

Permalink
feat: InitChainer auto stakes uniformly to validators at genesis (#26)
Browse files Browse the repository at this point in the history
Stake automatically 50% of balance for accounts that have more than 25
$GOVGEN at genesis initialization. The resulting stake distribution will
provide approximately the same voting power to all genesis validators.
Accounts will automatically stake to 5 validators if balance is less
than 500 $GOVGEN, 10 validators if balance is less than 10000 $GOVGEN
and 20 validators if more, uniformly.

---------

Co-authored-by: Giuseppe Natale <[email protected]>
  • Loading branch information
tbruyelle and giunatale authored Feb 26, 2024
1 parent 7837875 commit 3c40c31
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@
([#13](https://github.com/atomone-hub/govgen/pull/13)).
* Adapt voting period to proposal type.
([#16](https://github.com/atomone-hub/govgen/pull/16)).
* `InitChainer` auto stakes uniformly to validators at genesis
([#26](https://github.com/atomone-hub/govgen/pull/26)).

### STATE BREAKING
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ The following modifications have been made to the Cosmos Hub software to create
7. Removed ability for validators to vote on proposals with delegations, they can only use their own stake
8. Removed community spend proposal
9. Allowed setting different voting periods for different proposal types
10. Stake automatically 50% of balance for accounts that have more than 25 $GOVGEN at genesis initialization. The resulting stake distribution will provide approximately the same voting power to all genesis validators. Accounts will automatically stake to 5 validators if balance is less than 500 $GOVGEN, 10 validators if balance is less than 10000 $GOVGEN and 20 validators if more, uniformly.

85 changes: 84 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"os"
"path/filepath"
"sort"

"github.com/gorilla/mux"
"github.com/rakyll/statik/fs"
Expand All @@ -15,6 +16,7 @@ import (
"github.com/tendermint/tendermint/libs/log"
tmos "github.com/tendermint/tendermint/libs/os"
dbm "github.com/tendermint/tm-db"
"golang.org/x/exp/slices"

// unnamed import of statik for swagger UI support
_ "github.com/cosmos/cosmos-sdk/client/docs/statik"
Expand All @@ -37,6 +39,7 @@ import (
authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/crisis"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
Expand Down Expand Up @@ -241,7 +244,87 @@ func (app *GovGenApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) ab

app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap())

return app.mm.InitGenesis(ctx, app.appCodec, genesisState)
res := app.mm.InitGenesis(ctx, app.appCodec, genesisState)

// auto stake genesis accounts
app.setInitialStakingDistribution(ctx, genesisState)

return res
}

// setInitialStakingDistribution auto stakes genesis accounts in a fairly
// distributed manner.
func (app *GovGenApp) setInitialStakingDistribution(ctx sdk.Context, genesisState GenesisState) {
var bankState banktypes.GenesisState
app.appCodec.MustUnmarshalJSON(genesisState[banktypes.ModuleName], &bankState)
// Sort balances in descending order
sort.Slice(bankState.Balances, func(i, j int) bool {
return bankState.Balances[i].Coins.IsAllGT(bankState.Balances[j].Coins)
})

var (
minTokens = sdk.NewInt(25_000_000)
powerReduction = app.StakingKeeper.PowerReduction(ctx)
)
// Extend validator to track delegations
type validator struct {
stakingtypes.Validator
totalDelegations int64
}
var validators []*validator
for _, val := range app.StakingKeeper.GetAllValidators(ctx) {
validators = append(validators, &validator{
Validator: val,
})
}
if len(validators) == 0 {
return
}
for _, balance := range bankState.Balances {
tokens := balance.Coins.AmountOf("ugovgen")
if tokens.LTE(minTokens) {
// Don't stake when tokens <= minToken
continue
}
// Take 50% of the balance for staking
stake := tokens.QuoRaw(2)

// Determine the number of validators that will receive a delegation
var splitStake sdk.Int
switch {
case stake.LT(sdk.NewInt(500_000_000)):
splitStake = stake.QuoRaw(5)
case stake.LT(sdk.NewInt(10_000_000_000)):
splitStake = stake.QuoRaw(10)
default:
splitStake = stake.QuoRaw(20)
}

// Delegation loop for each selected validator
for ; stake.GTE(powerReduction); stake = stake.Sub(splitStake) {
bondAmt := sdk.MinInt(stake, splitStake)
// Delegate to validator which has the less delegations
validator := slices.MinFunc(validators, func(val1, val2 *validator) int {
return int(val1.totalDelegations - val2.totalDelegations)
})
if _, err := app.StakingKeeper.Delegate(
ctx,
balance.GetAddress(),
bondAmt,
stakingtypes.Unbonded,
validator.Validator,
true,
); err != nil {
panic(err)
}

// track delegation for the sake of the algorithm
validator.totalDelegations += bondAmt.Int64()

// reload validator to avoid power index problem
validator.Validator, _ = app.StakingKeeper.GetValidator(ctx, validator.GetOperator())
}
}
}

// LoadHeight loads a particular height
Expand Down
3 changes: 2 additions & 1 deletion x/gov/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ func TestItCreatesModuleAccountOnInitBlock(t *testing.T) {

app.InitChain(
abcitypes.RequestInitChain{
AppStateBytes: []byte("{}"),
// bank module must be present because of app.setInitialStakingDistribution
AppStateBytes: []byte(`{"bank":{}}`),
ChainId: "test-chain-id",
},
)
Expand Down

0 comments on commit 3c40c31

Please sign in to comment.