- package state
-
- import (
- "bytes"
- "fmt"
- "io/ioutil"
- "time"
-
- "github.com/tendermint/tendermint/types"
- tmtime "github.com/tendermint/tendermint/types/time"
- "github.com/tendermint/tendermint/version"
- )
-
- // database keys
- var (
- stateKey = []byte("stateKey")
- )
-
- //-----------------------------------------------------------------------------
-
- // Version is for versioning the State.
- // It holds the Block and App version needed for making blocks,
- // and the software version to support upgrades to the format of
- // the State as stored on disk.
- type Version struct {
- Consensus version.Consensus
- Software string
- }
-
- // initStateVersion sets the Consensus.Block and Software versions,
- // but leaves the Consensus.App version blank.
- // The Consensus.App version will be set during the Handshake, once
- // we hear from the app what protocol version it is running.
- var initStateVersion = Version{
- Consensus: version.Consensus{
- Block: version.BlockProtocol,
- App: 0,
- },
- Software: version.TMCoreSemVer,
- }
-
- //-----------------------------------------------------------------------------
-
- // State is a short description of the latest committed block of the Tendermint consensus.
- // It keeps all information necessary to validate new blocks,
- // including the last validator set and the consensus params.
- // All fields are exposed so the struct can be easily serialized,
- // but none of them should be mutated directly.
- // Instead, use state.Copy() or state.NextState(...).
- // NOTE: not goroutine-safe.
- type State struct {
- Version Version
-
- // immutable
- ChainID string
-
- // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
- LastBlockHeight int64
- LastBlockTotalTx int64
- LastBlockID types.BlockID
- LastBlockTime time.Time
-
- // LastValidators is used to validate block.LastCommit.
- // Validators are persisted to the database separately every time they change,
- // so we can query for historical validator sets.
- // Note that if s.LastBlockHeight causes a valset change,
- // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1
- NextValidators *types.ValidatorSet
- Validators *types.ValidatorSet
- LastValidators *types.ValidatorSet
- LastHeightValidatorsChanged int64
-
- // Consensus parameters used for validating blocks.
- // Changes returned by EndBlock and updated after Commit.
- ConsensusParams types.ConsensusParams
- LastHeightConsensusParamsChanged int64
-
- // Merkle root of the results from executing prev block
- LastResultsHash []byte
-
- // the latest AppHash we've received from calling abci.Commit()
- AppHash []byte
- }
-
- // Copy makes a copy of the State for mutating.
- func (state State) Copy() State {
- return State{
- Version: state.Version,
- ChainID: state.ChainID,
-
- LastBlockHeight: state.LastBlockHeight,
- LastBlockTotalTx: state.LastBlockTotalTx,
- LastBlockID: state.LastBlockID,
- LastBlockTime: state.LastBlockTime,
-
- NextValidators: state.NextValidators.Copy(),
- Validators: state.Validators.Copy(),
- LastValidators: state.LastValidators.Copy(),
- LastHeightValidatorsChanged: state.LastHeightValidatorsChanged,
-
- ConsensusParams: state.ConsensusParams,
- LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged,
-
- AppHash: state.AppHash,
-
- LastResultsHash: state.LastResultsHash,
- }
- }
-
- // Equals returns true if the States are identical.
- func (state State) Equals(state2 State) bool {
- sbz, s2bz := state.Bytes(), state2.Bytes()
- return bytes.Equal(sbz, s2bz)
- }
-
- // Bytes serializes the State using go-amino.
- func (state State) Bytes() []byte {
- return cdc.MustMarshalBinaryBare(state)
- }
-
- // IsEmpty returns true if the State is equal to the empty State.
- func (state State) IsEmpty() bool {
- return state.Validators == nil // XXX can't compare to Empty
- }
-
- //------------------------------------------------------------------------
- // Create a block from the latest state
-
- // MakeBlock builds a block from the current state with the given txs, commit,
- // and evidence. Note it also takes a proposerAddress because the state does not
- // track rounds, and hence does not know the correct proposer. TODO: fix this!
- func (state State) MakeBlock(
- height int64,
- txs []types.Tx,
- commit *types.Commit,
- evidence []types.Evidence,
- proposerAddress []byte,
- ) (*types.Block, *types.PartSet) {
-
- // Build base block with block data.
- block := types.MakeBlock(height, txs, commit, evidence)
-
- // Set time.
- var timestamp time.Time
- if height == 1 {
- timestamp = state.LastBlockTime // genesis time
- } else {
- timestamp = MedianTime(commit, state.LastValidators)
- }
-
- // Fill rest of header with state data.
- block.Header.Populate(
- state.Version.Consensus, state.ChainID,
- timestamp, state.LastBlockID, state.LastBlockTotalTx+block.NumTxs,
- state.Validators.Hash(), state.NextValidators.Hash(),
- state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash,
- proposerAddress,
- )
-
- return block, block.MakePartSet(types.BlockPartSizeBytes)
- }
-
- // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the
- // corresponding validator set. The computed time is always between timestamps of
- // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the
- // computed value.
- func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time {
-
- weightedTimes := make([]*tmtime.WeightedTime, len(commit.Precommits))
- totalVotingPower := int64(0)
-
- for i, vote := range commit.Precommits {
- if vote != nil {
- _, validator := validators.GetByIndex(vote.ValidatorIndex)
- totalVotingPower += validator.VotingPower
- weightedTimes[i] = tmtime.NewWeightedTime(vote.Timestamp, validator.VotingPower)
- }
- }
-
- return tmtime.WeightedMedian(weightedTimes, totalVotingPower)
- }
-
- //------------------------------------------------------------------------
- // Genesis
-
- // MakeGenesisStateFromFile reads and unmarshals state from the given
- // file.
- //
- // Used during replay and in tests.
- func MakeGenesisStateFromFile(genDocFile string) (State, error) {
- genDoc, err := MakeGenesisDocFromFile(genDocFile)
- if err != nil {
- return State{}, err
- }
- return MakeGenesisState(genDoc)
- }
-
- // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file.
- func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) {
- genDocJSON, err := ioutil.ReadFile(genDocFile)
- if err != nil {
- return nil, fmt.Errorf("Couldn't read GenesisDoc file: %v", err)
- }
- genDoc, err := types.GenesisDocFromJSON(genDocJSON)
- if err != nil {
- return nil, fmt.Errorf("Error reading GenesisDoc: %v", err)
- }
- return genDoc, nil
- }
-
- // MakeGenesisState creates state from types.GenesisDoc.
- func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
- err := genDoc.ValidateAndComplete()
- if err != nil {
- return State{}, fmt.Errorf("Error in genesis file: %v", err)
- }
-
- var validatorSet, nextValidatorSet *types.ValidatorSet
- if genDoc.Validators == nil {
- validatorSet = types.NewValidatorSet(nil)
- nextValidatorSet = types.NewValidatorSet(nil)
- } else {
- validators := make([]*types.Validator, len(genDoc.Validators))
- for i, val := range genDoc.Validators {
- validators[i] = types.NewValidator(val.PubKey, val.Power)
- }
- validatorSet = types.NewValidatorSet(validators)
- nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementAccum(1)
- }
-
- return State{
- Version: initStateVersion,
- ChainID: genDoc.ChainID,
-
- LastBlockHeight: 0,
- LastBlockID: types.BlockID{},
- LastBlockTime: genDoc.GenesisTime,
-
- NextValidators: nextValidatorSet,
- Validators: validatorSet,
- LastValidators: types.NewValidatorSet(nil),
- LastHeightValidatorsChanged: 1,
-
- ConsensusParams: *genDoc.ConsensusParams,
- LastHeightConsensusParamsChanged: 1,
-
- AppHash: genDoc.AppHash,
- }, nil
- }
|