github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/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/franono/tendermint/abci/types"
     9  	tmmath "github.com/franono/tendermint/libs/math"
    10  	tmos "github.com/franono/tendermint/libs/os"
    11  	"github.com/franono/tendermint/types"
    12  )
    13  
    14  const (
    15  	// persist validators every valSetCheckpointInterval blocks to avoid
    16  	// LoadValidators taking too much time.
    17  	// https://github.com/franono/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  	err := db.SetSync(key, state.Bytes())
   115  	if err != nil {
   116  		panic(err)
   117  	}
   118  }
   119  
   120  // BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height.
   121  func BootstrapState(db dbm.DB, state State) error {
   122  	height := state.LastBlockHeight
   123  	saveValidatorsInfo(db, height, height, state.LastValidators)
   124  	saveValidatorsInfo(db, height+1, height+1, state.Validators)
   125  	saveValidatorsInfo(db, height+2, height+2, state.NextValidators)
   126  	saveConsensusParamsInfo(db, height+1, height+1, state.ConsensusParams)
   127  	return db.SetSync(stateKey, state.Bytes())
   128  }
   129  
   130  //------------------------------------------------------------------------
   131  
   132  // ABCIResponses retains the responses
   133  // of the various ABCI calls during block processing.
   134  // It is persisted to disk for each height before calling Commit.
   135  type ABCIResponses struct {
   136  	DeliverTxs []*abci.ResponseDeliverTx `json:"deliver_txs"`
   137  	EndBlock   *abci.ResponseEndBlock    `json:"end_block"`
   138  	BeginBlock *abci.ResponseBeginBlock  `json:"begin_block"`
   139  }
   140  
   141  // PruneStates deletes states between the given heights (including from, excluding to). It is not
   142  // guaranteed to delete all states, since the last checkpointed state and states being pointed to by
   143  // e.g. `LastHeightChanged` must remain. The state at to must also exist.
   144  //
   145  // The from parameter is necessary since we can't do a key scan in a performant way due to the key
   146  // encoding not preserving ordering: https://github.com/franono/tendermint/issues/4567
   147  // This will cause some old states to be left behind when doing incremental partial prunes,
   148  // specifically older checkpoints and LastHeightChanged targets.
   149  func PruneStates(db dbm.DB, from int64, to int64) error {
   150  	if from <= 0 || to <= 0 {
   151  		return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to)
   152  	}
   153  	if from >= to {
   154  		return fmt.Errorf("from height %v must be lower than to height %v", from, to)
   155  	}
   156  	valInfo := loadValidatorsInfo(db, to)
   157  	if valInfo == nil {
   158  		return fmt.Errorf("validators at height %v not found", to)
   159  	}
   160  	paramsInfo := loadConsensusParamsInfo(db, to)
   161  	if paramsInfo == nil {
   162  		return fmt.Errorf("consensus params at height %v not found", to)
   163  	}
   164  
   165  	keepVals := make(map[int64]bool)
   166  	if valInfo.ValidatorSet == nil {
   167  		keepVals[valInfo.LastHeightChanged] = true
   168  		keepVals[lastStoredHeightFor(to, valInfo.LastHeightChanged)] = true // keep last checkpoint too
   169  	}
   170  	keepParams := make(map[int64]bool)
   171  	if paramsInfo.ConsensusParams.Equals(&types.ConsensusParams{}) {
   172  		keepParams[paramsInfo.LastHeightChanged] = true
   173  	}
   174  
   175  	batch := db.NewBatch()
   176  	defer batch.Close()
   177  	pruned := uint64(0)
   178  	var err error
   179  
   180  	// We have to delete in reverse order, to avoid deleting previous heights that have validator
   181  	// sets and consensus params that we may need to retrieve.
   182  	for h := to - 1; h >= from; h-- {
   183  		// For heights we keep, we must make sure they have the full validator set or consensus
   184  		// params, otherwise they will panic if they're retrieved directly (instead of
   185  		// indirectly via a LastHeightChanged pointer).
   186  		if keepVals[h] {
   187  			v := loadValidatorsInfo(db, h)
   188  			if v.ValidatorSet == nil {
   189  				v.ValidatorSet, err = LoadValidators(db, h)
   190  				if err != nil {
   191  					return err
   192  				}
   193  				v.LastHeightChanged = h
   194  				batch.Set(calcValidatorsKey(h), v.Bytes())
   195  			}
   196  		} else {
   197  			batch.Delete(calcValidatorsKey(h))
   198  		}
   199  
   200  		if keepParams[h] {
   201  			p := loadConsensusParamsInfo(db, h)
   202  			if p.ConsensusParams.Equals(&types.ConsensusParams{}) {
   203  				p.ConsensusParams, err = LoadConsensusParams(db, h)
   204  				if err != nil {
   205  					return err
   206  				}
   207  				p.LastHeightChanged = h
   208  				batch.Set(calcConsensusParamsKey(h), p.Bytes())
   209  			}
   210  		} else {
   211  			batch.Delete(calcConsensusParamsKey(h))
   212  		}
   213  
   214  		batch.Delete(calcABCIResponsesKey(h))
   215  		pruned++
   216  
   217  		// avoid batches growing too large by flushing to database regularly
   218  		if pruned%1000 == 0 && pruned > 0 {
   219  			err := batch.Write()
   220  			if err != nil {
   221  				return err
   222  			}
   223  			batch.Close()
   224  			batch = db.NewBatch()
   225  			defer batch.Close()
   226  		}
   227  	}
   228  
   229  	err = batch.WriteSync()
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	return nil
   235  }
   236  
   237  // NewABCIResponses returns a new ABCIResponses
   238  func NewABCIResponses(block *types.Block) *ABCIResponses {
   239  	resDeliverTxs := make([]*abci.ResponseDeliverTx, len(block.Data.Txs))
   240  	if len(block.Data.Txs) == 0 {
   241  		// This makes Amino encoding/decoding consistent.
   242  		resDeliverTxs = nil
   243  	}
   244  	return &ABCIResponses{
   245  		DeliverTxs: resDeliverTxs,
   246  	}
   247  }
   248  
   249  // Bytes serializes the ABCIResponse using go-amino.
   250  func (arz *ABCIResponses) Bytes() []byte {
   251  	return cdc.MustMarshalBinaryBare(arz)
   252  }
   253  
   254  func (arz *ABCIResponses) ResultsHash() []byte {
   255  	results := types.NewResults(arz.DeliverTxs)
   256  	return results.Hash()
   257  }
   258  
   259  // LoadABCIResponses loads the ABCIResponses for the given height from the database.
   260  // This is useful for recovering from crashes where we called app.Commit and before we called
   261  // s.Save(). It can also be used to produce Merkle proofs of the result of txs.
   262  func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) {
   263  	buf, err := db.Get(calcABCIResponsesKey(height))
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	if len(buf) == 0 {
   268  		return nil, ErrNoABCIResponsesForHeight{height}
   269  	}
   270  
   271  	abciResponses := new(ABCIResponses)
   272  	err = cdc.UnmarshalBinaryBare(buf, abciResponses)
   273  	if err != nil {
   274  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   275  		tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has
   276                  changed: %v\n`, err))
   277  	}
   278  	// TODO: ensure that buf is completely read.
   279  
   280  	return abciResponses, nil
   281  }
   282  
   283  // SaveABCIResponses persists the ABCIResponses to the database.
   284  // This is useful in case we crash after app.Commit and before s.Save().
   285  // Responses are indexed by height so they can also be loaded later to produce
   286  // Merkle proofs.
   287  //
   288  // Exposed for testing.
   289  func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
   290  	db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
   291  }
   292  
   293  //-----------------------------------------------------------------------------
   294  
   295  // ValidatorsInfo represents the latest validator set, or the last height it changed
   296  type ValidatorsInfo struct {
   297  	ValidatorSet      *types.ValidatorSet
   298  	LastHeightChanged int64
   299  }
   300  
   301  // Bytes serializes the ValidatorsInfo using go-amino.
   302  func (valInfo *ValidatorsInfo) Bytes() []byte {
   303  	return cdc.MustMarshalBinaryBare(valInfo)
   304  }
   305  
   306  // LoadValidators loads the ValidatorSet for a given height.
   307  // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
   308  func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
   309  	valInfo := loadValidatorsInfo(db, height)
   310  	if valInfo == nil {
   311  		return nil, ErrNoValSetForHeight{height}
   312  	}
   313  	if valInfo.ValidatorSet == nil {
   314  		lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
   315  		valInfo2 := loadValidatorsInfo(db, lastStoredHeight)
   316  		if valInfo2 == nil || valInfo2.ValidatorSet == nil {
   317  			panic(
   318  				fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)",
   319  					lastStoredHeight,
   320  					height,
   321  				),
   322  			)
   323  		}
   324  		valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate
   325  		valInfo = valInfo2
   326  	}
   327  
   328  	return valInfo.ValidatorSet, nil
   329  }
   330  
   331  func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
   332  	checkpointHeight := height - height%valSetCheckpointInterval
   333  	return tmmath.MaxInt64(checkpointHeight, lastHeightChanged)
   334  }
   335  
   336  // CONTRACT: Returned ValidatorsInfo can be mutated.
   337  func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
   338  	buf, err := db.Get(calcValidatorsKey(height))
   339  	if err != nil {
   340  		panic(err)
   341  	}
   342  	if len(buf) == 0 {
   343  		return nil
   344  	}
   345  
   346  	v := new(ValidatorsInfo)
   347  	err = cdc.UnmarshalBinaryBare(buf, v)
   348  	if err != nil {
   349  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   350  		tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed:
   351                  %v\n`, err))
   352  	}
   353  	// TODO: ensure that buf is completely read.
   354  
   355  	return v
   356  }
   357  
   358  // saveValidatorsInfo persists the validator set.
   359  //
   360  // `height` is the effective height for which the validator is responsible for
   361  // signing. It should be called from s.Save(), right before the state itself is
   362  // persisted.
   363  func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
   364  	if lastHeightChanged > height {
   365  		panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
   366  	}
   367  	valInfo := &ValidatorsInfo{
   368  		LastHeightChanged: lastHeightChanged,
   369  	}
   370  	// Only persist validator set if it was updated or checkpoint height (see
   371  	// valSetCheckpointInterval) is reached.
   372  	if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
   373  		valInfo.ValidatorSet = valSet
   374  	}
   375  	db.Set(calcValidatorsKey(height), valInfo.Bytes())
   376  }
   377  
   378  //-----------------------------------------------------------------------------
   379  
   380  // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
   381  type ConsensusParamsInfo struct {
   382  	ConsensusParams   types.ConsensusParams
   383  	LastHeightChanged int64
   384  }
   385  
   386  // Bytes serializes the ConsensusParamsInfo using go-amino.
   387  func (params ConsensusParamsInfo) Bytes() []byte {
   388  	return cdc.MustMarshalBinaryBare(params)
   389  }
   390  
   391  // LoadConsensusParams loads the ConsensusParams for a given height.
   392  func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) {
   393  	empty := types.ConsensusParams{}
   394  
   395  	paramsInfo := loadConsensusParamsInfo(db, height)
   396  	if paramsInfo == nil {
   397  		return empty, ErrNoConsensusParamsForHeight{height}
   398  	}
   399  
   400  	if paramsInfo.ConsensusParams.Equals(&empty) {
   401  		paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
   402  		if paramsInfo2 == nil {
   403  			panic(
   404  				fmt.Sprintf(
   405  					"Couldn't find consensus params at height %d as last changed from height %d",
   406  					paramsInfo.LastHeightChanged,
   407  					height,
   408  				),
   409  			)
   410  		}
   411  		paramsInfo = paramsInfo2
   412  	}
   413  
   414  	return paramsInfo.ConsensusParams, nil
   415  }
   416  
   417  func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo {
   418  	buf, err := db.Get(calcConsensusParamsKey(height))
   419  	if err != nil {
   420  		panic(err)
   421  	}
   422  	if len(buf) == 0 {
   423  		return nil
   424  	}
   425  
   426  	paramsInfo := new(ConsensusParamsInfo)
   427  	err = cdc.UnmarshalBinaryBare(buf, paramsInfo)
   428  	if err != nil {
   429  		// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   430  		tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed:
   431                  %v\n`, err))
   432  	}
   433  	// TODO: ensure that buf is completely read.
   434  
   435  	return paramsInfo
   436  }
   437  
   438  // saveConsensusParamsInfo persists the consensus params for the next block to disk.
   439  // It should be called from s.Save(), right before the state itself is persisted.
   440  // If the consensus params did not change after processing the latest block,
   441  // only the last height for which they changed is persisted.
   442  func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) {
   443  	paramsInfo := &ConsensusParamsInfo{
   444  		LastHeightChanged: changeHeight,
   445  	}
   446  	if changeHeight == nextHeight {
   447  		paramsInfo.ConsensusParams = params
   448  	}
   449  	db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
   450  }