genesis: add support for arbitrary initial height (#5191)

Adds a genesis parameter `initial_height` which specifies the initial block height, as well as ABCI `RequestInitChain.InitialHeight` to pass it to the ABCI application, and `State.InitialHeight` to keep track of the initial height throughout the code. Fixes #2543, based on [RFC-002](https://github.com/tendermint/spec/pull/119). Spec changes in https://github.com/tendermint/spec/pull/135.
This commit is contained in:
Erik Grinaker
2020-08-11 19:03:28 +02:00
committed by GitHub
parent 08ffe13295
commit cc247c091b
28 changed files with 555 additions and 317 deletions

View File

@@ -555,27 +555,33 @@ func (cs *State) updateToState(state sm.State) {
panic(fmt.Sprintf("updateToState() expected state height of %v but found %v",
cs.Height, state.LastBlockHeight))
}
if !cs.state.IsEmpty() && cs.state.LastBlockHeight+1 != cs.Height {
// This might happen when someone else is mutating cs.state.
// Someone forgot to pass in state.Copy() somewhere?!
panic(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v",
cs.state.LastBlockHeight+1, cs.Height))
}
if !cs.state.IsEmpty() {
if cs.state.LastBlockHeight > 0 && cs.state.LastBlockHeight+1 != cs.Height {
// This might happen when someone else is mutating cs.state.
// Someone forgot to pass in state.Copy() somewhere?!
panic(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v",
cs.state.LastBlockHeight+1, cs.Height))
}
if cs.state.LastBlockHeight > 0 && cs.Height == cs.state.InitialHeight {
panic(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight %v, expected 0 for initial height %v",
cs.state.LastBlockHeight, cs.state.InitialHeight))
}
// If state isn't further out than cs.state, just ignore.
// This happens when SwitchToConsensus() is called in the reactor.
// We don't want to reset e.g. the Votes, but we still want to
// signal the new round step, because other services (eg. txNotifier)
// depend on having an up-to-date peer state!
if !cs.state.IsEmpty() && (state.LastBlockHeight <= cs.state.LastBlockHeight) {
cs.Logger.Info(
"Ignoring updateToState()",
"newHeight",
state.LastBlockHeight+1,
"oldHeight",
cs.state.LastBlockHeight+1)
cs.newStep()
return
// If state isn't further out than cs.state, just ignore.
// This happens when SwitchToConsensus() is called in the reactor.
// We don't want to reset e.g. the Votes, but we still want to
// signal the new round step, because other services (eg. txNotifier)
// depend on having an up-to-date peer state!
if state.LastBlockHeight <= cs.state.LastBlockHeight {
cs.Logger.Info(
"Ignoring updateToState()",
"newHeight",
state.LastBlockHeight+1,
"oldHeight",
cs.state.LastBlockHeight+1)
cs.newStep()
return
}
}
// Reset fields based on state.
@@ -595,13 +601,16 @@ func (cs *State) updateToState(state sm.State) {
case cs.LastCommit == nil:
// NOTE: when Tendermint starts, it has no votes. reconstructLastCommit
// must be called to reconstruct LastCommit from SeenCommit.
panic(fmt.Sprintf("LastCommit cannot be empty in heights > 1 (H:%d)",
panic(fmt.Sprintf("LastCommit cannot be empty after initial block (H:%d)",
state.LastBlockHeight+1,
))
}
// Next desired block height
height := state.LastBlockHeight + 1
if height == 1 {
height = state.InitialHeight
}
// RoundState fields
cs.updateHeight(height)
@@ -933,7 +942,7 @@ func (cs *State) enterNewRound(height int64, round int32) {
// needProofBlock returns true on the first height (so the genesis app hash is signed right away)
// and where the last block (height-1) caused the app hash to change
func (cs *State) needProofBlock(height int64) bool {
if height == 1 {
if height == cs.state.InitialHeight {
return true
}
@@ -1090,7 +1099,7 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa
var commit *types.Commit
switch {
case cs.Height == 1:
case cs.Height == cs.state.InitialHeight:
// We're creating a proposal for the first block.
// The commit is empty, but not nil.
commit = types.NewCommit(0, 0, types.BlockID{}, nil)
@@ -1607,7 +1616,7 @@ func (cs *State) recordMetrics(height int64, block *types.Block) {
// height=0 -> MissingValidators and MissingValidatorsPower are both 0.
// Remember that the first LastCommit is intentionally empty, so it's not
// fair to increment missing validators number.
if height > 1 {
if height > cs.state.InitialHeight {
// Sanity check that commit size matches validator set size - only applies
// after first block.
var (
@@ -1818,7 +1827,7 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) {
return added, err
}
var timestamp time.Time
if voteErr.VoteA.Height == 1 {
if voteErr.VoteA.Height == cs.state.InitialHeight {
timestamp = cs.state.LastBlockTime // genesis time
} else {
timestamp = sm.MedianTime(cs.LastCommit.MakeCommit(), cs.LastValidators)