package state import ( "bytes" "fmt" "io/ioutil" "time" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" wire "github.com/tendermint/go-wire" "github.com/tendermint/tendermint/types" ) // database keys var ( stateKey = []byte("stateKey") ) func calcValidatorsKey(height int64) []byte { return []byte(cmn.Fmt("validatorsKey:%v", height)) } func calcConsensusParamsKey(height int64) []byte { return []byte(cmn.Fmt("consensusParamsKey:%v", height)) } func calcABCIResponsesKey(height int64) []byte { return []byte(cmn.Fmt("abciResponsesKey:%v", height)) } //----------------------------------------------------------------------------- // 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() ro state.NextState(...). // NOTE: not goroutine-safe. type State struct { // 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 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 (s State) Copy() State { return State{ ChainID: s.ChainID, LastBlockHeight: s.LastBlockHeight, LastBlockTotalTx: s.LastBlockTotalTx, LastBlockID: s.LastBlockID, LastBlockTime: s.LastBlockTime, Validators: s.Validators.Copy(), LastValidators: s.LastValidators.Copy(), LastHeightValidatorsChanged: s.LastHeightValidatorsChanged, ConsensusParams: s.ConsensusParams, LastHeightConsensusParamsChanged: s.LastHeightConsensusParamsChanged, AppHash: s.AppHash, LastResultsHash: s.LastResultsHash, } } // Save persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database. // It sets the given appHash on the state before persisting. func (s State) Save(db dbm.DB, appHash []byte) { s.AppHash = appHash nextHeight := s.LastBlockHeight + 1 saveValidatorsInfo(db, nextHeight, s.LastHeightValidatorsChanged, s.Validators) saveConsensusParamsInfo(db, nextHeight, s.LastHeightConsensusParamsChanged, s.ConsensusParams) db.SetSync(stateKey, s.Bytes()) } // Equals returns true if the States are identical. func (s State) Equals(s2 State) bool { return bytes.Equal(s.Bytes(), s2.Bytes()) } // Bytes serializes the State using go-wire. func (s State) Bytes() []byte { return wire.BinaryBytes(s) } // NextState returns a new State updated according to the header and responses. func (s State) NextState(blockID types.BlockID, header *types.Header, abciResponses *ABCIResponses) (State, error) { // copy the valset so we can apply changes from EndBlock // and update s.LastValidators and s.Validators prevValSet := s.Validators.Copy() nextValSet := prevValSet.Copy() // update the validator set with the latest abciResponses lastHeightValsChanged := s.LastHeightValidatorsChanged if len(abciResponses.EndBlock.ValidatorUpdates) > 0 { err := updateValidators(nextValSet, abciResponses.EndBlock.ValidatorUpdates) if err != nil { return s, fmt.Errorf("Error changing validator set: %v", err) } // change results from this height but only applies to the next height lastHeightValsChanged = header.Height + 1 } // Update validator accums and set state variables nextValSet.IncrementAccum(1) // update the params with the latest abciResponses nextParams := s.ConsensusParams lastHeightParamsChanged := s.LastHeightConsensusParamsChanged if abciResponses.EndBlock.ConsensusParamUpdates != nil { // NOTE: must not mutate s.ConsensusParams nextParams = s.ConsensusParams.Update(abciResponses.EndBlock.ConsensusParamUpdates) err := nextParams.Validate() if err != nil { return s, fmt.Errorf("Error updating consensus params: %v", err) } // change results from this height but only applies to the next height lastHeightParamsChanged = header.Height + 1 } // NOTE: the AppHash has not been populated. // It will be filled on state.Save. return State{ ChainID: s.ChainID, LastBlockHeight: header.Height, LastBlockTotalTx: s.LastBlockTotalTx + header.NumTxs, LastBlockID: blockID, LastBlockTime: header.Time, Validators: nextValSet, LastValidators: s.Validators.Copy(), LastHeightValidatorsChanged: lastHeightValsChanged, ConsensusParams: nextParams, LastHeightConsensusParamsChanged: lastHeightParamsChanged, LastResultsHash: abciResponses.ResultsHash(), AppHash: nil, }, nil } // GetValidators returns the last and current validator sets. func (s State) GetValidators() (last *types.ValidatorSet, current *types.ValidatorSet) { return s.LastValidators, s.Validators } //------------------------------------------------------------------------ // 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 nil, 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 nil, fmt.Errorf("Error in genesis file: %v", err) } // Make validators slice validators := make([]*types.Validator, len(genDoc.Validators)) for i, val := range genDoc.Validators { pubKey := val.PubKey address := pubKey.Address() // Make validator validators[i] = &types.Validator{ Address: address, PubKey: pubKey, VotingPower: val.Power, } } return &State{ ChainID: genDoc.ChainID, LastBlockHeight: 0, LastBlockID: types.BlockID{}, LastBlockTime: genDoc.GenesisTime, Validators: types.NewValidatorSet(validators), LastValidators: types.NewValidatorSet(nil), LastHeightValidatorsChanged: 1, ConsensusParams: *genDoc.ConsensusParams, LastHeightConsensusParamsChanged: 1, AppHash: genDoc.AppHash, }, nil }