Ensure InitGenesis returns with non-empty validator set #8961
Description
Summary
Currently, the SDK allows InitGenesis
to return with an empty validator set. In practice, the error for an empty validator set gets thrown in tendermint. This error is very common and is unhelpful in figuring out the root of the issue (usually an undelgated validator). The SDK should consider an empty validator set after InitGenesis
to be invalid, as a chain cannot produce a block without a validator set and should then panic.
Problem Definition
Simapp relies on an empty validator set
Proposal
I would like to add this fix in types/module/module.go
:
// InitGenesis performs init genesis functionality for modules
+// InitGenesis performs init genesis functionality for modules. Exactly one
+// module must return a non-empty validator set update to correctly initialize
+// the chain.
func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, genesisData map[string]json.RawMessage) abci.ResponseInitChain {
var validatorUpdates []abci.ValidatorUpdate
for _, moduleName := range m.OrderInitGenesis {
@@ -315,6 +318,11 @@ func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, genesisD
}
}
+ // a chain must initialize with a non-empty validator set
+ if len(validatorUpdates) == 0 {
+ panic(fmt.Sprintf("validator set is empty after InitGenesis, please ensure at least one validator is initialized with a delegation greater than or equal to the PowerReduction (%d)", sdk.PowerReduction))
+ }
+
return abci.ResponseInitChain{
Validators: validatorUpdates,
}
But this breaks simapp. Specifically the default simapp runs without a validator set and it uses InitGenesis
. I'd like simapp to use a single validator by default. I did a lot of the heavy lifting several months ago by adding SetupWithGenesisValSet
function.
We would need to add this into the Setup
function of simapp:
// generate validator private/public key
privVal := mock.NewPV()
pubKey, err := privVal.GetPubKey()
require.NoError(t, err)
// create validator set with single validator
validator := tmtypes.NewValidator(pubKey, 1)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
// generate genesis account
senderPrivKey := secp256k1.GenPrivKey()
acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0)
balance := banktypes.Balance{
Address: acc.GetAddress().String(),
Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))),
}
app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance)
NOTE: mock
is a package in ibc-go
which mocks a simple tendermint validator. It is very straightforward and we could port it over to the SDK
I did a quick check locally to see how troublesome this change is, and several tests either:
- rely on empty validator set (looks like grpc query functions)
- attempt to do their own initialize (not via
Setup
)
After fixing Setup
, all other tests doing their on simapp setup should be switched to use the Setup
function unless they have a very good reason for not using it
There are arguments to be made that we should keep simapp as simple as possible since most of these tests are for unit tests
For Admin Use
- Not duplicate issue
- Appropriate labels applied
- Appropriate contributors tagged
- Contributor assigned/self-assigned
Activity