mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-13 21:31:23 +00:00
Save/Load Results for every height
Add some tests. Behaves like saving validator set, except it always saves at each height instead of a reference to last changed.
This commit is contained in:
committed by
Ethan Buchman
parent
f870a49f42
commit
632cc918b4
@ -41,6 +41,10 @@ type (
|
|||||||
ErrNoConsensusParamsForHeight struct {
|
ErrNoConsensusParamsForHeight struct {
|
||||||
Height int64
|
Height int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrNoResultsForHeight struct {
|
||||||
|
Height int64
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e ErrUnknownBlock) Error() string {
|
func (e ErrUnknownBlock) Error() string {
|
||||||
@ -69,3 +73,7 @@ func (e ErrNoValSetForHeight) Error() string {
|
|||||||
func (e ErrNoConsensusParamsForHeight) Error() string {
|
func (e ErrNoConsensusParamsForHeight) Error() string {
|
||||||
return cmn.Fmt("Could not find consensus params for height #%d", e.Height)
|
return cmn.Fmt("Could not find consensus params for height #%d", e.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e ErrNoResultsForHeight) Error() string {
|
||||||
|
return cmn.Fmt("Could not find results for height #%d", e.Height)
|
||||||
|
}
|
||||||
|
@ -35,6 +35,10 @@ func calcConsensusParamsKey(height int64) []byte {
|
|||||||
return []byte(cmn.Fmt("consensusParamsKey:%v", height))
|
return []byte(cmn.Fmt("consensusParamsKey:%v", height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calcResultsKey(height int64) []byte {
|
||||||
|
return []byte(cmn.Fmt("resultsKey:%v", height))
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// State is a short description of the latest committed block of the Tendermint consensus.
|
// State is a short description of the latest committed block of the Tendermint consensus.
|
||||||
@ -75,7 +79,7 @@ type State struct {
|
|||||||
LastHeightConsensusParamsChanged int64
|
LastHeightConsensusParamsChanged int64
|
||||||
|
|
||||||
// Store LastABCIResults along with hash
|
// Store LastABCIResults along with hash
|
||||||
LastResults ABCIResults
|
LastResults ABCIResults // TODO: remove??
|
||||||
LastResultHash []byte
|
LastResultHash []byte
|
||||||
|
|
||||||
// The latest AppHash we've received from calling abci.Commit()
|
// The latest AppHash we've received from calling abci.Commit()
|
||||||
@ -163,6 +167,7 @@ func (s *State) Save() {
|
|||||||
|
|
||||||
s.saveValidatorsInfo()
|
s.saveValidatorsInfo()
|
||||||
s.saveConsensusParamsInfo()
|
s.saveConsensusParamsInfo()
|
||||||
|
s.saveResults()
|
||||||
s.db.SetSync(stateKey, s.Bytes())
|
s.db.SetSync(stateKey, s.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,6 +307,39 @@ func (s *State) saveConsensusParamsInfo() {
|
|||||||
s.db.SetSync(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
|
s.db.SetSync(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadResults loads the ABCIResults for a given height.
|
||||||
|
func (s *State) LoadResults(height int64) (ABCIResults, error) {
|
||||||
|
resInfo := s.loadResults(height)
|
||||||
|
if resInfo == nil {
|
||||||
|
return nil, ErrNoResultsForHeight{height}
|
||||||
|
}
|
||||||
|
return resInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) loadResults(height int64) ABCIResults {
|
||||||
|
buf := s.db.Get(calcResultsKey(height))
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v := new(ABCIResults)
|
||||||
|
err := wire.ReadBinaryBytes(buf, v)
|
||||||
|
if err != nil {
|
||||||
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
||||||
|
cmn.Exit(cmn.Fmt(`LoadResults: Data has been corrupted or its spec has changed:
|
||||||
|
%v\n`, err))
|
||||||
|
}
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveResults persists the results for the last block to disk.
|
||||||
|
// It should be called from s.Save(), right before the state itself is persisted.
|
||||||
|
func (s *State) saveResults() {
|
||||||
|
nextHeight := s.LastBlockHeight + 1
|
||||||
|
results := s.LastResults
|
||||||
|
s.db.SetSync(calcResultsKey(nextHeight), results.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
// Equals returns true if the States are identical.
|
// 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())
|
return bytes.Equal(s.Bytes(), s2.Bytes())
|
||||||
@ -444,6 +482,11 @@ func NewResults(del []*abci.ResponseDeliverTx) ABCIResults {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bytes serializes the ABCIResponse using go-wire
|
||||||
|
func (a ABCIResults) Bytes() []byte {
|
||||||
|
return wire.BinaryBytes(a)
|
||||||
|
}
|
||||||
|
|
||||||
// Hash returns a merkle hash of all results
|
// Hash returns a merkle hash of all results
|
||||||
func (a ABCIResults) Hash() []byte {
|
func (a ABCIResults) Hash() []byte {
|
||||||
return merkle.SimpleHashFromHashables(a.toHashables())
|
return merkle.SimpleHashFromHashables(a.toHashables())
|
||||||
|
@ -313,6 +313,73 @@ func TestABCIResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestResultsSaveLoad tests saving and loading abci results.
|
||||||
|
func TestResultsSaveLoad(t *testing.T) {
|
||||||
|
tearDown, _, state := setupTestCase(t)
|
||||||
|
defer tearDown(t)
|
||||||
|
// nolint: vetshadow
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
cases := [...]struct {
|
||||||
|
// height is implied index+2
|
||||||
|
// as block 1 is created from genesis
|
||||||
|
added []*abci.ResponseDeliverTx
|
||||||
|
expected ABCIResults
|
||||||
|
}{
|
||||||
|
0: {
|
||||||
|
[]*abci.ResponseDeliverTx{},
|
||||||
|
ABCIResults{},
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
[]*abci.ResponseDeliverTx{
|
||||||
|
{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
|
||||||
|
},
|
||||||
|
ABCIResults{
|
||||||
|
{32, []byte("Hello")},
|
||||||
|
}},
|
||||||
|
2: {
|
||||||
|
[]*abci.ResponseDeliverTx{
|
||||||
|
{Code: 383},
|
||||||
|
{Data: []byte("Gotcha!"),
|
||||||
|
Tags: []*abci.KVPair{
|
||||||
|
abci.KVPairInt("a", 1),
|
||||||
|
abci.KVPairString("build", "stuff"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
ABCIResults{
|
||||||
|
{383, []byte{}},
|
||||||
|
{0, []byte("Gotcha!")},
|
||||||
|
}},
|
||||||
|
3: {
|
||||||
|
nil,
|
||||||
|
ABCIResults{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// query all before, should return error
|
||||||
|
for i := range cases {
|
||||||
|
h := int64(i + 2)
|
||||||
|
res, err := state.LoadResults(h)
|
||||||
|
assert.Error(err, "%d: %#v", i, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all cases
|
||||||
|
for i, tc := range cases {
|
||||||
|
h := int64(i + 1) // last block height, one below what we save
|
||||||
|
header, parts, responses := makeHeaderPartsResults(state, h, tc.added)
|
||||||
|
state.SetBlockAndValidators(header, parts, responses)
|
||||||
|
state.Save()
|
||||||
|
}
|
||||||
|
|
||||||
|
// query all before, should return expected value
|
||||||
|
for i, tc := range cases {
|
||||||
|
h := int64(i + 2)
|
||||||
|
res, err := state.LoadResults(h)
|
||||||
|
assert.NoError(err, "%d", i)
|
||||||
|
assert.Equal(tc.expected, res, "%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func makeParams(blockBytes, blockTx, blockGas, txBytes,
|
func makeParams(blockBytes, blockTx, blockGas, txBytes,
|
||||||
txGas, partSize int) types.ConsensusParams {
|
txGas, partSize int) types.ConsensusParams {
|
||||||
|
|
||||||
@ -510,3 +577,15 @@ type paramsChangeTestCase struct {
|
|||||||
height int64
|
height int64
|
||||||
params types.ConsensusParams
|
params types.ConsensusParams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeHeaderPartsResults(state *State, height int64,
|
||||||
|
results []*abci.ResponseDeliverTx) (*types.Header, types.PartSetHeader, *ABCIResponses) {
|
||||||
|
|
||||||
|
block := makeBlock(state, height)
|
||||||
|
abciResponses := &ABCIResponses{
|
||||||
|
Height: height,
|
||||||
|
DeliverTx: results,
|
||||||
|
EndBlock: &abci.ResponseEndBlock{},
|
||||||
|
}
|
||||||
|
return block.Header, types.PartSetHeader{}, abciResponses
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user