github.com/okex/exchain@v1.8.0/libs/tendermint/state/store.go (about)

     1  package state
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/tendermint/go-amino"
     9  
    10  	abci "github.com/okex/exchain/libs/tendermint/abci/types"
    11  	tmmath "github.com/okex/exchain/libs/tendermint/libs/math"
    12  	tmos "github.com/okex/exchain/libs/tendermint/libs/os"
    13  	"github.com/okex/exchain/libs/tendermint/types"
    14  	dbm "github.com/okex/exchain/libs/tm-db"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  const (
    19  	// persist validators every valSetCheckpointInterval blocks to avoid
    20  	// LoadValidators taking too much time.
    21  	// https://github.com/tendermint/tendermint/pull/3438
    22  	// 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators)
    23  	valSetCheckpointInterval = 100000
    24  )
    25  
    26  //------------------------------------------------------------------------
    27  
    28  func calcValidatorsKey(height int64) []byte {
    29  	return []byte(fmt.Sprintf("validatorsKey:%v", height))
    30  }
    31  
    32  func calcConsensusParamsKey(height int64) []byte {
    33  	return []byte(fmt.Sprintf("consensusParamsKey:%v", height))
    34  }
    35  
    36  func calcABCIResponsesKey(height int64) []byte {
    37  	return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
    38  }
    39  
    40  // LoadStateFromDBOrGenesisFile loads the most recent state from the database,
    41  // or creates a new one from the given genesisFilePath and persists the result
    42  // to the database.
    43  func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State, error) {
    44  	state := LoadState(stateDB)
    45  	if state.IsEmpty() {
    46  		var err error
    47  		state, err = MakeGenesisStateFromFile(genesisFilePath)
    48  		if err != nil {
    49  			return state, err
    50  		}
    51  		SaveState(stateDB, state)
    52  	}
    53  
    54  	return state, nil
    55  }
    56  
    57  // LoadStateFromDBOrGenesisDoc loads the most recent state from the database,
    58  // or creates a new one from the given genesisDoc and persists the result
    59  // to the database.
    60  func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) {
    61  	state := LoadState(stateDB)
    62  	if state.IsEmpty() {
    63  		var err error
    64  		state, err = MakeGenesisState(genesisDoc)
    65  		if err != nil {
    66  			return state, err
    67  		}
    68  		SaveState(stateDB, state)
    69  	}
    70  
    71  	return state, nil
    72  }
    73  
    74  // LoadState loads the State from the database.
    75  func LoadState(db dbm.DB) State {
    76  	return loadState(db, stateKey)
    77  }
    78  
    79  func loadState(db dbm.DB, key []byte) (state State) {
    80  	buf, err := db.Get(key)
    81  	if err != nil {
    82  		panic(err)
    83  	}
    84  	if len(buf) == 0 {
    85  		return state
    86  	}
    87  
    88  	err = cdc.UnmarshalBinaryBare(buf, &state)
    89  	if err != nil {
    90  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
    91  		tmos.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed:
    92                  %v\n`, err))
    93  	}
    94  	// TODO: ensure that buf is completely read.
    95  
    96  	return state
    97  }
    98  
    99  // SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
   100  // This flushes the writes (e.g. calls SetSync).
   101  func SaveState(db dbm.DB, state State) {
   102  	saveState(db, state, stateKey)
   103  }
   104  
   105  func saveState(db dbm.DB, state State, key []byte) {
   106  	nextHeight := state.LastBlockHeight + 1
   107  	// If first block, save validators for block 1.
   108  	if nextHeight == types.GetStartBlockHeight()+1 {
   109  		// This extra logic due to Tendermint validator set changes being delayed 1 block.
   110  		// It may get overwritten due to InitChain validator updates.
   111  		lastHeightVoteChanged := types.GetStartBlockHeight() + 1
   112  		saveValidatorsInfo(db, nextHeight, lastHeightVoteChanged, state.Validators)
   113  	}
   114  	// Save next validators.
   115  	saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
   116  	// Save next consensus params.
   117  	saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
   118  	db.SetSync(key, state.Bytes())
   119  }
   120  
   121  //------------------------------------------------------------------------
   122  
   123  // ABCIResponses retains the responses
   124  // of the various ABCI calls during block processing.
   125  // It is persisted to disk for each height before calling Commit.
   126  type ABCIResponses struct {
   127  	DeliverTxs []*abci.ResponseDeliverTx `json:"deliver_txs"`
   128  	EndBlock   *abci.ResponseEndBlock    `json:"end_block"`
   129  	BeginBlock *abci.ResponseBeginBlock  `json:"begin_block"`
   130  }
   131  
   132  func (arz ABCIResponses) AminoSize(cdc *amino.Codec) int {
   133  	size := 0
   134  	for _, tx := range arz.DeliverTxs {
   135  		txSize := tx.AminoSize(cdc)
   136  		size += 1 + amino.UvarintSize(uint64(txSize)) + txSize
   137  	}
   138  	if arz.EndBlock != nil {
   139  		endBlockSize := arz.EndBlock.AminoSize(cdc)
   140  		size += 1 + amino.UvarintSize(uint64(endBlockSize)) + endBlockSize
   141  	}
   142  	if arz.BeginBlock != nil {
   143  		beginBlockSize := arz.BeginBlock.AminoSize(cdc)
   144  		size += 1 + amino.UvarintSize(uint64(beginBlockSize)) + beginBlockSize
   145  	}
   146  	return size
   147  }
   148  
   149  func (arz ABCIResponses) MarshalToAmino(cdc *amino.Codec) ([]byte, error) {
   150  	var buf bytes.Buffer
   151  	buf.Grow(arz.AminoSize(cdc))
   152  	err := arz.MarshalAminoTo(cdc, &buf)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	return buf.Bytes(), nil
   157  }
   158  
   159  func (arz ABCIResponses) MarshalAminoTo(cdc *amino.Codec, buf *bytes.Buffer) error {
   160  	var err error
   161  	// field 1
   162  	for i := 0; i < len(arz.DeliverTxs); i++ {
   163  		const pbKey = 1<<3 | 2
   164  		buf.WriteByte(pbKey)
   165  		txSize := arz.DeliverTxs[i].AminoSize(cdc)
   166  		err = amino.EncodeUvarintToBuffer(buf, uint64(txSize))
   167  		if err != nil {
   168  			return err
   169  		}
   170  		lenBeforeData := buf.Len()
   171  		err = arz.DeliverTxs[i].MarshalAminoTo(cdc, buf)
   172  		if err != nil {
   173  			return err
   174  		}
   175  		if buf.Len()-lenBeforeData != txSize {
   176  			return amino.NewSizerError(arz.DeliverTxs[i], buf.Len()-lenBeforeData, txSize)
   177  		}
   178  	}
   179  	// field 2
   180  	if arz.EndBlock != nil {
   181  		const pbKey = 2<<3 | 2
   182  		buf.WriteByte(pbKey)
   183  		endBlockSize := arz.EndBlock.AminoSize(cdc)
   184  		err = amino.EncodeUvarintToBuffer(buf, uint64(endBlockSize))
   185  		if err != nil {
   186  			return err
   187  		}
   188  		lenBeforeData := buf.Len()
   189  		err = arz.EndBlock.MarshalAminoTo(cdc, buf)
   190  		if err != nil {
   191  			return err
   192  		}
   193  		if buf.Len()-lenBeforeData != endBlockSize {
   194  			return amino.NewSizerError(arz.EndBlock, buf.Len()-lenBeforeData, endBlockSize)
   195  		}
   196  	}
   197  	// field 3
   198  	if arz.BeginBlock != nil {
   199  		const pbKey = 3<<3 | 2
   200  		buf.WriteByte(pbKey)
   201  		beginBlockSize := arz.BeginBlock.AminoSize(cdc)
   202  		err = amino.EncodeUvarintToBuffer(buf, uint64(beginBlockSize))
   203  		if err != nil {
   204  			return err
   205  		}
   206  		lenBeforeData := buf.Len()
   207  		err = arz.BeginBlock.MarshalAminoTo(cdc, buf)
   208  		if err != nil {
   209  			return err
   210  		}
   211  		if buf.Len()-lenBeforeData != beginBlockSize {
   212  			return amino.NewSizerError(arz.BeginBlock, buf.Len()-lenBeforeData, beginBlockSize)
   213  		}
   214  	}
   215  
   216  	return nil
   217  }
   218  
   219  // UnmarshalFromAmino unmarshal data from amino bytes.
   220  func (arz *ABCIResponses) UnmarshalFromAmino(cdc *amino.Codec, data []byte) error {
   221  	var dataLen uint64 = 0
   222  	var subData []byte
   223  
   224  	for {
   225  		data = data[dataLen:]
   226  		if len(data) == 0 {
   227  			break
   228  		}
   229  		pos, pbType, err := amino.ParseProtoPosAndTypeMustOneByte(data[0])
   230  		if err != nil {
   231  			return err
   232  		}
   233  		data = data[1:]
   234  
   235  		if pbType == amino.Typ3_ByteLength {
   236  			var n int
   237  			dataLen, n, _ = amino.DecodeUvarint(data)
   238  
   239  			data = data[n:]
   240  			if len(data) < int(dataLen) {
   241  				return errors.New("not enough data")
   242  			}
   243  			subData = data[:dataLen]
   244  		}
   245  
   246  		switch pos {
   247  		case 1:
   248  			var resDeliverTx *abci.ResponseDeliverTx = nil
   249  			if len(subData) != 0 {
   250  				resDeliverTx = &abci.ResponseDeliverTx{}
   251  				err := resDeliverTx.UnmarshalFromAmino(cdc, subData)
   252  				if err != nil {
   253  					return err
   254  				}
   255  			}
   256  			arz.DeliverTxs = append(arz.DeliverTxs, resDeliverTx)
   257  
   258  		case 2:
   259  			eBlock := &abci.ResponseEndBlock{}
   260  			if len(subData) != 0 {
   261  				err := eBlock.UnmarshalFromAmino(cdc, subData)
   262  				if err != nil {
   263  					return err
   264  				}
   265  			}
   266  			arz.EndBlock = eBlock
   267  
   268  		case 3:
   269  			bBlock := &abci.ResponseBeginBlock{}
   270  			if len(subData) != 0 {
   271  				err := bBlock.UnmarshalFromAmino(cdc, subData)
   272  				if err != nil {
   273  					return err
   274  				}
   275  			}
   276  			arz.BeginBlock = bBlock
   277  
   278  		default:
   279  			return fmt.Errorf("unexpect feild num %d", pos)
   280  		}
   281  	}
   282  	return nil
   283  }
   284  
   285  // PruneStates deletes states between the given heights (including from, excluding to). It is not
   286  // guaranteed to delete all states, since the last checkpointed state and states being pointed to by
   287  // e.g. `LastHeightChanged` must remain. The state at to must also exist.
   288  //
   289  // The from parameter is necessary since we can't do a key scan in a performant way due to the key
   290  // encoding not preserving ordering: https://github.com/tendermint/tendermint/issues/4567
   291  // This will cause some old states to be left behind when doing incremental partial prunes,
   292  // specifically older checkpoints and LastHeightChanged targets.
   293  func PruneStates(db dbm.DB, from int64, to int64) error {
   294  	if from <= 0 || to <= 0 {
   295  		return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to)
   296  	}
   297  	if from >= to {
   298  		return fmt.Errorf("from height %v must be lower than to height %v", from, to)
   299  	}
   300  	valInfo := loadValidatorsInfo(db, to)
   301  	if valInfo == nil {
   302  		return fmt.Errorf("validators at height %v not found", to)
   303  	}
   304  	paramsInfo := loadConsensusParamsInfo(db, to)
   305  	if paramsInfo == nil {
   306  		return fmt.Errorf("consensus params at height %v not found", to)
   307  	}
   308  
   309  	keepVals := make(map[int64]bool)
   310  	if valInfo.ValidatorSet == nil {
   311  		keepVals[valInfo.LastHeightChanged] = true
   312  		keepVals[lastStoredHeightFor(to, valInfo.LastHeightChanged)] = true // keep last checkpoint too
   313  	}
   314  	keepParams := make(map[int64]bool)
   315  	if paramsInfo.ConsensusParams.Equals(&types.ConsensusParams{}) {
   316  		keepParams[paramsInfo.LastHeightChanged] = true
   317  	}
   318  
   319  	batch := db.NewBatch()
   320  	defer batch.Close()
   321  	pruned := uint64(0)
   322  	var err error
   323  
   324  	// We have to delete in reverse order, to avoid deleting previous heights that have validator
   325  	// sets and consensus params that we may need to retrieve.
   326  	for h := to - 1; h >= from; h-- {
   327  		// For heights we keep, we must make sure they have the full validator set or consensus
   328  		// params, otherwise they will panic if they're retrieved directly (instead of
   329  		// indirectly via a LastHeightChanged pointer).
   330  		if keepVals[h] {
   331  			v := loadValidatorsInfo(db, h)
   332  			if v.ValidatorSet == nil {
   333  				v.ValidatorSet, err = LoadValidators(db, h)
   334  				if err != nil {
   335  					return err
   336  				}
   337  				v.LastHeightChanged = h
   338  				batch.Set(calcValidatorsKey(h), v.Bytes())
   339  			}
   340  		} else {
   341  			batch.Delete(calcValidatorsKey(h))
   342  		}
   343  
   344  		if keepParams[h] {
   345  			p := loadConsensusParamsInfo(db, h)
   346  			if p.ConsensusParams.Equals(&types.ConsensusParams{}) {
   347  				p.ConsensusParams, err = LoadConsensusParams(db, h)
   348  				if err != nil {
   349  					return err
   350  				}
   351  				p.LastHeightChanged = h
   352  				batch.Set(calcConsensusParamsKey(h), p.Bytes())
   353  			}
   354  		} else {
   355  			batch.Delete(calcConsensusParamsKey(h))
   356  		}
   357  
   358  		batch.Delete(calcABCIResponsesKey(h))
   359  		pruned++
   360  
   361  		// avoid batches growing too large by flushing to database regularly
   362  		if pruned%1000 == 0 && pruned > 0 {
   363  			err := batch.Write()
   364  			if err != nil {
   365  				return err
   366  			}
   367  			batch.Close()
   368  			batch = db.NewBatch()
   369  			defer batch.Close()
   370  		}
   371  	}
   372  
   373  	err = batch.WriteSync()
   374  	if err != nil {
   375  		return err
   376  	}
   377  
   378  	return nil
   379  }
   380  
   381  // NewABCIResponses returns a new ABCIResponses
   382  func NewABCIResponses(block *types.Block) *ABCIResponses {
   383  	resDeliverTxs := make([]*abci.ResponseDeliverTx, len(block.Data.Txs))
   384  	if len(block.Data.Txs) == 0 {
   385  		// This makes Amino encoding/decoding consistent.
   386  		resDeliverTxs = nil
   387  	}
   388  	return &ABCIResponses{
   389  		DeliverTxs: resDeliverTxs,
   390  	}
   391  }
   392  
   393  // Bytes serializes the ABCIResponse using go-amino.
   394  func (arz *ABCIResponses) Bytes() []byte {
   395  	bz, err := arz.MarshalToAmino(cdc)
   396  	if err != nil {
   397  		return cdc.MustMarshalBinaryBare(arz)
   398  	}
   399  	return bz
   400  }
   401  
   402  func (arz *ABCIResponses) ResultsHash() []byte {
   403  	results := types.NewResults(arz.DeliverTxs)
   404  	return results.Hash()
   405  }
   406  func (arz *ABCIResponses) String() string {
   407  	str := strings.Builder{}
   408  	results := types.NewResults(arz.DeliverTxs)
   409  	for _, v := range results {
   410  		str.WriteString(fmt.Sprintf("code:%d,msg:=%s\n", v.Code, v.Data.String()))
   411  	}
   412  	return str.String()
   413  }
   414  
   415  // LoadABCIResponses loads the ABCIResponses for the given height from the database.
   416  // This is useful for recovering from crashes where we called app.Commit and before we called
   417  // s.Save(). It can also be used to produce Merkle proofs of the result of txs.
   418  func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) {
   419  	buf, err := db.Get(calcABCIResponsesKey(height))
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  	if len(buf) == 0 {
   424  		return nil, ErrNoABCIResponsesForHeight{height}
   425  	}
   426  
   427  	abciResponses := new(ABCIResponses)
   428  	err = cdc.UnmarshalBinaryBare(buf, abciResponses)
   429  	if err != nil {
   430  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   431  		tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has
   432                  changed: %v\n`, err))
   433  	}
   434  	// TODO: ensure that buf is completely read.
   435  
   436  	return abciResponses, nil
   437  }
   438  
   439  // SaveABCIResponses persists the ABCIResponses to the database.
   440  // This is useful in case we crash after app.Commit and before s.Save().
   441  // Responses are indexed by height so they can also be loaded later to produce
   442  // Merkle proofs.
   443  //
   444  // Exposed for testing.
   445  func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
   446  	db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
   447  }
   448  
   449  //-----------------------------------------------------------------------------
   450  
   451  // ValidatorsInfo represents the latest validator set, or the last height it changed
   452  type ValidatorsInfo struct {
   453  	ValidatorSet      *types.ValidatorSet
   454  	LastHeightChanged int64
   455  }
   456  
   457  // Bytes serializes the ValidatorsInfo using go-amino.
   458  func (valInfo *ValidatorsInfo) Bytes() []byte {
   459  	return cdc.MustMarshalBinaryBare(valInfo)
   460  }
   461  
   462  // LoadValidators loads the ValidatorSet for a given height.
   463  // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
   464  func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
   465  	valSet, _, err := LoadValidatorsWithStoredHeight(db, height)
   466  	return valSet, err
   467  }
   468  
   469  // LoadValidators loads the ValidatorSet for a given height. plus the last LastHeightChanged
   470  // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
   471  func LoadValidatorsWithStoredHeight(db dbm.DB, height int64) (*types.ValidatorSet, int64, error) {
   472  	valInfo := loadValidatorsInfo(db, height)
   473  	if valInfo == nil {
   474  		return nil, -1, ErrNoValSetForHeight{height}
   475  	}
   476  	if valInfo.ValidatorSet == nil {
   477  		lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
   478  		valInfo2 := loadValidatorsInfo(db, lastStoredHeight)
   479  		if valInfo2 == nil || valInfo2.ValidatorSet == nil {
   480  			panic(
   481  				fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)",
   482  					lastStoredHeight,
   483  					height,
   484  				),
   485  			)
   486  		}
   487  		valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate
   488  		valInfo = valInfo2
   489  	}
   490  
   491  	return valInfo.ValidatorSet, valInfo.LastHeightChanged, nil
   492  }
   493  
   494  func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
   495  	checkpointHeight := height - height%valSetCheckpointInterval
   496  	return tmmath.MaxInt64(checkpointHeight, lastHeightChanged)
   497  }
   498  
   499  // CONTRACT: Returned ValidatorsInfo can be mutated.
   500  func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
   501  	buf, err := db.Get(calcValidatorsKey(height))
   502  	if err != nil {
   503  		panic(err)
   504  	}
   505  	if len(buf) == 0 {
   506  		return nil
   507  	}
   508  
   509  	v := new(ValidatorsInfo)
   510  	err = cdc.UnmarshalBinaryBare(buf, v)
   511  	if err != nil {
   512  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   513  		tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed:
   514                  %v\n`, err))
   515  	}
   516  	// TODO: ensure that buf is completely read.
   517  
   518  	return v
   519  }
   520  
   521  // saveValidatorsInfo persists the validator set.
   522  //
   523  // `height` is the effective height for which the validator is responsible for
   524  // signing. It should be called from s.Save(), right before the state itself is
   525  // persisted.
   526  func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
   527  	if !IgnoreSmbCheck && lastHeightChanged > height {
   528  		panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
   529  	}
   530  	valInfo := &ValidatorsInfo{
   531  		LastHeightChanged: lastHeightChanged,
   532  	}
   533  	// Only persist validator set if it was updated or checkpoint height (see
   534  	// valSetCheckpointInterval) is reached.
   535  	if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
   536  		valInfo.ValidatorSet = valSet
   537  	}
   538  	db.Set(calcValidatorsKey(height), valInfo.Bytes())
   539  }
   540  
   541  //-----------------------------------------------------------------------------
   542  
   543  // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
   544  type ConsensusParamsInfo struct {
   545  	ConsensusParams   types.ConsensusParams
   546  	LastHeightChanged int64
   547  }
   548  
   549  // Bytes serializes the ConsensusParamsInfo using go-amino.
   550  func (params ConsensusParamsInfo) Bytes() []byte {
   551  	return cdc.MustMarshalBinaryBare(params)
   552  }
   553  
   554  // LoadConsensusParams loads the ConsensusParams for a given height.
   555  func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) {
   556  	empty := types.ConsensusParams{}
   557  
   558  	paramsInfo := loadConsensusParamsInfo(db, height)
   559  	if paramsInfo == nil {
   560  		return empty, ErrNoConsensusParamsForHeight{height}
   561  	}
   562  
   563  	if paramsInfo.ConsensusParams.Equals(&empty) {
   564  		paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
   565  		if paramsInfo2 == nil {
   566  			panic(
   567  				fmt.Sprintf(
   568  					"Couldn't find consensus params at height %d as last changed from height %d",
   569  					paramsInfo.LastHeightChanged,
   570  					height,
   571  				),
   572  			)
   573  		}
   574  		paramsInfo = paramsInfo2
   575  	}
   576  
   577  	return paramsInfo.ConsensusParams, nil
   578  }
   579  
   580  func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo {
   581  	buf, err := db.Get(calcConsensusParamsKey(height))
   582  	if err != nil {
   583  		panic(err)
   584  	}
   585  	if len(buf) == 0 {
   586  		return nil
   587  	}
   588  
   589  	paramsInfo := new(ConsensusParamsInfo)
   590  	err = cdc.UnmarshalBinaryBare(buf, paramsInfo)
   591  	if err != nil {
   592  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   593  		tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed:
   594                  %v\n`, err))
   595  	}
   596  	// TODO: ensure that buf is completely read.
   597  
   598  	return paramsInfo
   599  }
   600  
   601  // saveConsensusParamsInfo persists the consensus params for the next block to disk.
   602  // It should be called from s.Save(), right before the state itself is persisted.
   603  // If the consensus params did not change after processing the latest block,
   604  // only the last height for which they changed is persisted.
   605  func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) {
   606  	paramsInfo := &ConsensusParamsInfo{
   607  		LastHeightChanged: changeHeight,
   608  	}
   609  	if changeHeight == nextHeight {
   610  		paramsInfo.ConsensusParams = params
   611  	}
   612  	db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
   613  }