github.com/noirx94/tendermintmp@v0.0.1/state/store.go (about)

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