github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/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  	tmtime "github.com/ari-anchor/sei-tendermint/libs/time"
    13  
    14  	tmstate "github.com/ari-anchor/sei-tendermint/proto/tendermint/state"
    15  	tmversion "github.com/ari-anchor/sei-tendermint/proto/tendermint/version"
    16  	"github.com/ari-anchor/sei-tendermint/types"
    17  	"github.com/ari-anchor/sei-tendermint/version"
    18  )
    19  
    20  //-----------------------------------------------------------------------------
    21  
    22  type Version struct {
    23  	Consensus version.Consensus ` json:"consensus"`
    24  	Software  string            ` json:"software"`
    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 = Version{
    32  	Consensus: version.Consensus{
    33  		Block: version.BlockProtocol,
    34  		App:   0,
    35  	},
    36  	Software: version.TMVersion,
    37  }
    38  
    39  func (v *Version) ToProto() tmstate.Version {
    40  	return tmstate.Version{
    41  		Consensus: tmversion.Consensus{
    42  			Block: v.Consensus.Block,
    43  			App:   v.Consensus.App,
    44  		},
    45  		Software: v.Software,
    46  	}
    47  }
    48  
    49  func VersionFromProto(v tmstate.Version) Version {
    50  	return Version{
    51  		Consensus: version.Consensus{
    52  			Block: v.Consensus.Block,
    53  			App:   v.Consensus.App,
    54  		},
    55  		Software: v.Software,
    56  	}
    57  }
    58  
    59  //-----------------------------------------------------------------------------
    60  
    61  // State is a short description of the latest committed block of the Tendermint consensus.
    62  // It keeps all information necessary to validate new blocks,
    63  // including the last validator set and the consensus params.
    64  // All fields are exposed so the struct can be easily serialized,
    65  // but none of them should be mutated directly.
    66  // Instead, use state.Copy() or updateState(...).
    67  // NOTE: not goroutine-safe.
    68  type State struct {
    69  	// FIXME: This can be removed as TMVersion is a constant, and version.Consensus should
    70  	// eventually be replaced by VersionParams in ConsensusParams
    71  	Version Version
    72  
    73  	// immutable
    74  	ChainID       string
    75  	InitialHeight int64 // should be 1, not 0, when starting from height 1
    76  
    77  	// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
    78  	LastBlockHeight int64
    79  	LastBlockID     types.BlockID
    80  	LastBlockTime   time.Time
    81  
    82  	// LastValidators is used to validate block.LastCommit.
    83  	// Validators are persisted to the database separately every time they change,
    84  	// so we can query for historical validator sets.
    85  	// Note that if s.LastBlockHeight causes a valset change,
    86  	// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1
    87  	// Extra +1 due to nextValSet delay.
    88  	NextValidators              *types.ValidatorSet
    89  	Validators                  *types.ValidatorSet
    90  	LastValidators              *types.ValidatorSet
    91  	LastHeightValidatorsChanged int64
    92  
    93  	// Consensus parameters used for validating blocks.
    94  	// Changes returned by FinalizeBlock and updated after Commit.
    95  	ConsensusParams                  types.ConsensusParams
    96  	LastHeightConsensusParamsChanged int64
    97  
    98  	// Merkle root of the results from executing prev block
    99  	LastResultsHash []byte
   100  
   101  	// the latest AppHash we've received from calling abci.Commit()
   102  	AppHash []byte
   103  }
   104  
   105  // Copy makes a copy of the State for mutating.
   106  func (state State) Copy() State {
   107  
   108  	return State{
   109  		Version:       state.Version,
   110  		ChainID:       state.ChainID,
   111  		InitialHeight: state.InitialHeight,
   112  
   113  		LastBlockHeight: state.LastBlockHeight,
   114  		LastBlockID:     state.LastBlockID,
   115  		LastBlockTime:   state.LastBlockTime,
   116  
   117  		NextValidators:              state.NextValidators.Copy(),
   118  		Validators:                  state.Validators.Copy(),
   119  		LastValidators:              state.LastValidators.Copy(),
   120  		LastHeightValidatorsChanged: state.LastHeightValidatorsChanged,
   121  
   122  		ConsensusParams:                  state.ConsensusParams,
   123  		LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged,
   124  
   125  		AppHash: state.AppHash,
   126  
   127  		LastResultsHash: state.LastResultsHash,
   128  	}
   129  }
   130  
   131  // Equals returns true if the States are identical.
   132  func (state State) Equals(state2 State) (bool, error) {
   133  	sbz, err := state.Bytes()
   134  	if err != nil {
   135  		return false, err
   136  	}
   137  	s2bz, err := state2.Bytes()
   138  	if err != nil {
   139  		return false, err
   140  	}
   141  	return bytes.Equal(sbz, s2bz), nil
   142  }
   143  
   144  // Bytes serializes the State using protobuf, propagating marshaling
   145  // errors
   146  func (state State) Bytes() ([]byte, error) {
   147  	sm, err := state.ToProto()
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	bz, err := proto.Marshal(sm)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	return bz, nil
   156  }
   157  
   158  // IsEmpty returns true if the State is equal to the empty State.
   159  func (state State) IsEmpty() bool {
   160  	return state.Validators == nil // XXX can't compare to Empty
   161  }
   162  
   163  // ToProto takes the local state type and returns the equivalent proto type
   164  func (state *State) ToProto() (*tmstate.State, error) {
   165  	if state == nil {
   166  		return nil, errors.New("state is nil")
   167  	}
   168  
   169  	sm := new(tmstate.State)
   170  
   171  	sm.Version = state.Version.ToProto()
   172  	sm.ChainID = state.ChainID
   173  	sm.InitialHeight = state.InitialHeight
   174  	sm.LastBlockHeight = state.LastBlockHeight
   175  
   176  	sm.LastBlockID = state.LastBlockID.ToProto()
   177  	sm.LastBlockTime = state.LastBlockTime
   178  	vals, err := state.Validators.ToProto()
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	sm.Validators = vals
   183  
   184  	nVals, err := state.NextValidators.ToProto()
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  	sm.NextValidators = nVals
   189  
   190  	if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil
   191  		lVals, err := state.LastValidators.ToProto()
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		sm.LastValidators = lVals
   196  	}
   197  
   198  	sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged
   199  	sm.ConsensusParams = state.ConsensusParams.ToProto()
   200  	sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged
   201  	sm.LastResultsHash = state.LastResultsHash
   202  	sm.AppHash = state.AppHash
   203  
   204  	return sm, nil
   205  }
   206  
   207  // FromProto takes a state proto message & returns the local state type
   208  func FromProto(pb *tmstate.State) (*State, error) {
   209  	if pb == nil {
   210  		return nil, errors.New("nil State")
   211  	}
   212  
   213  	state := new(State)
   214  
   215  	state.Version = VersionFromProto(pb.Version)
   216  	state.ChainID = pb.ChainID
   217  	state.InitialHeight = pb.InitialHeight
   218  
   219  	bi, err := types.BlockIDFromProto(&pb.LastBlockID)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	state.LastBlockID = *bi
   224  	state.LastBlockHeight = pb.LastBlockHeight
   225  	state.LastBlockTime = pb.LastBlockTime
   226  
   227  	vals, err := types.ValidatorSetFromProto(pb.Validators)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  	state.Validators = vals
   232  
   233  	nVals, err := types.ValidatorSetFromProto(pb.NextValidators)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	state.NextValidators = nVals
   238  
   239  	if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil
   240  		lVals, err := types.ValidatorSetFromProto(pb.LastValidators)
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  		state.LastValidators = lVals
   245  	} else {
   246  		state.LastValidators = types.NewValidatorSet(nil)
   247  	}
   248  
   249  	state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged
   250  	state.ConsensusParams = types.ConsensusParamsFromProto(pb.ConsensusParams)
   251  	state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged
   252  	state.LastResultsHash = pb.LastResultsHash
   253  	state.AppHash = pb.AppHash
   254  
   255  	return state, nil
   256  }
   257  
   258  //------------------------------------------------------------------------
   259  // Create a block from the latest state
   260  
   261  // MakeBlock builds a block from the current state with the given txs, commit,
   262  // and evidence. Note it also takes a proposerAddress because the state does not
   263  // track rounds, and hence does not know the correct proposer. TODO: fix this!
   264  func (state State) MakeBlock(
   265  	height int64,
   266  	txs []types.Tx,
   267  	commit *types.Commit,
   268  	evidence []types.Evidence,
   269  	proposerAddress []byte,
   270  ) *types.Block {
   271  
   272  	// Build base block with block data.
   273  	block := types.MakeBlock(height, txs, commit, evidence)
   274  
   275  	// Fill rest of header with state data.
   276  	block.Header.Populate(
   277  		state.Version.Consensus, state.ChainID,
   278  		tmtime.Now(), state.LastBlockID,
   279  		state.Validators.Hash(), state.NextValidators.Hash(),
   280  		state.ConsensusParams.HashConsensusParams(), state.AppHash, state.LastResultsHash,
   281  		proposerAddress,
   282  	)
   283  
   284  	return block
   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: %w", err)
   307  	}
   308  	genDoc, err := types.GenesisDocFromJSON(genDocJSON)
   309  	if err != nil {
   310  		return nil, fmt.Errorf("error reading GenesisDoc: %w", 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 doc: %w", err)
   320  	}
   321  
   322  	var validatorSet, nextValidatorSet *types.ValidatorSet
   323  	if genDoc.Validators == nil || len(genDoc.Validators) == 0 {
   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  }