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

feat: InitChainer auto stakes uniformly to validators at genesis #26

Merged
merged 4 commits into from
Feb 26, 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
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
Loading