From c915719f85f229e58de13876d414bbb63987bc6e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 27 Dec 2017 17:50:16 -0500 Subject: [PATCH] *State->State; SetBlockAndValidators->NextState --- evidence/pool.go | 2 +- state/db.go | 41 +++++++++++++ state/execution.go | 4 +- state/state.go | 140 +++++++++++---------------------------------- 4 files changed, 76 insertions(+), 111 deletions(-) diff --git a/evidence/pool.go b/evidence/pool.go index 1965d063..381801df 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -21,7 +21,7 @@ type EvidencePool struct { evidenceChan chan types.Evidence } -func NewEvidencePool(params types.EvidenceParams, evidenceStore *EvidenceStore, state *types.State) *EvidencePool { +func NewEvidencePool(params types.EvidenceParams, evidenceStore *EvidenceStore, state types.State) *EvidencePool { evpool := &EvidencePool{ params: params, logger: log.NewNopLogger(), diff --git a/state/db.go b/state/db.go index 5174b9cb..08da59cb 100644 --- a/state/db.go +++ b/state/db.go @@ -11,6 +11,47 @@ import ( dbm "github.com/tendermint/tmlibs/db" ) +// GetState loads the most recent state from the database, +// or creates a new one from the given genesisFile and persists the result +// to the database. +func GetState(stateDB dbm.DB, genesisFile string) (*State, error) { + state := LoadState(stateDB) + if state == nil { + var err error + state, err = MakeGenesisStateFromFile(stateDB, genesisFile) + if err != nil { + return nil, err + } + state.Save() + } + + return state, nil +} + +// LoadState loads the State from the database. +func LoadState(db dbm.DB) *State { + return loadState(db, stateKey) +} + +func loadState(db dbm.DB, key []byte) *State { + buf := db.Get(key) + if len(buf) == 0 { + return nil + } + + s := &State{db: db} + r, n, err := bytes.NewReader(buf), new(int), new(error) + wire.ReadBinaryPtr(&s, r, 0, n, err) + if *err != nil { + // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED + cmn.Exit(cmn.Fmt(`LoadState: Data has been corrupted or its spec has changed: + %v\n`, *err)) + } + // TODO: ensure that buf is completely read. + + return s +} + //------------------------------------------------------------------------ // ABCIResponses retains the responses diff --git a/state/execution.go b/state/execution.go index b56f61f9..d05e043d 100644 --- a/state/execution.go +++ b/state/execution.go @@ -213,7 +213,7 @@ func changeInVotingPowerMoreOrEqualToOneThird(currentSet *types.ValidatorSet, up // Validate block // MakeBlock builds a block with the given txs and commit from the current state. -func (s *State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*types.Block, *types.PartSet) { +func (s State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*types.Block, *types.PartSet) { // build base block block := types.MakeBlock(height, txs, commit) @@ -309,7 +309,7 @@ func (s State) validateBlock(b *types.Block) error { // It returns the priority of this evidence, or an error. // NOTE: return error may be ErrNoValSetForHeight, in which case the validator set // for the evidence height could not be loaded. -func VerifyEvidence(s State, evidence types.Evidence) (priority int64, err error) { +func (s State) VerifyEvidence(evidence types.Evidence) (priority int64, err error) { height := s.LastBlockHeight evidenceAge := height - evidence.Height() maxAge := s.ConsensusParams.EvidenceParams.MaxAge diff --git a/state/state.go b/state/state.go index 1aaddeb3..ca9cf16b 100644 --- a/state/state.go +++ b/state/state.go @@ -8,7 +8,6 @@ import ( cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" - "github.com/tendermint/tmlibs/log" wire "github.com/tendermint/go-wire" @@ -38,16 +37,13 @@ func calcABCIResponsesKey(height int64) []byte { // 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 the fields should only be changed by calling state.SetBlockAndValidators. +// but none of them should be mutated directly. +// Instead, use state.Copy() ro state.NextState(...). // NOTE: not goroutine-safe. type State struct { - db dbm.DB - // Immutable ChainID string - // Exposed fields are updated by SetBlockAndValidators. - // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) LastBlockHeight int64 LastBlockTotalTx int64 @@ -73,65 +69,11 @@ type State struct { // The latest AppHash we've received from calling abci.Commit() AppHash []byte - - logger log.Logger -} - -func (s *State) DB() dbm.DB { - return s.db -} - -// GetState loads the most recent state from the database, -// or creates a new one from the given genesisFile and persists the result -// to the database. -func GetState(stateDB dbm.DB, genesisFile string) (*State, error) { - state := LoadState(stateDB) - if state == nil { - var err error - state, err = MakeGenesisStateFromFile(stateDB, genesisFile) - if err != nil { - return nil, err - } - state.Save() - } - - return state, nil -} - -// LoadState loads the State from the database. -func LoadState(db dbm.DB) *State { - return loadState(db, stateKey) -} - -func loadState(db dbm.DB, key []byte) *State { - buf := db.Get(key) - if len(buf) == 0 { - return nil - } - - s := &State{db: db} - r, n, err := bytes.NewReader(buf), new(int), new(error) - wire.ReadBinaryPtr(&s, r, 0, n, err) - if *err != nil { - // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED - cmn.Exit(cmn.Fmt(`LoadState: Data has been corrupted or its spec has changed: - %v\n`, *err)) - } - // TODO: ensure that buf is completely read. - - return s -} - -// SetLogger sets the logger on the State. -func (s *State) SetLogger(l log.Logger) { - s.logger = l } // Copy makes a copy of the State for mutating. -func (s *State) Copy() *State { +func (s State) Copy() State { return &State{ - db: s.db, - ChainID: s.ChainID, LastBlockHeight: s.LastBlockHeight, @@ -149,36 +91,30 @@ func (s *State) Copy() *State { AppHash: s.AppHash, LastResultsHash: s.LastResultsHash, - - logger: s.logger, } } -// Save persists the State to the database. -func (s *State) Save() { +// Save persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database. +func (s State) Save(db dbm.DB) { nextHeight := s.LastBlockHeight + 1 - - // persist everything to db - db := s.db 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 { +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 { +func (s State) Bytes() []byte { return wire.BinaryBytes(s) } -// SetBlockAndValidators mutates State variables -// to update block and validators after running EndBlock. -func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, - abciResponses *ABCIResponses) error { +// NextState returns a new State updated according to the header and responses. +func (s State) NextState(header *types.Header, blockPartsHeader types.PartSetHeader, + abciResponses *ABCIResponses) (State, error) { // copy the valset so we can apply changes from EndBlock // and update s.LastValidators and s.Validators @@ -186,13 +122,14 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ 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 fmt.Errorf("Error changing validator set: %v", err) } // change results from this height but only applies to the next height - s.LastHeightValidatorsChanged = header.Height + 1 + lastHeightValsChanged = header.Height + 1 } // Update validator accums and set state variables @@ -200,6 +137,7 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ // 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) @@ -208,40 +146,27 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ return fmt.Errorf("Error updating consensus params: %v", err) } // change results from this height but only applies to the next height - s.LastHeightConsensusParamsChanged = header.Height + 1 + lastHeightParamsChanged = header.Height + 1 } - s.setBlockAndValidators(header.Height, - header.NumTxs, - types.BlockID{header.Hash(), blockPartsHeader}, - header.Time, - nextValSet, - nextParams, - abciResponses.ResultsHash()) - return nil -} - -func (s *State) setBlockAndValidators(height int64, - newTxs int64, blockID types.BlockID, blockTime time.Time, - valSet *types.ValidatorSet, - params types.ConsensusParams, - resultsHash []byte) { - - s.LastBlockHeight = height - s.LastBlockTotalTx += newTxs - s.LastBlockID = blockID - s.LastBlockTime = blockTime - - s.LastValidators = s.Validators.Copy() - s.Validators = valSet - - s.ConsensusParams = params - - s.LastResultsHash = resultsHash + return State{ + ChainID: s.ChainID, + LastBlockHeight: header.Height, + LastBlockTotalTx: s.LastBlockTotalTx + header.NumTxs, + LastBlockID: types.BlockID{header.Hash(), blockPartsHeader}, + LastBlockTime: header.Time, + Validators: nextValSet, + LastValidators: s.Validators.Copy(), + LastHeightValidatorsChanged: lastHeightValsChanged, + ConsensusParams: nextParams, + LastHeightConsensusParamsChanged: lastHeightParamsChanged, + LastResultsHash: abciResponses.ResultsHash(), + AppHash: nil, + } } // GetValidators returns the last and current validator sets. -func (s *State) GetValidators() (last *types.ValidatorSet, current *types.ValidatorSet) { +func (s State) GetValidators() (last *types.ValidatorSet, current *types.ValidatorSet) { return s.LastValidators, s.Validators } @@ -252,12 +177,12 @@ func (s *State) GetValidators() (last *types.ValidatorSet, current *types.Valida // file. // // Used during replay and in tests. -func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*State, error) { +func MakeGenesisStateFromFile(genDocFile string) (*State, error) { genDoc, err := MakeGenesisDocFromFile(genDocFile) if err != nil { return nil, err } - return MakeGenesisState(db, genDoc) + return MakeGenesisState(genDoc) } // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file. @@ -274,7 +199,7 @@ func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { } // MakeGenesisState creates state from types.GenesisDoc. -func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) { +func MakeGenesisState(genDoc *types.GenesisDoc) (*State, error) { err := genDoc.ValidateAndComplete() if err != nil { return nil, fmt.Errorf("Error in genesis file: %v", err) @@ -295,7 +220,6 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) { } return &State{ - db: db, ChainID: genDoc.ChainID,