github.com/okex/exchain@v1.8.0/libs/tendermint/state/state.go (about)

     1  package state
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"time"
     8  
     9  	"github.com/okex/exchain/libs/tendermint/types"
    10  	tmtime "github.com/okex/exchain/libs/tendermint/types/time"
    11  	"github.com/okex/exchain/libs/tendermint/version"
    12  )
    13  
    14  // database keys
    15  var (
    16  	stateKey = []byte("stateKey")
    17  )
    18  
    19  //-----------------------------------------------------------------------------
    20  
    21  // Version is for versioning the State.
    22  // It holds the Block and App version needed for making blocks,
    23  // and the software version to support upgrades to the format of
    24  // the State as stored on disk.
    25  type Version struct {
    26  	Consensus version.Consensus
    27  	Software  string
    28  }
    29  
    30  // initStateVersion sets the Consensus.Block and Software versions,
    31  // but leaves the Consensus.App version blank.
    32  // The Consensus.App version will be set during the Handshake, once
    33  // we hear from the app what protocol version it is running.
    34  var initStateVersion = Version{
    35  	Consensus: version.Consensus{
    36  		Block: version.BlockProtocol,
    37  		App:   0,
    38  	},
    39  	Software: version.TMCoreSemVer,
    40  }
    41  
    42  //-----------------------------------------------------------------------------
    43  
    44  // State is a short description of the latest committed block of the Tendermint consensus.
    45  // It keeps all information necessary to validate new blocks,
    46  // including the last validator set and the consensus params.
    47  // All fields are exposed so the struct can be easily serialized,
    48  // but none of them should be mutated directly.
    49  // Instead, use state.Copy() or state.NextState(...).
    50  // NOTE: not goroutine-safe.
    51  type State struct {
    52  	Version Version
    53  
    54  	// immutable
    55  	ChainID string
    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  	// LastValidators is used to validate block.LastCommit.
    63  	// Validators are persisted to the database separately every time they change,
    64  	// so we can query for historical validator sets.
    65  	// Note that if s.LastBlockHeight causes a valset change,
    66  	// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1
    67  	// Extra +1 due to nextValSet delay.
    68  	NextValidators              *types.ValidatorSet
    69  	Validators                  *types.ValidatorSet
    70  	LastValidators              *types.ValidatorSet
    71  	LastHeightValidatorsChanged int64
    72  
    73  	// Consensus parameters used for validating blocks.
    74  	// Changes returned by EndBlock and updated after Commit.
    75  	ConsensusParams                  types.ConsensusParams
    76  	LastHeightConsensusParamsChanged int64
    77  
    78  	// Merkle root of the results from executing prev block
    79  	LastResultsHash []byte
    80  
    81  	// the latest AppHash we've received from calling abci.Commit()
    82  	AppHash []byte
    83  }
    84  
    85  // Copy makes a copy of the State for mutating.
    86  func (state State) Copy() State {
    87  	return State{
    88  		Version: state.Version,
    89  		ChainID: state.ChainID,
    90  
    91  		LastBlockHeight: state.LastBlockHeight,
    92  		LastBlockID:     state.LastBlockID,
    93  		LastBlockTime:   state.LastBlockTime,
    94  
    95  		NextValidators:              state.NextValidators.Copy(),
    96  		Validators:                  state.Validators.Copy(),
    97  		LastValidators:              state.LastValidators.Copy(),
    98  		LastHeightValidatorsChanged: state.LastHeightValidatorsChanged,
    99  
   100  		ConsensusParams:                  state.ConsensusParams,
   101  		LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged,
   102  
   103  		AppHash: state.AppHash,
   104  
   105  		LastResultsHash: state.LastResultsHash,
   106  	}
   107  }
   108  
   109  // Equals returns true if the States are identical.
   110  func (state State) Equals(state2 State) bool {
   111  	sbz, s2bz := state.Bytes(), state2.Bytes()
   112  	return bytes.Equal(sbz, s2bz)
   113  }
   114  
   115  // Bytes serializes the State using go-amino.
   116  func (state State) Bytes() []byte {
   117  	return cdc.MustMarshalBinaryBare(state)
   118  }
   119  
   120  // IsEmpty returns true if the State is equal to the empty State.
   121  func (state State) IsEmpty() bool {
   122  	return state.Validators == nil // XXX can't compare to Empty
   123  }
   124  
   125  //------------------------------------------------------------------------
   126  // Create a block from the latest state
   127  
   128  // MakeBlock builds a block from the current state with the given txs, commit,
   129  // and evidence. Note it also takes a proposerAddress because the state does not
   130  // track rounds, and hence does not know the correct proposer. TODO: fix this!
   131  func (state State) MakeBlock(
   132  	height int64,
   133  	txs []types.Tx,
   134  	commit *types.Commit,
   135  	evidence []types.Evidence,
   136  	proposerAddress []byte,
   137  ) (*types.Block, *types.PartSet) {
   138  
   139  	// Build base block with block data.
   140  	block := types.MakeBlock(height, txs, commit, evidence)
   141  
   142  	// Set time.
   143  	var timestamp time.Time
   144  	if height == types.GetStartBlockHeight()+1 {
   145  		timestamp = state.LastBlockTime // genesis time
   146  	} else {
   147  		timestamp = MedianTime(commit, state.LastValidators)
   148  	}
   149  
   150  	// Fill rest of header with state data.
   151  	block.Header.Populate(
   152  		state.Version.Consensus, state.ChainID,
   153  		timestamp, state.LastBlockID,
   154  		state.Validators.Hash(height), state.NextValidators.Hash(height+1),
   155  		state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash,
   156  		proposerAddress,
   157  	)
   158  
   159  	return block, block.MakePartSet(types.BlockPartSizeBytes)
   160  }
   161  
   162  func (state *State) String() string {
   163  	return fmt.Sprintf("%+v", *state)
   164  }
   165  
   166  // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the
   167  // corresponding validator set. The computed time is always between timestamps of
   168  // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the
   169  // computed value.
   170  func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time {
   171  	weightedTimes := make([]*tmtime.WeightedTime, len(commit.Signatures))
   172  	totalVotingPower := int64(0)
   173  
   174  	for i, commitSig := range commit.Signatures {
   175  		if commitSig.Absent() {
   176  			continue
   177  		}
   178  		_, validator := validators.GetByAddress(commitSig.ValidatorAddress)
   179  		// If there's no condition, TestValidateBlockCommit panics; not needed normally.
   180  		if validator != nil {
   181  			totalVotingPower += validator.VotingPower
   182  			weightedTimes[i] = tmtime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower)
   183  		}
   184  	}
   185  
   186  	return tmtime.WeightedMedian(weightedTimes, totalVotingPower)
   187  }
   188  
   189  //------------------------------------------------------------------------
   190  // Genesis
   191  
   192  // MakeGenesisStateFromFile reads and unmarshals state from the given
   193  // file.
   194  //
   195  // Used during replay and in tests.
   196  func MakeGenesisStateFromFile(genDocFile string) (State, error) {
   197  	genDoc, err := MakeGenesisDocFromFile(genDocFile)
   198  	if err != nil {
   199  		return State{}, err
   200  	}
   201  	return MakeGenesisState(genDoc)
   202  }
   203  
   204  // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file.
   205  func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) {
   206  	genDocJSON, err := ioutil.ReadFile(genDocFile)
   207  	if err != nil {
   208  		return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err)
   209  	}
   210  	genDoc, err := types.GenesisDocFromJSON(genDocJSON)
   211  	if err != nil {
   212  		return nil, fmt.Errorf("error reading GenesisDoc: %v", err)
   213  	}
   214  	return genDoc, nil
   215  }
   216  
   217  // MakeGenesisState creates state from types.GenesisDoc.
   218  func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
   219  	err := genDoc.ValidateAndComplete()
   220  	if err != nil {
   221  		return State{}, fmt.Errorf("error in genesis file: %v", err)
   222  	}
   223  
   224  	var validatorSet, nextValidatorSet *types.ValidatorSet
   225  	if genDoc.Validators == nil {
   226  		validatorSet = types.NewValidatorSet(nil)
   227  		nextValidatorSet = types.NewValidatorSet(nil)
   228  	} else {
   229  		validators := make([]*types.Validator, len(genDoc.Validators))
   230  		for i, val := range genDoc.Validators {
   231  			validators[i] = types.NewValidator(val.PubKey, val.Power)
   232  		}
   233  		validatorSet = types.NewValidatorSet(validators)
   234  		nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1)
   235  	}
   236  
   237  	return State{
   238  		Version: GetStateVersion(types.GetStartBlockHeight()),
   239  		ChainID: genDoc.ChainID,
   240  
   241  		LastBlockHeight: types.GetStartBlockHeight(),
   242  		LastBlockID:     types.BlockID{},
   243  		LastBlockTime:   genDoc.GenesisTime,
   244  
   245  		NextValidators:              nextValidatorSet,
   246  		Validators:                  validatorSet,
   247  		LastValidators:              types.NewValidatorSet(nil),
   248  		LastHeightValidatorsChanged: types.GetStartBlockHeight() + 1,
   249  
   250  		ConsensusParams:                  *genDoc.ConsensusParams,
   251  		LastHeightConsensusParamsChanged: types.GetStartBlockHeight() + 1,
   252  
   253  		AppHash: genDoc.AppHash,
   254  	}, nil
   255  }