github.com/DFWallet/tendermint-cosmos@v0.0.2/state/state.go (about)

     1  package state
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"time"
     9  
    10  	"github.com/gogo/protobuf/proto"
    11  
    12  	tmstate "github.com/DFWallet/tendermint-cosmos/proto/tendermint/state"
    13  	tmproto "github.com/DFWallet/tendermint-cosmos/proto/tendermint/types"
    14  	tmversion "github.com/DFWallet/tendermint-cosmos/proto/tendermint/version"
    15  	"github.com/DFWallet/tendermint-cosmos/types"
    16  	tmtime "github.com/DFWallet/tendermint-cosmos/types/time"
    17  	"github.com/DFWallet/tendermint-cosmos/version"
    18  )
    19  
    20  // database keys
    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  
    86  	return State{
    87  		Version:       state.Version,
    88  		ChainID:       state.ChainID,
    89  		InitialHeight: state.InitialHeight,
    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 protobuf.
   116  // It panics if either casting to protobuf or serialization fails.
   117  func (state State) Bytes() []byte {
   118  	sm, err := state.ToProto()
   119  	if err != nil {
   120  		panic(err)
   121  	}
   122  	bz, err := proto.Marshal(sm)
   123  	if err != nil {
   124  		panic(err)
   125  	}
   126  	return bz
   127  }
   128  
   129  // IsEmpty returns true if the State is equal to the empty State.
   130  func (state State) IsEmpty() bool {
   131  	return state.Validators == nil // XXX can't compare to Empty
   132  }
   133  
   134  // ToProto takes the local state type and returns the equivalent proto type
   135  func (state *State) ToProto() (*tmstate.State, error) {
   136  	if state == nil {
   137  		return nil, errors.New("state is nil")
   138  	}
   139  
   140  	sm := new(tmstate.State)
   141  
   142  	sm.Version = state.Version
   143  	sm.ChainID = state.ChainID
   144  	sm.InitialHeight = state.InitialHeight
   145  	sm.LastBlockHeight = state.LastBlockHeight
   146  
   147  	sm.LastBlockID = state.LastBlockID.ToProto()
   148  	sm.LastBlockTime = state.LastBlockTime
   149  	vals, err := state.Validators.ToProto()
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	sm.Validators = vals
   154  
   155  	nVals, err := state.NextValidators.ToProto()
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	sm.NextValidators = nVals
   160  
   161  	if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil
   162  		lVals, err := state.LastValidators.ToProto()
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  		sm.LastValidators = lVals
   167  	}
   168  
   169  	sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged
   170  	sm.ConsensusParams = state.ConsensusParams
   171  	sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged
   172  	sm.LastResultsHash = state.LastResultsHash
   173  	sm.AppHash = state.AppHash
   174  
   175  	return sm, nil
   176  }
   177  
   178  // StateFromProto takes a state proto message & returns the local state type
   179  func StateFromProto(pb *tmstate.State) (*State, error) { //nolint:golint
   180  	if pb == nil {
   181  		return nil, errors.New("nil State")
   182  	}
   183  
   184  	state := new(State)
   185  
   186  	state.Version = pb.Version
   187  	state.ChainID = pb.ChainID
   188  	state.InitialHeight = pb.InitialHeight
   189  
   190  	bi, err := types.BlockIDFromProto(&pb.LastBlockID)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	state.LastBlockID = *bi
   195  	state.LastBlockHeight = pb.LastBlockHeight
   196  	state.LastBlockTime = pb.LastBlockTime
   197  
   198  	vals, err := types.ValidatorSetFromProto(pb.Validators)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	state.Validators = vals
   203  
   204  	nVals, err := types.ValidatorSetFromProto(pb.NextValidators)
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  	state.NextValidators = nVals
   209  
   210  	if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil
   211  		lVals, err := types.ValidatorSetFromProto(pb.LastValidators)
   212  		if err != nil {
   213  			return nil, err
   214  		}
   215  		state.LastValidators = lVals
   216  	} else {
   217  		state.LastValidators = types.NewValidatorSet(nil)
   218  	}
   219  
   220  	state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged
   221  	state.ConsensusParams = pb.ConsensusParams
   222  	state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged
   223  	state.LastResultsHash = pb.LastResultsHash
   224  	state.AppHash = pb.AppHash
   225  
   226  	return state, nil
   227  }
   228  
   229  //------------------------------------------------------------------------
   230  // Create a block from the latest state
   231  
   232  // MakeBlock builds a block from the current state with the given txs, commit,
   233  // and evidence. Note it also takes a proposerAddress because the state does not
   234  // track rounds, and hence does not know the correct proposer. TODO: fix this!
   235  func (state State) MakeBlock(
   236  	height int64,
   237  	txs []types.Tx,
   238  	commit *types.Commit,
   239  	evidence []types.Evidence,
   240  	proposerAddress []byte,
   241  ) (*types.Block, *types.PartSet) {
   242  
   243  	// Build base block with block data.
   244  	block := types.MakeBlock(height, txs, commit, evidence)
   245  
   246  	// Set time.
   247  	var timestamp time.Time
   248  	if height == state.InitialHeight {
   249  		timestamp = state.LastBlockTime // genesis time
   250  	} else {
   251  		timestamp = MedianTime(commit, state.LastValidators)
   252  	}
   253  
   254  	// Fill rest of header with state data.
   255  	block.Header.Populate(
   256  		state.Version.Consensus, state.ChainID,
   257  		timestamp, state.LastBlockID,
   258  		state.Validators.Hash(), state.NextValidators.Hash(),
   259  		types.HashConsensusParams(state.ConsensusParams), state.AppHash, state.LastResultsHash,
   260  		proposerAddress,
   261  	)
   262  
   263  	return block, block.MakePartSet(types.BlockPartSizeBytes)
   264  }
   265  
   266  // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the
   267  // corresponding validator set. The computed time is always between timestamps of
   268  // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the
   269  // computed value.
   270  func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time {
   271  	weightedTimes := make([]*tmtime.WeightedTime, len(commit.Signatures))
   272  	totalVotingPower := int64(0)
   273  
   274  	for i, commitSig := range commit.Signatures {
   275  		if commitSig.Absent() {
   276  			continue
   277  		}
   278  		_, validator := validators.GetByAddress(commitSig.ValidatorAddress)
   279  		// If there's no condition, TestValidateBlockCommit panics; not needed normally.
   280  		if validator != nil {
   281  			totalVotingPower += validator.VotingPower
   282  			weightedTimes[i] = tmtime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower)
   283  		}
   284  	}
   285  
   286  	return tmtime.WeightedMedian(weightedTimes, totalVotingPower)
   287  }
   288  
   289  //------------------------------------------------------------------------
   290  // Genesis
   291  
   292  // MakeGenesisStateFromFile reads and unmarshals state from the given
   293  // file.
   294  //
   295  // Used during replay and in tests.
   296  func MakeGenesisStateFromFile(genDocFile string) (State, error) {
   297  	genDoc, err := MakeGenesisDocFromFile(genDocFile)
   298  	if err != nil {
   299  		return State{}, err
   300  	}
   301  	return MakeGenesisState(genDoc)
   302  }
   303  
   304  // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file.
   305  func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) {
   306  	genDocJSON, err := ioutil.ReadFile(genDocFile)
   307  	if err != nil {
   308  		return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err)
   309  	}
   310  	genDoc, err := types.GenesisDocFromJSON(genDocJSON)
   311  	if err != nil {
   312  		return nil, fmt.Errorf("error reading GenesisDoc: %v", err)
   313  	}
   314  	return genDoc, nil
   315  }
   316  
   317  // MakeGenesisState creates state from types.GenesisDoc.
   318  func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
   319  	err := genDoc.ValidateAndComplete()
   320  	if err != nil {
   321  		return State{}, fmt.Errorf("error in genesis file: %v", err)
   322  	}
   323  
   324  	var validatorSet, nextValidatorSet *types.ValidatorSet
   325  	if genDoc.Validators == nil {
   326  		validatorSet = types.NewValidatorSet(nil)
   327  		nextValidatorSet = types.NewValidatorSet(nil)
   328  	} else {
   329  		validators := make([]*types.Validator, len(genDoc.Validators))
   330  		for i, val := range genDoc.Validators {
   331  			validators[i] = types.NewValidator(val.PubKey, val.Power)
   332  		}
   333  		validatorSet = types.NewValidatorSet(validators)
   334  		nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1)
   335  	}
   336  
   337  	return State{
   338  		Version:       InitStateVersion,
   339  		ChainID:       genDoc.ChainID,
   340  		InitialHeight: genDoc.InitialHeight,
   341  
   342  		LastBlockHeight: 0,
   343  		LastBlockID:     types.BlockID{},
   344  		LastBlockTime:   genDoc.GenesisTime,
   345  
   346  		NextValidators:              nextValidatorSet,
   347  		Validators:                  validatorSet,
   348  		LastValidators:              types.NewValidatorSet(nil),
   349  		LastHeightValidatorsChanged: genDoc.InitialHeight,
   350  
   351  		ConsensusParams:                  *genDoc.ConsensusParams,
   352  		LastHeightConsensusParamsChanged: genDoc.InitialHeight,
   353  
   354  		AppHash: genDoc.AppHash,
   355  	}, nil
   356  }