github.com/Finschia/ostracon@v1.1.5/state/state.go (about)

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