|
|
@ -3,11 +3,13 @@ package state |
|
|
|
import ( |
|
|
|
"fmt" |
|
|
|
|
|
|
|
"github.com/gogo/protobuf/proto" |
|
|
|
dbm "github.com/tendermint/tm-db" |
|
|
|
|
|
|
|
abci "github.com/tendermint/tendermint/abci/types" |
|
|
|
tmmath "github.com/tendermint/tendermint/libs/math" |
|
|
|
tmos "github.com/tendermint/tendermint/libs/os" |
|
|
|
tmstate "github.com/tendermint/tendermint/proto/state" |
|
|
|
tmproto "github.com/tendermint/tendermint/proto/types" |
|
|
|
"github.com/tendermint/tendermint/types" |
|
|
|
) |
|
|
@ -56,6 +58,7 @@ func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State |
|
|
|
// to the database.
|
|
|
|
func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) { |
|
|
|
state := LoadState(stateDB) |
|
|
|
|
|
|
|
if state.IsEmpty() { |
|
|
|
var err error |
|
|
|
state, err = MakeGenesisState(genesisDoc) |
|
|
@ -82,15 +85,21 @@ func loadState(db dbm.DB, key []byte) (state State) { |
|
|
|
return state |
|
|
|
} |
|
|
|
|
|
|
|
err = cdc.UnmarshalBinaryBare(buf, &state) |
|
|
|
sp := new(tmstate.State) |
|
|
|
|
|
|
|
err = proto.Unmarshal(buf, sp) |
|
|
|
if err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
tmos.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed: |
|
|
|
%v\n`, err)) |
|
|
|
%v\n`, err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
|
|
|
|
return state |
|
|
|
sm, err := StateFromProto(sp) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
return *sm |
|
|
|
} |
|
|
|
|
|
|
|
// SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
|
|
|
@ -110,6 +119,7 @@ func saveState(db dbm.DB, state State, key []byte) { |
|
|
|
} |
|
|
|
// Save next validators.
|
|
|
|
saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) |
|
|
|
|
|
|
|
// Save next consensus params.
|
|
|
|
saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams) |
|
|
|
err := db.SetSync(key, state.Bytes()) |
|
|
@ -130,15 +140,6 @@ func BootstrapState(db dbm.DB, state State) error { |
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// 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 { |
|
|
|
DeliverTxs []*abci.ResponseDeliverTx `json:"deliver_txs"` |
|
|
|
EndBlock *abci.ResponseEndBlock `json:"end_block"` |
|
|
|
BeginBlock *abci.ResponseBeginBlock `json:"begin_block"` |
|
|
|
} |
|
|
|
|
|
|
|
// PruneStates deletes states between the given heights (including from, excluding to). It is not
|
|
|
|
// guaranteed to delete all states, since the last checkpointed state and states being pointed to by
|
|
|
|
// e.g. `LastHeightChanged` must remain. The state at to must also exist.
|
|
|
@ -187,12 +188,25 @@ func PruneStates(db dbm.DB, from int64, to int64) error { |
|
|
|
if keepVals[h] { |
|
|
|
v := loadValidatorsInfo(db, h) |
|
|
|
if v.ValidatorSet == nil { |
|
|
|
v.ValidatorSet, err = LoadValidators(db, h) |
|
|
|
|
|
|
|
vip, err := LoadValidators(db, h) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
pvi, err := vip.ToProto() |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
v.ValidatorSet = pvi |
|
|
|
v.LastHeightChanged = h |
|
|
|
batch.Set(calcValidatorsKey(h), v.Bytes()) |
|
|
|
|
|
|
|
bz, err := v.Marshal() |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
batch.Set(calcValidatorsKey(h), bz) |
|
|
|
} |
|
|
|
} else { |
|
|
|
batch.Delete(calcValidatorsKey(h)) |
|
|
@ -206,7 +220,11 @@ func PruneStates(db dbm.DB, from int64, to int64) error { |
|
|
|
return err |
|
|
|
} |
|
|
|
p.LastHeightChanged = h |
|
|
|
batch.Set(calcConsensusParamsKey(h), p.Bytes()) |
|
|
|
bz, err := p.Marshal() |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
batch.Set(calcConsensusParamsKey(h), bz) |
|
|
|
} |
|
|
|
} else { |
|
|
|
batch.Delete(calcConsensusParamsKey(h)) |
|
|
@ -235,42 +253,27 @@ func PruneStates(db dbm.DB, from int64, to int64) error { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// NewABCIResponses returns a new ABCIResponses
|
|
|
|
func NewABCIResponses(block *types.Block) *ABCIResponses { |
|
|
|
resDeliverTxs := make([]*abci.ResponseDeliverTx, len(block.Data.Txs)) |
|
|
|
if len(block.Data.Txs) == 0 { |
|
|
|
// This makes Amino encoding/decoding consistent.
|
|
|
|
resDeliverTxs = nil |
|
|
|
} |
|
|
|
return &ABCIResponses{ |
|
|
|
DeliverTxs: resDeliverTxs, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Bytes serializes the ABCIResponse using go-amino.
|
|
|
|
func (arz *ABCIResponses) Bytes() []byte { |
|
|
|
return cdc.MustMarshalBinaryBare(arz) |
|
|
|
} |
|
|
|
|
|
|
|
func (arz *ABCIResponses) ResultsHash() []byte { |
|
|
|
results := types.NewResults(arz.DeliverTxs) |
|
|
|
// ABCIResponsesResultsHash returns the merkle hash of the deliverTxs within ABCIResponses
|
|
|
|
func ABCIResponsesResultsHash(ar tmstate.ABCIResponses) []byte { |
|
|
|
results := types.NewResults(ar.DeliverTxs) |
|
|
|
return results.Hash() |
|
|
|
} |
|
|
|
|
|
|
|
// 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 LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) { |
|
|
|
func LoadABCIResponses(db dbm.DB, height int64) (*tmstate.ABCIResponses, error) { |
|
|
|
buf, err := db.Get(calcABCIResponsesKey(height)) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
if len(buf) == 0 { |
|
|
|
|
|
|
|
return nil, ErrNoABCIResponsesForHeight{height} |
|
|
|
} |
|
|
|
|
|
|
|
abciResponses := new(ABCIResponses) |
|
|
|
err = cdc.UnmarshalBinaryBare(buf, abciResponses) |
|
|
|
abciResponses := new(tmstate.ABCIResponses) |
|
|
|
err = abciResponses.Unmarshal(buf) |
|
|
|
if err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has |
|
|
@ -287,22 +290,24 @@ func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) { |
|
|
|
// Merkle proofs.
|
|
|
|
//
|
|
|
|
// Exposed for testing.
|
|
|
|
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) { |
|
|
|
db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes()) |
|
|
|
} |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *tmstate.ABCIResponses) { |
|
|
|
var dtxs []*abci.ResponseDeliverTx |
|
|
|
//strip nil values,
|
|
|
|
for _, tx := range abciResponses.DeliverTxs { |
|
|
|
if tx != nil { |
|
|
|
dtxs = append(dtxs, tx) |
|
|
|
} |
|
|
|
} |
|
|
|
abciResponses.DeliverTxs = dtxs |
|
|
|
|
|
|
|
// ValidatorsInfo represents the latest validator set, or the last height it changed
|
|
|
|
type ValidatorsInfo struct { |
|
|
|
ValidatorSet *types.ValidatorSet |
|
|
|
LastHeightChanged int64 |
|
|
|
bz, err := abciResponses.Marshal() |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
db.SetSync(calcABCIResponsesKey(height), bz) |
|
|
|
} |
|
|
|
|
|
|
|
// Bytes serializes the ValidatorsInfo using go-amino.
|
|
|
|
func (valInfo *ValidatorsInfo) Bytes() []byte { |
|
|
|
return cdc.MustMarshalBinaryBare(valInfo) |
|
|
|
} |
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// LoadValidators loads the ValidatorSet for a given height.
|
|
|
|
// Returns ErrNoValSetForHeight if the validator set can't be found for this height.
|
|
|
@ -322,11 +327,28 @@ func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) { |
|
|
|
), |
|
|
|
) |
|
|
|
} |
|
|
|
valInfo2.ValidatorSet.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate
|
|
|
|
|
|
|
|
vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
vs.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate
|
|
|
|
vi2, err := vs.ToProto() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
valInfo2.ValidatorSet = vi2 |
|
|
|
valInfo = valInfo2 |
|
|
|
} |
|
|
|
|
|
|
|
return valInfo.ValidatorSet, nil |
|
|
|
vip, err := types.ValidatorSetFromProto(valInfo.ValidatorSet) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
return vip, nil |
|
|
|
} |
|
|
|
|
|
|
|
func lastStoredHeightFor(height, lastHeightChanged int64) int64 { |
|
|
@ -335,17 +357,18 @@ func lastStoredHeightFor(height, lastHeightChanged int64) int64 { |
|
|
|
} |
|
|
|
|
|
|
|
// CONTRACT: Returned ValidatorsInfo can be mutated.
|
|
|
|
func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { |
|
|
|
func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo { |
|
|
|
buf, err := db.Get(calcValidatorsKey(height)) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
if len(buf) == 0 { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
v := new(ValidatorsInfo) |
|
|
|
err = cdc.UnmarshalBinaryBare(buf, v) |
|
|
|
v := new(tmstate.ValidatorsInfo) |
|
|
|
err = v.Unmarshal(buf) |
|
|
|
if err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed: |
|
|
@ -365,29 +388,30 @@ func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *type |
|
|
|
if lastHeightChanged > height { |
|
|
|
panic("LastHeightChanged cannot be greater than ValidatorsInfo height") |
|
|
|
} |
|
|
|
valInfo := &ValidatorsInfo{ |
|
|
|
valInfo := &tmstate.ValidatorsInfo{ |
|
|
|
LastHeightChanged: lastHeightChanged, |
|
|
|
} |
|
|
|
// Only persist validator set if it was updated or checkpoint height (see
|
|
|
|
// valSetCheckpointInterval) is reached.
|
|
|
|
if height == lastHeightChanged || height%valSetCheckpointInterval == 0 { |
|
|
|
valInfo.ValidatorSet = valSet |
|
|
|
pv, err := valSet.ToProto() |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
valInfo.ValidatorSet = pv |
|
|
|
} |
|
|
|
db.Set(calcValidatorsKey(height), valInfo.Bytes()) |
|
|
|
|
|
|
|
bz, err := valInfo.Marshal() |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
db.Set(calcValidatorsKey(height), bz) |
|
|
|
} |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// ConsensusParamsInfo represents the latest consensus params, or the last height it changed
|
|
|
|
type ConsensusParamsInfo struct { |
|
|
|
ConsensusParams tmproto.ConsensusParams |
|
|
|
LastHeightChanged int64 |
|
|
|
} |
|
|
|
|
|
|
|
// Bytes serializes the ConsensusParamsInfo using go-amino.
|
|
|
|
func (params ConsensusParamsInfo) Bytes() []byte { |
|
|
|
return cdc.MustMarshalBinaryBare(params) |
|
|
|
} |
|
|
|
|
|
|
|
// LoadConsensusParams loads the ConsensusParams for a given height.
|
|
|
|
func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, error) { |
|
|
@ -409,13 +433,14 @@ func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, erro |
|
|
|
), |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
paramsInfo = paramsInfo2 |
|
|
|
} |
|
|
|
|
|
|
|
return paramsInfo.ConsensusParams, nil |
|
|
|
} |
|
|
|
|
|
|
|
func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { |
|
|
|
func loadConsensusParamsInfo(db dbm.DB, height int64) *tmstate.ConsensusParamsInfo { |
|
|
|
buf, err := db.Get(calcConsensusParamsKey(height)) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
@ -424,9 +449,8 @@ func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
paramsInfo := new(ConsensusParamsInfo) |
|
|
|
err = cdc.UnmarshalBinaryBare(buf, paramsInfo) |
|
|
|
if err != nil { |
|
|
|
paramsInfo := new(tmstate.ConsensusParamsInfo) |
|
|
|
if err = paramsInfo.Unmarshal(buf); err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed: |
|
|
|
%v\n`, err)) |
|
|
@ -441,11 +465,17 @@ func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { |
|
|
|
// If the consensus params did not change after processing the latest block,
|
|
|
|
// only the last height for which they changed is persisted.
|
|
|
|
func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params tmproto.ConsensusParams) { |
|
|
|
paramsInfo := &ConsensusParamsInfo{ |
|
|
|
paramsInfo := &tmstate.ConsensusParamsInfo{ |
|
|
|
LastHeightChanged: changeHeight, |
|
|
|
} |
|
|
|
|
|
|
|
if changeHeight == nextHeight { |
|
|
|
paramsInfo.ConsensusParams = params |
|
|
|
} |
|
|
|
db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes()) |
|
|
|
bz, err := paramsInfo.Marshal() |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
db.Set(calcConsensusParamsKey(nextHeight), bz) |
|
|
|
} |