|
|
@ -4,11 +4,8 @@ import ( |
|
|
|
"bytes" |
|
|
|
"fmt" |
|
|
|
"io/ioutil" |
|
|
|
"sync" |
|
|
|
"time" |
|
|
|
|
|
|
|
abci "github.com/tendermint/abci/types" |
|
|
|
|
|
|
|
cmn "github.com/tendermint/tmlibs/common" |
|
|
|
dbm "github.com/tendermint/tmlibs/db" |
|
|
|
"github.com/tendermint/tmlibs/log" |
|
|
@ -44,9 +41,7 @@ func calcABCIResponsesKey(height int64) []byte { |
|
|
|
// but the fields should only be changed by calling state.SetBlockAndValidators.
|
|
|
|
// NOTE: not goroutine-safe.
|
|
|
|
type State struct { |
|
|
|
// mtx for writing to db
|
|
|
|
mtx sync.Mutex |
|
|
|
db dbm.DB |
|
|
|
db dbm.DB |
|
|
|
|
|
|
|
// Immutable
|
|
|
|
ChainID string |
|
|
@ -82,6 +77,10 @@ type State struct { |
|
|
|
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.
|
|
|
@ -157,150 +156,13 @@ func (s *State) Copy() *State { |
|
|
|
|
|
|
|
// Save persists the State to the database.
|
|
|
|
func (s *State) Save() { |
|
|
|
s.mtx.Lock() |
|
|
|
defer s.mtx.Unlock() |
|
|
|
|
|
|
|
s.saveValidatorsInfo() |
|
|
|
s.saveConsensusParamsInfo() |
|
|
|
s.db.SetSync(stateKey, s.Bytes()) |
|
|
|
} |
|
|
|
|
|
|
|
// SaveABCIResponses persists the ABCIResponses to the database.
|
|
|
|
// This is useful in case we crash after app.Commit and before s.Save().
|
|
|
|
// Responses are indexed by height so they can also be loaded later to produce Merkle proofs.
|
|
|
|
func (s *State) SaveABCIResponses(height int64, abciResponses *ABCIResponses) { |
|
|
|
s.db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes()) |
|
|
|
} |
|
|
|
|
|
|
|
// LoadABCIResponses loads the ABCIResponses for the given height from the database.
|
|
|
|
// This is useful for recovering from crashes where we called app.Commit and before we called
|
|
|
|
// s.Save(). It can also be used to produce Merkle proofs of the result of txs.
|
|
|
|
func (s *State) LoadABCIResponses(height int64) (*ABCIResponses, error) { |
|
|
|
buf := s.db.Get(calcABCIResponsesKey(height)) |
|
|
|
if len(buf) == 0 { |
|
|
|
return nil, ErrNoABCIResponsesForHeight{height} |
|
|
|
} |
|
|
|
|
|
|
|
abciResponses := new(ABCIResponses) |
|
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error) |
|
|
|
wire.ReadBinaryPtr(abciResponses, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt(`LoadABCIResponses: Data has been corrupted or its spec has |
|
|
|
changed: %v\n`, *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
|
|
|
|
return abciResponses, nil |
|
|
|
} |
|
|
|
|
|
|
|
// LoadValidators loads the ValidatorSet for a given height.
|
|
|
|
// Returns ErrNoValSetForHeight if the validator set can't be found for this height.
|
|
|
|
func (s *State) LoadValidators(height int64) (*types.ValidatorSet, error) { |
|
|
|
valInfo := s.loadValidatorsInfo(height) |
|
|
|
if valInfo == nil { |
|
|
|
return nil, ErrNoValSetForHeight{height} |
|
|
|
} |
|
|
|
|
|
|
|
if valInfo.ValidatorSet == nil { |
|
|
|
valInfo = s.loadValidatorsInfo(valInfo.LastHeightChanged) |
|
|
|
if valInfo == nil { |
|
|
|
cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at height %d as |
|
|
|
last changed from height %d`, valInfo.LastHeightChanged, height)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return valInfo.ValidatorSet, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (s *State) loadValidatorsInfo(height int64) *ValidatorsInfo { |
|
|
|
buf := s.db.Get(calcValidatorsKey(height)) |
|
|
|
if len(buf) == 0 { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
v := new(ValidatorsInfo) |
|
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error) |
|
|
|
wire.ReadBinaryPtr(v, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt(`LoadValidators: Data has been corrupted or its spec has changed: |
|
|
|
%v\n`, *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
|
|
|
|
return v |
|
|
|
} |
|
|
|
|
|
|
|
// saveValidatorsInfo persists the validator set for the next block to disk.
|
|
|
|
// It should be called from s.Save(), right before the state itself is persisted.
|
|
|
|
// If the validator set did not change after processing the latest block,
|
|
|
|
// only the last height for which the validators changed is persisted.
|
|
|
|
func (s *State) saveValidatorsInfo() { |
|
|
|
changeHeight := s.LastHeightValidatorsChanged |
|
|
|
nextHeight := s.LastBlockHeight + 1 |
|
|
|
valInfo := &ValidatorsInfo{ |
|
|
|
LastHeightChanged: changeHeight, |
|
|
|
} |
|
|
|
if changeHeight == nextHeight { |
|
|
|
valInfo.ValidatorSet = s.Validators |
|
|
|
} |
|
|
|
s.db.SetSync(calcValidatorsKey(nextHeight), valInfo.Bytes()) |
|
|
|
} |
|
|
|
|
|
|
|
// LoadConsensusParams loads the ConsensusParams for a given height.
|
|
|
|
func (s *State) LoadConsensusParams(height int64) (types.ConsensusParams, error) { |
|
|
|
empty := types.ConsensusParams{} |
|
|
|
|
|
|
|
paramsInfo := s.loadConsensusParamsInfo(height) |
|
|
|
if paramsInfo == nil { |
|
|
|
return empty, ErrNoConsensusParamsForHeight{height} |
|
|
|
} |
|
|
|
|
|
|
|
if paramsInfo.ConsensusParams == empty { |
|
|
|
paramsInfo = s.loadConsensusParamsInfo(paramsInfo.LastHeightChanged) |
|
|
|
if paramsInfo == nil { |
|
|
|
cmn.PanicSanity(fmt.Sprintf(`Couldn't find consensus params at height %d as |
|
|
|
last changed from height %d`, paramsInfo.LastHeightChanged, height)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return paramsInfo.ConsensusParams, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (s *State) loadConsensusParamsInfo(height int64) *ConsensusParamsInfo { |
|
|
|
buf := s.db.Get(calcConsensusParamsKey(height)) |
|
|
|
if len(buf) == 0 { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
paramsInfo := new(ConsensusParamsInfo) |
|
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error) |
|
|
|
wire.ReadBinaryPtr(paramsInfo, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt(`LoadConsensusParams: Data has been corrupted or its spec has changed: |
|
|
|
%v\n`, *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
|
|
|
|
return paramsInfo |
|
|
|
} |
|
|
|
|
|
|
|
// saveConsensusParamsInfo persists the consensus params for the next block to disk.
|
|
|
|
// It should be called from s.Save(), right before the state itself is persisted.
|
|
|
|
// If the consensus params did not change after processing the latest block,
|
|
|
|
// only the last height for which they changed is persisted.
|
|
|
|
func (s *State) saveConsensusParamsInfo() { |
|
|
|
changeHeight := s.LastHeightConsensusParamsChanged |
|
|
|
nextHeight := s.LastBlockHeight + 1 |
|
|
|
paramsInfo := &ConsensusParamsInfo{ |
|
|
|
LastHeightChanged: changeHeight, |
|
|
|
} |
|
|
|
if changeHeight == nextHeight { |
|
|
|
paramsInfo.ConsensusParams = s.ConsensusParams |
|
|
|
} |
|
|
|
s.db.SetSync(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes()) |
|
|
|
// 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.
|
|
|
@ -383,96 +245,6 @@ func (s *State) GetValidators() (last *types.ValidatorSet, current *types.Valida |
|
|
|
return s.LastValidators, s.Validators |
|
|
|
} |
|
|
|
|
|
|
|
// VerifyEvidence verifies the evidence fully by checking it is internally
|
|
|
|
// consistent and corresponds to an existing or previous validator.
|
|
|
|
// 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 (s *State) VerifyEvidence(evidence types.Evidence) (priority int64, err error) { |
|
|
|
evidenceAge := s.LastBlockHeight - evidence.Height() |
|
|
|
maxAge := s.ConsensusParams.EvidenceParams.MaxAge |
|
|
|
if evidenceAge > maxAge { |
|
|
|
return priority, fmt.Errorf("Evidence from height %d is too old. Min height is %d", |
|
|
|
evidence.Height(), s.LastBlockHeight-maxAge) |
|
|
|
} |
|
|
|
|
|
|
|
if err := evidence.Verify(s.ChainID); err != nil { |
|
|
|
return priority, err |
|
|
|
} |
|
|
|
|
|
|
|
// The address must have been an active validator at the height
|
|
|
|
ev := evidence |
|
|
|
height, addr, idx := ev.Height(), ev.Address(), ev.Index() |
|
|
|
valset, err := s.LoadValidators(height) |
|
|
|
if err != nil { |
|
|
|
// XXX/TODO: what do we do if we can't load the valset?
|
|
|
|
// eg. if we have pruned the state or height is too high?
|
|
|
|
return priority, err |
|
|
|
} |
|
|
|
valIdx, val := valset.GetByAddress(addr) |
|
|
|
if val == nil { |
|
|
|
return priority, fmt.Errorf("Address %X was not a validator at height %d", addr, height) |
|
|
|
} else if idx != valIdx { |
|
|
|
return priority, fmt.Errorf("Address %X was validator %d at height %d, not %d", addr, valIdx, height, idx) |
|
|
|
} |
|
|
|
|
|
|
|
priority = val.VotingPower |
|
|
|
return priority, nil |
|
|
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// ABCIResponses retains the responses
|
|
|
|
// of the various ABCI calls during block processing.
|
|
|
|
// It is persisted to disk for each height before calling Commit.
|
|
|
|
type ABCIResponses struct { |
|
|
|
DeliverTx []*abci.ResponseDeliverTx |
|
|
|
EndBlock *abci.ResponseEndBlock |
|
|
|
} |
|
|
|
|
|
|
|
// NewABCIResponses returns a new ABCIResponses
|
|
|
|
func NewABCIResponses(block *types.Block) *ABCIResponses { |
|
|
|
return &ABCIResponses{ |
|
|
|
DeliverTx: make([]*abci.ResponseDeliverTx, block.NumTxs), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Bytes serializes the ABCIResponse using go-wire
|
|
|
|
func (a *ABCIResponses) Bytes() []byte { |
|
|
|
return wire.BinaryBytes(*a) |
|
|
|
} |
|
|
|
|
|
|
|
func (a *ABCIResponses) ResultsHash() []byte { |
|
|
|
results := types.NewResults(a.DeliverTx) |
|
|
|
return results.Hash() |
|
|
|
} |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// ValidatorsInfo represents the latest validator set, or the last height it changed
|
|
|
|
type ValidatorsInfo struct { |
|
|
|
ValidatorSet *types.ValidatorSet |
|
|
|
LastHeightChanged int64 |
|
|
|
} |
|
|
|
|
|
|
|
// Bytes serializes the ValidatorsInfo using go-wire
|
|
|
|
func (valInfo *ValidatorsInfo) Bytes() []byte { |
|
|
|
return wire.BinaryBytes(*valInfo) |
|
|
|
} |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// ConsensusParamsInfo represents the latest consensus params, or the last height it changed
|
|
|
|
type ConsensusParamsInfo struct { |
|
|
|
ConsensusParams types.ConsensusParams |
|
|
|
LastHeightChanged int64 |
|
|
|
} |
|
|
|
|
|
|
|
// Bytes serializes the ConsensusParamsInfo using go-wire
|
|
|
|
func (params ConsensusParamsInfo) Bytes() []byte { |
|
|
|
return wire.BinaryBytes(params) |
|
|
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
// Genesis
|
|
|
|
|
|
|
|