github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/state/store.go (about)

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