github.com/vipernet-xyz/tm@v0.34.24/state/state.go (about)

     1  package state
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/gogo/protobuf/proto"
    11  
    12  	tmstate "github.com/vipernet-xyz/tm/proto/tendermint/state"
    13  	tmproto "github.com/vipernet-xyz/tm/proto/tendermint/types"
    14  	tmversion "github.com/vipernet-xyz/tm/proto/tendermint/version"
    15  	"github.com/vipernet-xyz/tm/types"
    16  	tmtime "github.com/vipernet-xyz/tm/types/time"
    17  	"github.com/vipernet-xyz/tm/version"
    18  )
    19  
    20  // database key
    21  var (
    22  	stateKey = []byte("stateKey")
    23  )
    24  
    25  //-----------------------------------------------------------------------------
    26  
    27  // InitStateVersion sets the Consensus.Block and Software versions,
    28  // but leaves the Consensus.App version blank.
    29  // The Consensus.App version will be set during the Handshake, once
    30  // we hear from the app what protocol version it is running.
    31  var InitStateVersion = tmstate.Version{
    32  	Consensus: tmversion.Consensus{
    33  		Block: version.BlockProtocol,
    34  		App:   0,
    35  	},
    36  	Software: version.TMCoreSemVer,
    37  }
    38  
    39  //-----------------------------------------------------------------------------
    40  
    41  // State is a short description of the latest committed block of the Tendermint consensus.
    42  // It keeps all information necessary to validate new blocks,
    43  // including the last validator set and the consensus params.
    44  // All fields are exposed so the struct can be easily serialized,
    45  // but none of them should be mutated directly.
    46  // Instead, use state.Copy() or state.NextState(...).
    47  // NOTE: not goroutine-safe.
    48  type State struct {
    49  	Version tmstate.Version
    50  
    51  	// immutable
    52  	ChainID       string
    53  	InitialHeight int64 // should be 1, not 0, when starting from height 1
    54  
    55  	// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
    56  	LastBlockHeight int64
    57  	LastBlockID     types.BlockID
    58  	LastBlockTime   time.Time
    59  
    60  	// LastValidators is used to validate block.LastCommit.
    61  	// Validators are persisted to the database separately every time they change,
    62  	// so we can query for historical validator sets.
    63  	// Note that if s.LastBlockHeight causes a valset change,
    64  	// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1
    65  	// Extra +1 due to nextValSet delay.
    66  	NextValidators              *types.ValidatorSet
    67  	Validators                  *types.ValidatorSet
    68  	LastValidators              *types.ValidatorSet
    69  	LastHeightValidatorsChanged int64
    70  
    71  	// Consensus parameters used for validating blocks.
    72  	// Changes returned by EndBlock and updated after Commit.
    73  	ConsensusParams                  tmproto.ConsensusParams
    74  	LastHeightConsensusParamsChanged int64
    75  
    76  	// Merkle root of the results from executing prev block
    77  	LastResultsHash []byte
    78  
    79  	// the latest AppHash we've received from calling abci.Commit()
    80  	AppHash []byte
    81  }
    82  
    83  // Copy makes a copy of the State for mutating.
    84  func (state State) Copy() State {
    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() (*tmstate.State, error) {
   135  	if state == nil {
   136  		return nil, errors.New("state is nil")
   137  	}
   138  
   139  	sm := new(tmstate.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
   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 *tmstate.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 = 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  	commit *types.Commit,
   238  	evidence []types.Evidence,
   239  	proposerAddress []byte,
   240  ) (*types.Block, *types.PartSet) {
   241  	// Build base block with block data.
   242  	block := types.MakeBlock(height, txs, commit, evidence)
   243  
   244  	// Set time.
   245  	var timestamp time.Time
   246  	if height == state.InitialHeight {
   247  		timestamp = state.LastBlockTime // genesis time
   248  	} else {
   249  		timestamp = MedianTime(commit, state.LastValidators)
   250  	}
   251  
   252  	// Fill rest of header with state data.
   253  	block.Header.Populate(
   254  		state.Version.Consensus, state.ChainID,
   255  		timestamp, state.LastBlockID,
   256  		state.Validators.Hash(), state.NextValidators.Hash(),
   257  		types.HashConsensusParams(state.ConsensusParams), state.AppHash, state.LastResultsHash,
   258  		proposerAddress,
   259  	)
   260  
   261  	return block, block.MakePartSet(types.BlockPartSizeBytes)
   262  }
   263  
   264  // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the
   265  // corresponding validator set. The computed time is always between timestamps of
   266  // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the
   267  // computed value.
   268  func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time {
   269  	weightedTimes := make([]*tmtime.WeightedTime, len(commit.Signatures))
   270  	totalVotingPower := int64(0)
   271  
   272  	for i, commitSig := range commit.Signatures {
   273  		if commitSig.Absent() {
   274  			continue
   275  		}
   276  		_, validator := validators.GetByAddress(commitSig.ValidatorAddress)
   277  		// If there's no condition, TestValidateBlockCommit panics; not needed normally.
   278  		if validator != nil {
   279  			totalVotingPower += validator.VotingPower
   280  			weightedTimes[i] = tmtime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower)
   281  		}
   282  	}
   283  
   284  	return tmtime.WeightedMedian(weightedTimes, totalVotingPower)
   285  }
   286  
   287  //------------------------------------------------------------------------
   288  // Genesis
   289  
   290  // MakeGenesisStateFromFile reads and unmarshals state from the given
   291  // file.
   292  //
   293  // Used during replay and in tests.
   294  func MakeGenesisStateFromFile(genDocFile string) (State, error) {
   295  	genDoc, err := MakeGenesisDocFromFile(genDocFile)
   296  	if err != nil {
   297  		return State{}, err
   298  	}
   299  	return MakeGenesisState(genDoc)
   300  }
   301  
   302  // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file.
   303  func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) {
   304  	genDocJSON, err := os.ReadFile(genDocFile)
   305  	if err != nil {
   306  		return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err)
   307  	}
   308  	genDoc, err := types.GenesisDocFromJSON(genDocJSON)
   309  	if err != nil {
   310  		return nil, fmt.Errorf("error reading GenesisDoc: %v", err)
   311  	}
   312  	return genDoc, nil
   313  }
   314  
   315  // MakeGenesisState creates state from types.GenesisDoc.
   316  func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
   317  	err := genDoc.ValidateAndComplete()
   318  	if err != nil {
   319  		return State{}, fmt.Errorf("error in genesis file: %v", err)
   320  	}
   321  
   322  	var validatorSet, nextValidatorSet *types.ValidatorSet
   323  	if genDoc.Validators == nil {
   324  		validatorSet = types.NewValidatorSet(nil)
   325  		nextValidatorSet = types.NewValidatorSet(nil)
   326  	} else {
   327  		validators := make([]*types.Validator, len(genDoc.Validators))
   328  		for i, val := range genDoc.Validators {
   329  			validators[i] = types.NewValidator(val.PubKey, val.Power)
   330  		}
   331  		validatorSet = types.NewValidatorSet(validators)
   332  		nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1)
   333  	}
   334  
   335  	return State{
   336  		Version:       InitStateVersion,
   337  		ChainID:       genDoc.ChainID,
   338  		InitialHeight: genDoc.InitialHeight,
   339  
   340  		LastBlockHeight: 0,
   341  		LastBlockID:     types.BlockID{},
   342  		LastBlockTime:   genDoc.GenesisTime,
   343  
   344  		NextValidators:              nextValidatorSet,
   345  		Validators:                  validatorSet,
   346  		LastValidators:              types.NewValidatorSet(nil),
   347  		LastHeightValidatorsChanged: genDoc.InitialHeight,
   348  
   349  		ConsensusParams:                  *genDoc.ConsensusParams,
   350  		LastHeightConsensusParamsChanged: genDoc.InitialHeight,
   351  
   352  		AppHash: genDoc.AppHash,
   353  	}, nil
   354  }