github.com/Finschia/ostracon@v1.1.5/state/store.go (about)

     1  package state
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/gogo/protobuf/proto"
     8  	abci "github.com/tendermint/tendermint/abci/types"
     9  	tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
    10  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    11  	dbm "github.com/tendermint/tm-db"
    12  
    13  	tmmath "github.com/Finschia/ostracon/libs/math"
    14  	tmos "github.com/Finschia/ostracon/libs/os"
    15  	ocstate "github.com/Finschia/ostracon/proto/ostracon/state"
    16  	"github.com/Finschia/ostracon/types"
    17  )
    18  
    19  const (
    20  	// persist validators every valSetCheckpointInterval blocks to avoid
    21  	// LoadValidators taking too much time.
    22  	// https://github.com/tendermint/tendermint/pull/3438
    23  	// 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators)
    24  	valSetCheckpointInterval = 100000
    25  )
    26  
    27  //------------------------------------------------------------------------
    28  
    29  func calcValidatorsKey(height int64) []byte {
    30  	return []byte(fmt.Sprintf("validatorsKey:%v", height))
    31  }
    32  
    33  func calcProofHashKey(height int64) []byte {
    34  	return []byte(fmt.Sprintf("proofHashKey:%v", height))
    35  }
    36  
    37  func calcConsensusParamsKey(height int64) []byte {
    38  	return []byte(fmt.Sprintf("consensusParamsKey:%v", height))
    39  }
    40  
    41  func calcABCIResponsesKey(height int64) []byte {
    42  	return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
    43  }
    44  
    45  //----------------------
    46  
    47  var (
    48  	lastABCIResponseKey = []byte("lastABCIResponseKey")
    49  )
    50  
    51  //go:generate ../scripts/mockery_generate.sh Store
    52  
    53  // Store defines the state store interface
    54  //
    55  // It is used to retrieve current state and save and load ABCI responses,
    56  // validators and consensus parameters
    57  type Store interface {
    58  	// LoadFromDBOrGenesisFile loads the most recent state.
    59  	// If the chain is new it will use the genesis file from the provided genesis file path as the current state.
    60  	LoadFromDBOrGenesisFile(string) (State, error)
    61  	// LoadFromDBOrGenesisDoc loads the most recent state.
    62  	// If the chain is new it will use the genesis doc as the current state.
    63  	LoadFromDBOrGenesisDoc(*types.GenesisDoc) (State, error)
    64  	// Load loads the current state of the blockchain
    65  	Load() (State, error)
    66  	// LoadValidators loads the validator set at a given height
    67  	LoadValidators(int64) (*types.ValidatorSet, error)
    68  	// LoadProofHash loads the proof hash at a given height
    69  	LoadProofHash(int64) ([]byte, error)
    70  	// LoadABCIResponses loads the abciResponse for a given height
    71  	LoadABCIResponses(int64) (*tmstate.ABCIResponses, error)
    72  	// LoadLastABCIResponse loads the last abciResponse for a given height
    73  	LoadLastABCIResponse(int64) (*tmstate.ABCIResponses, error)
    74  	// LoadConsensusParams loads the consensus params for a given height
    75  	LoadConsensusParams(int64) (tmproto.ConsensusParams, error)
    76  	// Save overwrites the previous state with the updated one
    77  	Save(State) error
    78  	// SaveABCIResponses saves ABCIResponses for a given height
    79  	SaveABCIResponses(int64, *tmstate.ABCIResponses) error
    80  	// Bootstrap is used for bootstrapping state when not starting from a initial height.
    81  	Bootstrap(State) error
    82  	// PruneStates takes the height from which to start prning and which height stop at
    83  	PruneStates(int64, int64) error
    84  	// Close closes the connection with the database
    85  	Close() error
    86  }
    87  
    88  // dbStore wraps a db (github.com/tendermint/tm-db)
    89  type dbStore struct {
    90  	db dbm.DB
    91  
    92  	StoreOptions
    93  }
    94  
    95  type StoreOptions struct {
    96  
    97  	// DiscardABCIResponses determines whether or not the store
    98  	// retains all ABCIResponses. If DiscardABCiResponses is enabled,
    99  	// the store will maintain only the response object from the latest
   100  	// height.
   101  	DiscardABCIResponses bool
   102  }
   103  
   104  var _ Store = (*dbStore)(nil)
   105  
   106  // NewStore creates the dbStore of the state pkg.
   107  func NewStore(db dbm.DB, options StoreOptions) Store {
   108  	return dbStore{db, options}
   109  }
   110  
   111  // LoadFromDBOrGenesisFile loads the most recent state from the database,
   112  // or creates a new one from the given genesisFilePath.
   113  func (store dbStore) LoadFromDBOrGenesisFile(genesisFilePath string) (State, error) {
   114  	state, err := store.Load()
   115  	if err != nil {
   116  		return State{}, err
   117  	}
   118  	if state.IsEmpty() {
   119  		var err error
   120  		state, err = MakeGenesisStateFromFile(genesisFilePath)
   121  		if err != nil {
   122  			return state, err
   123  		}
   124  	}
   125  
   126  	return state, nil
   127  }
   128  
   129  // LoadFromDBOrGenesisDoc loads the most recent state from the database,
   130  // or creates a new one from the given genesisDoc.
   131  func (store dbStore) LoadFromDBOrGenesisDoc(genesisDoc *types.GenesisDoc) (State, error) {
   132  	state, err := store.Load()
   133  	if err != nil {
   134  		return State{}, err
   135  	}
   136  
   137  	if state.IsEmpty() {
   138  		var err error
   139  		state, err = MakeGenesisState(genesisDoc)
   140  		if err != nil {
   141  			return state, err
   142  		}
   143  	}
   144  
   145  	return state, nil
   146  }
   147  
   148  // Load loads the State from the database.
   149  func (store dbStore) Load() (State, error) {
   150  	return store.loadState(stateKey)
   151  }
   152  
   153  func (store dbStore) loadState(key []byte) (state State, err error) {
   154  	buf, err := store.db.Get(key)
   155  	if err != nil {
   156  		return state, err
   157  	}
   158  	if len(buf) == 0 {
   159  		return state, nil
   160  	}
   161  
   162  	sp := new(ocstate.State)
   163  
   164  	err = proto.Unmarshal(buf, sp)
   165  	if err != nil {
   166  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   167  		tmos.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed:
   168  		%v\n`, err))
   169  	}
   170  
   171  	sm, err := FromProto(sp)
   172  	if err != nil {
   173  		return state, err
   174  	}
   175  
   176  	return *sm, nil
   177  }
   178  
   179  // Save persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
   180  // This flushes the writes (e.g. calls SetSync).
   181  func (store dbStore) Save(state State) error {
   182  	return store.save(state, stateKey)
   183  }
   184  
   185  func (store dbStore) save(state State, key []byte) error {
   186  	nextHeight := state.LastBlockHeight + 1
   187  	// If first block, save validators for the block.
   188  	if nextHeight == 1 {
   189  		nextHeight = state.InitialHeight
   190  		// This extra logic due to Ostracon validator set changes being delayed 1 block.
   191  		// It may get overwritten due to InitChain validator updates.
   192  		if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators); err != nil {
   193  			return err
   194  		}
   195  	}
   196  	// Save next validators.
   197  	if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators); err != nil {
   198  		return err
   199  	}
   200  
   201  	// Save current consensus params.
   202  	if err := store.saveConsensusParamsInfo(nextHeight,
   203  		state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil {
   204  		return err
   205  	}
   206  
   207  	// Save current proof hash
   208  	if err := store.saveProofHash(nextHeight, state.LastProofHash); err != nil {
   209  		return err
   210  	}
   211  
   212  	err := store.db.SetSync(key, state.Bytes())
   213  	if err != nil {
   214  		return err
   215  	}
   216  	return nil
   217  }
   218  
   219  // Bootstrap saves a new state, used e.g. by state sync when starting from non-zero height.
   220  func (store dbStore) Bootstrap(state State) error {
   221  	height := state.LastBlockHeight + 1
   222  	if height == 1 {
   223  		height = state.InitialHeight
   224  	}
   225  
   226  	if err := store.saveValidatorsInfo(height, height, state.Validators); err != nil {
   227  		return err
   228  	}
   229  
   230  	if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators); err != nil {
   231  		return err
   232  	}
   233  
   234  	if err := store.saveConsensusParamsInfo(height,
   235  		state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil {
   236  		return err
   237  	}
   238  
   239  	if err := store.saveProofHash(height, state.LastProofHash); err != nil {
   240  		return err
   241  	}
   242  	return store.db.SetSync(stateKey, state.Bytes())
   243  }
   244  
   245  // PruneStates deletes states between the given heights (including from, excluding to). It is not
   246  // guaranteed to delete all states, since the last checkpointed state and states being pointed to by
   247  // e.g. `LastHeightChanged` must remain. The state at to must also exist.
   248  //
   249  // The from parameter is necessary since we can't do a key scan in a performant way due to the key
   250  // encoding not preserving ordering: https://github.com/tendermint/tendermint/issues/4567
   251  // This will cause some old states to be left behind when doing incremental partial prunes,
   252  // specifically older checkpoints and LastHeightChanged targets.
   253  func (store dbStore) PruneStates(from int64, to int64) error {
   254  	if from <= 0 || to <= 0 {
   255  		return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to)
   256  	}
   257  	if from >= to {
   258  		return fmt.Errorf("from height %v must be lower than to height %v", from, to)
   259  	}
   260  	valInfo, err := loadValidatorsInfo(store.db, to)
   261  	if err != nil {
   262  		return fmt.Errorf("validators at height %v not found: %w", to, err)
   263  	}
   264  	paramsInfo, err := store.loadConsensusParamsInfo(to)
   265  	if err != nil {
   266  		return fmt.Errorf("consensus params at height %v not found: %w", to, err)
   267  	}
   268  
   269  	keepVals := make(map[int64]bool)
   270  	if valInfo.ValidatorSet == nil {
   271  		keepVals[valInfo.LastHeightChanged] = true
   272  		keepVals[lastStoredHeightFor(to, valInfo.LastHeightChanged)] = true // keep last checkpoint too
   273  	}
   274  	keepParams := make(map[int64]bool)
   275  	if paramsInfo.ConsensusParams.Equal(&tmproto.ConsensusParams{}) {
   276  		keepParams[paramsInfo.LastHeightChanged] = true
   277  	}
   278  
   279  	batch := store.db.NewBatch()
   280  	defer batch.Close()
   281  	pruned := uint64(0)
   282  
   283  	// We have to delete in reverse order, to avoid deleting previous heights that have validator
   284  	// sets and consensus params that we may need to retrieve.
   285  	for h := to - 1; h >= from; h-- {
   286  		// For heights we keep, we must make sure they have the full validator set or consensus
   287  		// params, otherwise they will panic if they're retrieved directly (instead of
   288  		// indirectly via a LastHeightChanged pointer).
   289  		if keepVals[h] {
   290  			v, err := loadValidatorsInfo(store.db, h)
   291  			if err != nil || v.ValidatorSet == nil {
   292  				vip, err := store.LoadValidators(h)
   293  				if err != nil {
   294  					return err
   295  				}
   296  
   297  				pvi, err := vip.ToProto()
   298  				if err != nil {
   299  					return err
   300  				}
   301  
   302  				v.ValidatorSet = pvi
   303  				v.LastHeightChanged = h
   304  
   305  				bz, err := v.Marshal()
   306  				if err != nil {
   307  					return err
   308  				}
   309  				err = batch.Set(calcValidatorsKey(h), bz)
   310  				if err != nil {
   311  					return err
   312  				}
   313  			}
   314  		} else {
   315  			err = batch.Delete(calcValidatorsKey(h))
   316  			if err != nil {
   317  				return err
   318  			}
   319  			err = batch.Delete(calcProofHashKey(h))
   320  			if err != nil {
   321  				return err
   322  			}
   323  		}
   324  
   325  		if keepParams[h] {
   326  			p, err := store.loadConsensusParamsInfo(h)
   327  			if err != nil {
   328  				return err
   329  			}
   330  
   331  			if p.ConsensusParams.Equal(&tmproto.ConsensusParams{}) {
   332  				p.ConsensusParams, err = store.LoadConsensusParams(h)
   333  				if err != nil {
   334  					return err
   335  				}
   336  
   337  				p.LastHeightChanged = h
   338  				bz, err := p.Marshal()
   339  				if err != nil {
   340  					return err
   341  				}
   342  
   343  				err = batch.Set(calcConsensusParamsKey(h), bz)
   344  				if err != nil {
   345  					return err
   346  				}
   347  			}
   348  		} else {
   349  			err = batch.Delete(calcConsensusParamsKey(h))
   350  			if err != nil {
   351  				return err
   352  			}
   353  		}
   354  
   355  		err = batch.Delete(calcABCIResponsesKey(h))
   356  		if err != nil {
   357  			return err
   358  		}
   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 = store.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  //------------------------------------------------------------------------
   382  
   383  // ABCIResponsesResultsHash returns the root hash of a Merkle tree of
   384  // ResponseDeliverTx responses (see ABCIResults.Hash)
   385  //
   386  // See merkle.SimpleHashFromByteSlices
   387  func ABCIResponsesResultsHash(ar *tmstate.ABCIResponses) []byte {
   388  	return types.NewResults(ar.DeliverTxs).Hash()
   389  }
   390  
   391  // LoadABCIResponses loads the ABCIResponses for the given height from the
   392  // database. If the node has DiscardABCIResponses set to true, ErrABCIResponsesNotPersisted
   393  // is persisted. If not found, ErrNoABCIResponsesForHeight is returned.
   394  func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, error) {
   395  	if store.DiscardABCIResponses {
   396  		return nil, ErrABCIResponsesNotPersisted
   397  	}
   398  
   399  	buf, err := store.db.Get(calcABCIResponsesKey(height))
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  	if len(buf) == 0 {
   404  
   405  		return nil, ErrNoABCIResponsesForHeight{height}
   406  	}
   407  
   408  	abciResponses := new(tmstate.ABCIResponses)
   409  	err = abciResponses.Unmarshal(buf)
   410  	if err != nil {
   411  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   412  		tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has
   413                  changed: %v\n`, err))
   414  	}
   415  	// TODO: ensure that buf is completely read.
   416  
   417  	return abciResponses, nil
   418  }
   419  
   420  // LoadLastABCIResponses loads the ABCIResponses from the most recent height.
   421  // The height parameter is used to ensure that the response corresponds to the latest height.
   422  // If not, an error is returned.
   423  //
   424  // This method is used for recovering in the case that we called the Commit ABCI
   425  // method on the application but crashed before persisting the results.
   426  func (store dbStore) LoadLastABCIResponse(height int64) (*tmstate.ABCIResponses, error) {
   427  	bz, err := store.db.Get(lastABCIResponseKey)
   428  	if err != nil {
   429  		return nil, err
   430  	}
   431  
   432  	if len(bz) == 0 {
   433  		return nil, errors.New("no last ABCI response has been persisted")
   434  	}
   435  
   436  	abciResponse := new(tmstate.ABCIResponsesInfo)
   437  	err = abciResponse.Unmarshal(bz)
   438  	if err != nil {
   439  		tmos.Exit(fmt.Sprintf(`LoadLastABCIResponses: Data has been corrupted or its spec has
   440  			changed: %v\n`, err))
   441  	}
   442  
   443  	// Here we validate the result by comparing its height to the expected height.
   444  	if height != abciResponse.GetHeight() {
   445  		return nil, errors.New("expected height %d but last stored abci responses was at height %d")
   446  	}
   447  
   448  	return abciResponse.AbciResponses, nil
   449  }
   450  
   451  // SaveABCIResponses persists the ABCIResponses to the database.
   452  // This is useful in case we crash after app.Commit and before s.Save().
   453  // Responses are indexed by height so they can also be loaded later to produce
   454  // Merkle proofs.
   455  //
   456  // CONTRACT: height must be monotonically increasing every time this is called.
   457  func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error {
   458  	var dtxs []*abci.ResponseDeliverTx
   459  	// strip nil values,
   460  	for _, tx := range abciResponses.DeliverTxs {
   461  		if tx != nil {
   462  			dtxs = append(dtxs, tx)
   463  		}
   464  	}
   465  	abciResponses.DeliverTxs = dtxs
   466  
   467  	// If the flag is false then we save the ABCIResponse. This can be used for the /BlockResults
   468  	// query or to reindex an event using the command line.
   469  	if !store.DiscardABCIResponses {
   470  		bz, err := abciResponses.Marshal()
   471  		if err != nil {
   472  			return err
   473  		}
   474  		if err := store.db.Set(calcABCIResponsesKey(height), bz); err != nil {
   475  			return err
   476  		}
   477  	}
   478  
   479  	// We always save the last ABCI response for crash recovery.
   480  	// This overwrites the previous saved ABCI Response.
   481  	response := &tmstate.ABCIResponsesInfo{
   482  		AbciResponses: abciResponses,
   483  		Height:        height,
   484  	}
   485  	bz, err := response.Marshal()
   486  	if err != nil {
   487  		return err
   488  	}
   489  
   490  	return store.db.SetSync(lastABCIResponseKey, bz)
   491  }
   492  
   493  //-----------------------------------------------------------------------------
   494  
   495  // LoadValidators loads the ValidatorSet for a given height.
   496  // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
   497  func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) {
   498  	if height == 0 {
   499  		return nil, ErrNoValSetForHeight{height}
   500  	}
   501  	valInfo, err := loadValidatorsInfo(store.db, height)
   502  	if err != nil || valInfo == nil {
   503  		return nil, ErrNoValSetForHeight{height}
   504  	}
   505  	if valInfo.ValidatorSet == nil {
   506  		lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
   507  		valInfo2, err := loadValidatorsInfo(store.db, lastStoredHeight)
   508  		if err != nil || valInfo2 == nil || valInfo2.ValidatorSet == nil {
   509  			return nil,
   510  				fmt.Errorf("couldn't find validators at height %d (height %d was originally requested): %w",
   511  					lastStoredHeight,
   512  					height,
   513  					err,
   514  				)
   515  		}
   516  
   517  		vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet)
   518  		if err != nil {
   519  			return nil, err
   520  		}
   521  
   522  		vs.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate
   523  		vi2, err := vs.ToProto()
   524  		if err != nil {
   525  			return nil, err
   526  		}
   527  
   528  		valInfo2.ValidatorSet = vi2
   529  		valInfo = valInfo2
   530  	}
   531  
   532  	vip, err := types.ValidatorSetFromProto(valInfo.ValidatorSet)
   533  	if err != nil {
   534  		return nil, err
   535  	}
   536  
   537  	return vip, nil
   538  }
   539  
   540  func (store dbStore) LoadProofHash(height int64) ([]byte, error) {
   541  	if height == 0 {
   542  		return nil, ErrNoValSetForHeight{height}
   543  	}
   544  	proofHash, err := loadProofHash(store.db, height)
   545  	if err != nil {
   546  		return nil, ErrNoProofHashForHeight{height}
   547  	}
   548  
   549  	return proofHash, nil
   550  }
   551  
   552  func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
   553  	checkpointHeight := height - height%valSetCheckpointInterval
   554  	return tmmath.MaxInt64(checkpointHeight, lastHeightChanged)
   555  }
   556  
   557  // CONTRACT: Returned ValidatorsInfo can be mutated.
   558  func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error) {
   559  	buf, err := db.Get(calcValidatorsKey(height))
   560  	if err != nil {
   561  		return nil, err
   562  	}
   563  
   564  	if len(buf) == 0 {
   565  		return nil, errors.New("value retrieved from db is empty")
   566  	}
   567  
   568  	v := new(tmstate.ValidatorsInfo)
   569  	err = v.Unmarshal(buf)
   570  	if err != nil {
   571  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   572  		tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed:
   573          %v\n`, err))
   574  	}
   575  	// TODO: ensure that buf is completely read.
   576  
   577  	return v, nil
   578  }
   579  
   580  // saveValidatorsInfo persists the validator set.
   581  //
   582  // `height` is the effective height for which the validator is responsible for
   583  // signing. It should be called from s.Save(), right before the state itself is
   584  // persisted.
   585  func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet *types.ValidatorSet) error {
   586  	if lastHeightChanged > height {
   587  		return errors.New("lastHeightChanged cannot be greater than ValidatorsInfo height")
   588  	}
   589  	valInfo := &tmstate.ValidatorsInfo{
   590  		LastHeightChanged: lastHeightChanged,
   591  	}
   592  	// Only persist validator set if it was updated or checkpoint height (see
   593  	// valSetCheckpointInterval) is reached.
   594  	if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
   595  		pv, err := valSet.ToProto()
   596  		if err != nil {
   597  			return err
   598  		}
   599  		valInfo.ValidatorSet = pv
   600  	}
   601  
   602  	bz, err := valInfo.Marshal()
   603  	if err != nil {
   604  		return err
   605  	}
   606  
   607  	err = store.db.Set(calcValidatorsKey(height), bz)
   608  	if err != nil {
   609  		return err
   610  	}
   611  
   612  	return nil
   613  }
   614  
   615  func loadProofHash(db dbm.DB, height int64) ([]byte, error) {
   616  	buf, err := db.Get(calcProofHashKey(height))
   617  	if err != nil {
   618  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   619  		tmos.Exit(fmt.Sprintf(`LoadValidators: ProofHash has been corrupted or its spec has changed:
   620                  %v\n`, err))
   621  	}
   622  	if len(buf) == 0 {
   623  		return nil, ErrNoProofHashForHeight{height}
   624  	}
   625  
   626  	return buf, nil
   627  }
   628  
   629  func (store dbStore) saveProofHash(height int64, proofHash []byte) error {
   630  	if err := store.db.Set(calcProofHashKey(height), proofHash); err != nil {
   631  		return err
   632  	}
   633  	return nil
   634  }
   635  
   636  //-----------------------------------------------------------------------------
   637  
   638  // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
   639  
   640  // LoadConsensusParams loads the ConsensusParams for a given height.
   641  func (store dbStore) LoadConsensusParams(height int64) (tmproto.ConsensusParams, error) {
   642  	empty := tmproto.ConsensusParams{}
   643  
   644  	paramsInfo, err := store.loadConsensusParamsInfo(height)
   645  	if err != nil {
   646  		return empty, fmt.Errorf("could not find consensus params for height #%d: %w", height, err)
   647  	}
   648  
   649  	if paramsInfo.ConsensusParams.Equal(&empty) {
   650  		paramsInfo2, err := store.loadConsensusParamsInfo(paramsInfo.LastHeightChanged)
   651  		if err != nil {
   652  			return empty, fmt.Errorf(
   653  				"couldn't find consensus params at height %d as last changed from height %d: %w",
   654  				paramsInfo.LastHeightChanged,
   655  				height,
   656  				err,
   657  			)
   658  		}
   659  
   660  		paramsInfo = paramsInfo2
   661  	}
   662  
   663  	return paramsInfo.ConsensusParams, nil
   664  }
   665  
   666  func (store dbStore) loadConsensusParamsInfo(height int64) (*tmstate.ConsensusParamsInfo, error) {
   667  	buf, err := store.db.Get(calcConsensusParamsKey(height))
   668  	if err != nil {
   669  		return nil, err
   670  	}
   671  	if len(buf) == 0 {
   672  		return nil, errors.New("value retrieved from db is empty")
   673  	}
   674  
   675  	paramsInfo := new(tmstate.ConsensusParamsInfo)
   676  	if err = paramsInfo.Unmarshal(buf); err != nil {
   677  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   678  		tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed:
   679                  %v\n`, err))
   680  	}
   681  	// TODO: ensure that buf is completely read.
   682  
   683  	return paramsInfo, nil
   684  }
   685  
   686  // saveConsensusParamsInfo persists the consensus params for the next block to disk.
   687  // It should be called from s.Save(), right before the state itself is persisted.
   688  // If the consensus params did not change after processing the latest block,
   689  // only the last height for which they changed is persisted.
   690  func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, params tmproto.ConsensusParams) error {
   691  	paramsInfo := &tmstate.ConsensusParamsInfo{
   692  		LastHeightChanged: changeHeight,
   693  	}
   694  
   695  	if changeHeight == nextHeight {
   696  		paramsInfo.ConsensusParams = params
   697  	}
   698  	bz, err := paramsInfo.Marshal()
   699  	if err != nil {
   700  		return err
   701  	}
   702  
   703  	err = store.db.Set(calcConsensusParamsKey(nextHeight), bz)
   704  	if err != nil {
   705  		return err
   706  	}
   707  
   708  	return nil
   709  }
   710  
   711  func (store dbStore) Close() error {
   712  	return store.db.Close()
   713  }