github.com/KYVENetwork/cometbft/v38@v38.0.3/state/state.go (about)

     1  package state
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	cmtversion "github.com/KYVENetwork/cometbft/v38/proto/cometbft/v38/version"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/cosmos/gogoproto/proto"
    12  
    13  	cmtstate "github.com/KYVENetwork/cometbft/v38/proto/cometbft/v38/state"
    14  	"github.com/KYVENetwork/cometbft/v38/types"
    15  	cmttime "github.com/KYVENetwork/cometbft/v38/types/time"
    16  	"github.com/KYVENetwork/cometbft/v38/version"
    17  )
    18  
    19  // database keys
    20  var (
    21  	stateKey = []byte("stateKey")
    22  )
    23  
    24  //-----------------------------------------------------------------------------
    25  
    26  // InitStateVersion sets the Consensus.Block and Software versions,
    27  // but leaves the Consensus.App version blank.
    28  // The Consensus.App version will be set during the Handshake, once
    29  // we hear from the app what protocol version it is running.
    30  var InitStateVersion = cmtstate.Version{
    31  	Consensus: cmtversion.Consensus{
    32  		Block: version.BlockProtocol,
    33  		App:   0,
    34  	},
    35  	Software: version.TMCoreSemVer,
    36  }
    37  
    38  //-----------------------------------------------------------------------------
    39  
    40  // State is a short description of the latest committed block of the consensus protocol.
    41  // It keeps all information necessary to validate new blocks,
    42  // including the last validator set and the consensus params.
    43  // All fields are exposed so the struct can be easily serialized,
    44  // but none of them should be mutated directly.
    45  // Instead, use state.Copy() or state.NextState(...).
    46  // NOTE: not goroutine-safe.
    47  type State struct {
    48  	Version cmtstate.Version
    49  
    50  	// immutable
    51  	ChainID       string
    52  	InitialHeight int64 // should be 1, not 0, when starting from height 1
    53  
    54  	// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
    55  	LastBlockHeight int64
    56  	LastBlockID     types.BlockID
    57  	LastBlockTime   time.Time
    58  
    59  	// LastValidators is used to validate block.LastCommit.
    60  	// Validators are persisted to the database separately every time they change,
    61  	// so we can query for historical validator sets.
    62  	// Note that if s.LastBlockHeight causes a valset change,
    63  	// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1
    64  	// Extra +1 due to nextValSet delay.
    65  	NextValidators              *types.ValidatorSet
    66  	Validators                  *types.ValidatorSet
    67  	LastValidators              *types.ValidatorSet
    68  	LastHeightValidatorsChanged int64
    69  
    70  	// Consensus parameters used for validating blocks.
    71  	// Changes returned by FinalizeBlock and updated after Commit.
    72  	ConsensusParams                  types.ConsensusParams
    73  	LastHeightConsensusParamsChanged int64
    74  
    75  	// Merkle root of the results from executing prev block
    76  	LastResultsHash []byte
    77  
    78  	// the latest AppHash we've received from calling abci.Commit()
    79  	AppHash []byte
    80  }
    81  
    82  // Copy makes a copy of the State for mutating.
    83  func (state State) Copy() State {
    84  
    85  	return State{
    86  		Version:       state.Version,
    87  		ChainID:       state.ChainID,
    88  		InitialHeight: state.InitialHeight,
    89  
    90  		LastBlockHeight: state.LastBlockHeight,
    91  		LastBlockID:     state.LastBlockID,
    92  		LastBlockTime:   state.LastBlockTime,
    93  
    94  		NextValidators:              state.NextValidators.Copy(),
    95  		Validators:                  state.Validators.Copy(),
    96  		LastValidators:              state.LastValidators.Copy(),
    97  		LastHeightValidatorsChanged: state.LastHeightValidatorsChanged,
    98  
    99  		ConsensusParams:                  state.ConsensusParams,
   100  		LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged,
   101  
   102  		AppHash: state.AppHash,
   103  
   104  		LastResultsHash: state.LastResultsHash,
   105  	}
   106  }
   107  
   108  // Equals returns true if the States are identical.
   109  func (state State) Equals(state2 State) bool {
   110  	sbz, s2bz := state.Bytes(), state2.Bytes()
   111  	return bytes.Equal(sbz, s2bz)
   112  }
   113  
   114  // Bytes serializes the State using protobuf.
   115  // It panics if either casting to protobuf or serialization fails.
   116  func (state State) Bytes() []byte {
   117  	sm, err := state.ToProto()
   118  	if err != nil {
   119  		panic(err)
   120  	}
   121  	bz, err := proto.Marshal(sm)
   122  	if err != nil {
   123  		panic(err)
   124  	}
   125  	return bz
   126  }
   127  
   128  // IsEmpty returns true if the State is equal to the empty State.
   129  func (state State) IsEmpty() bool {
   130  	return state.Validators == nil // XXX can't compare to Empty
   131  }
   132  
   133  // ToProto takes the local state type and returns the equivalent proto type
   134  func (state *State) ToProto() (*cmtstate.State, error) {
   135  	if state == nil {
   136  		return nil, errors.New("state is nil")
   137  	}
   138  
   139  	sm := new(cmtstate.State)
   140  
   141  	sm.Version = state.Version
   142  	sm.ChainID = state.ChainID
   143  	sm.InitialHeight = state.InitialHeight
   144  	sm.LastBlockHeight = state.LastBlockHeight
   145  
   146  	sm.LastBlockID = state.LastBlockID.ToProto()
   147  	sm.LastBlockTime = state.LastBlockTime
   148  	vals, err := state.Validators.ToProto()
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	sm.Validators = vals
   153  
   154  	nVals, err := state.NextValidators.ToProto()
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	sm.NextValidators = nVals
   159  
   160  	if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil
   161  		lVals, err := state.LastValidators.ToProto()
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  		sm.LastValidators = lVals
   166  	}
   167  
   168  	sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged
   169  	sm.ConsensusParams = state.ConsensusParams.ToProto()
   170  	sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged
   171  	sm.LastResultsHash = state.LastResultsHash
   172  	sm.AppHash = state.AppHash
   173  
   174  	return sm, nil
   175  }
   176  
   177  // FromProto takes a state proto message & returns the local state type
   178  func FromProto(pb *cmtstate.State) (*State, error) { //nolint:golint
   179  	if pb == nil {
   180  		return nil, errors.New("nil State")
   181  	}
   182  
   183  	state := new(State)
   184  
   185  	state.Version = pb.Version
   186  	state.ChainID = pb.ChainID
   187  	state.InitialHeight = pb.InitialHeight
   188  
   189  	bi, err := types.BlockIDFromProto(&pb.LastBlockID)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	state.LastBlockID = *bi
   194  	state.LastBlockHeight = pb.LastBlockHeight
   195  	state.LastBlockTime = pb.LastBlockTime
   196  
   197  	vals, err := types.ValidatorSetFromProto(pb.Validators)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	state.Validators = vals
   202  
   203  	nVals, err := types.ValidatorSetFromProto(pb.NextValidators)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	state.NextValidators = nVals
   208  
   209  	if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil
   210  		lVals, err := types.ValidatorSetFromProto(pb.LastValidators)
   211  		if err != nil {
   212  			return nil, err
   213  		}
   214  		state.LastValidators = lVals
   215  	} else {
   216  		state.LastValidators = types.NewValidatorSet(nil)
   217  	}
   218  
   219  	state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged
   220  	state.ConsensusParams = types.ConsensusParamsFromProto(pb.ConsensusParams)
   221  	state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged
   222  	state.LastResultsHash = pb.LastResultsHash
   223  	state.AppHash = pb.AppHash
   224  
   225  	return state, nil
   226  }
   227  
   228  //------------------------------------------------------------------------
   229  // Create a block from the latest state
   230  
   231  // MakeBlock builds a block from the current state with the given txs, commit,
   232  // and evidence. Note it also takes a proposerAddress because the state does not
   233  // track rounds, and hence does not know the correct proposer. TODO: fix this!
   234  func (state State) MakeBlock(
   235  	height int64,
   236  	txs []types.Tx,
   237  	lastCommit *types.Commit,
   238  	evidence []types.Evidence,
   239  	proposerAddress []byte,
   240  ) *types.Block {
   241  
   242  	// Build base block with block data.
   243  	block := types.MakeBlock(height, txs, lastCommit, evidence)
   244  
   245  	// Set time.
   246  	var timestamp time.Time
   247  	if height == state.InitialHeight {
   248  		timestamp = state.LastBlockTime // genesis time
   249  	} else {
   250  		timestamp = MedianTime(lastCommit, state.LastValidators)
   251  	}
   252  
   253  	// Fill rest of header with state data.
   254  	block.Header.Populate(
   255  		state.Version.Consensus, state.ChainID,
   256  		timestamp, state.LastBlockID,
   257  		state.Validators.Hash(), state.NextValidators.Hash(),
   258  		state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash,
   259  		proposerAddress,
   260  	)
   261  
   262  	return block
   263  }
   264  
   265  // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the
   266  // corresponding validator set. The computed time is always between timestamps of
   267  // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the
   268  // computed value.
   269  func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time {
   270  	weightedTimes := make([]*cmttime.WeightedTime, len(commit.Signatures))
   271  	totalVotingPower := int64(0)
   272  
   273  	for i, commitSig := range commit.Signatures {
   274  		if commitSig.BlockIDFlag == types.BlockIDFlagAbsent {
   275  			continue
   276  		}
   277  		_, validator := validators.GetByAddress(commitSig.ValidatorAddress)
   278  		// If there's no condition, TestValidateBlockCommit panics; not needed normally.
   279  		if validator != nil {
   280  			totalVotingPower += validator.VotingPower
   281  			weightedTimes[i] = cmttime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower)
   282  		}
   283  	}
   284  
   285  	return cmttime.WeightedMedian(weightedTimes, totalVotingPower)
   286  }
   287  
   288  //------------------------------------------------------------------------
   289  // Genesis
   290  
   291  // MakeGenesisStateFromFile reads and unmarshals state from the given
   292  // file.
   293  //
   294  // Used during replay and in tests.
   295  func MakeGenesisStateFromFile(genDocFile string) (State, error) {
   296  	genDoc, err := MakeGenesisDocFromFile(genDocFile)
   297  	if err != nil {
   298  		return State{}, err
   299  	}
   300  	return MakeGenesisState(genDoc)
   301  }
   302  
   303  // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file.
   304  func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) {
   305  	genDocJSON, err := os.ReadFile(genDocFile)
   306  	if err != nil {
   307  		return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err)
   308  	}
   309  	genDoc, err := types.GenesisDocFromJSON(genDocJSON)
   310  	if err != nil {
   311  		return nil, fmt.Errorf("error reading GenesisDoc: %v", err)
   312  	}
   313  	return genDoc, nil
   314  }
   315  
   316  // MakeGenesisState creates state from types.GenesisDoc.
   317  func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
   318  	err := genDoc.ValidateAndComplete()
   319  	if err != nil {
   320  		return State{}, fmt.Errorf("error in genesis doc: %w", err)
   321  	}
   322  
   323  	var validatorSet, nextValidatorSet *types.ValidatorSet
   324  	if genDoc.Validators == nil {
   325  		validatorSet = types.NewValidatorSet(nil)
   326  		nextValidatorSet = types.NewValidatorSet(nil)
   327  	} else {
   328  		validators := make([]*types.Validator, len(genDoc.Validators))
   329  		for i, val := range genDoc.Validators {
   330  			validators[i] = types.NewValidator(val.PubKey, val.Power)
   331  		}
   332  		validatorSet = types.NewValidatorSet(validators)
   333  		nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1)
   334  	}
   335  
   336  	return State{
   337  		Version:       InitStateVersion,
   338  		ChainID:       genDoc.ChainID,
   339  		InitialHeight: genDoc.InitialHeight,
   340  
   341  		LastBlockHeight: 0,
   342  		LastBlockID:     types.BlockID{},
   343  		LastBlockTime:   genDoc.GenesisTime,
   344  
   345  		NextValidators:              nextValidatorSet,
   346  		Validators:                  validatorSet,
   347  		LastValidators:              types.NewValidatorSet(nil),
   348  		LastHeightValidatorsChanged: genDoc.InitialHeight,
   349  
   350  		ConsensusParams:                  *genDoc.ConsensusParams,
   351  		LastHeightConsensusParamsChanged: genDoc.InitialHeight,
   352  
   353  		AppHash: genDoc.AppHash,
   354  	}, nil
   355  }