Browse Source

Add ABCIResults with Hash and Proof to State

State maintains LastResultsHash
Verify that we can produce unique hashes for each result,
and provide valid proofs from the root hash.
pull/999/head
Ethan Frey 7 years ago
committed by Ethan Buchman
parent
commit
f870a49f42
2 changed files with 101 additions and 2 deletions
  1. +67
    -2
      state/state.go
  2. +34
    -0
      state/state_test.go

+ 67
- 2
state/state.go View File

@ -8,10 +8,13 @@ import (
"time" "time"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-wire/data"
"golang.org/x/crypto/ripemd160"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
"github.com/tendermint/tmlibs/merkle"
wire "github.com/tendermint/go-wire" wire "github.com/tendermint/go-wire"
@ -71,6 +74,10 @@ type State struct {
LastConsensusParams types.ConsensusParams LastConsensusParams types.ConsensusParams
LastHeightConsensusParamsChanged int64 LastHeightConsensusParamsChanged int64
// Store LastABCIResults along with hash
LastResults ABCIResults
LastResultHash []byte
// The latest AppHash we've received from calling abci.Commit() // The latest AppHash we've received from calling abci.Commit()
AppHash []byte AppHash []byte
@ -346,14 +353,16 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ
types.BlockID{header.Hash(), blockPartsHeader}, types.BlockID{header.Hash(), blockPartsHeader},
header.Time, header.Time,
nextValSet, nextValSet,
nextParams)
nextParams,
NewResults(abciResponses.DeliverTx))
return nil return nil
} }
func (s *State) setBlockAndValidators(height int64, func (s *State) setBlockAndValidators(height int64,
newTxs int64, blockID types.BlockID, blockTime time.Time, newTxs int64, blockID types.BlockID, blockTime time.Time,
valSet *types.ValidatorSet, valSet *types.ValidatorSet,
params types.ConsensusParams) {
params types.ConsensusParams,
results ABCIResults) {
s.LastBlockHeight = height s.LastBlockHeight = height
s.LastBlockTotalTx += newTxs s.LastBlockTotalTx += newTxs
@ -365,6 +374,9 @@ func (s *State) setBlockAndValidators(height int64,
s.LastConsensusParams = s.ConsensusParams s.LastConsensusParams = s.ConsensusParams
s.ConsensusParams = params s.ConsensusParams = params
s.LastResults = results
s.LastResultHash = results.Hash()
} }
// GetValidators returns the last and current validator sets. // GetValidators returns the last and current validator sets.
@ -401,6 +413,59 @@ func (a *ABCIResponses) Bytes() []byte {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// ABCIResult is just the essential info to prove
// success/failure of a DeliverTx
type ABCIResult struct {
Code uint32 `json:"code"`
Data data.Bytes `json:"data"`
}
// Hash creates a canonical json hash of the ABCIResult
func (a ABCIResult) Hash() []byte {
// stupid canonical json output, easy to check in any language
bs := fmt.Sprintf(`{"code":%d,"data":"%s"}`, a.Code, a.Data)
var hasher = ripemd160.New()
hasher.Write([]byte(bs))
return hasher.Sum(nil)
}
// ABCIResults wraps the deliver tx results to return a proof
type ABCIResults []ABCIResult
// NewResults creates ABCIResults from ResponseDeliverTx
func NewResults(del []*abci.ResponseDeliverTx) ABCIResults {
res := make(ABCIResults, len(del))
for i, d := range del {
res[i] = ABCIResult{
Code: d.Code,
Data: d.Data,
}
}
return res
}
// Hash returns a merkle hash of all results
func (a ABCIResults) Hash() []byte {
return merkle.SimpleHashFromHashables(a.toHashables())
}
// ProveResult returns a merkle proof of one result from the set
func (a ABCIResults) ProveResult(i int) merkle.SimpleProof {
_, proofs := merkle.SimpleProofsFromHashables(a.toHashables())
return *proofs[i]
}
func (a ABCIResults) toHashables() []merkle.Hashable {
l := len(a)
hashables := make([]merkle.Hashable, l)
for i := 0; i < l; i++ {
hashables[i] = a[i]
}
return hashables
}
//-----------------------------------------------------------------------------
// ValidatorsInfo represents the latest validator set, or the last height it changed // ValidatorsInfo represents the latest validator set, or the last height it changed
type ValidatorsInfo struct { type ValidatorsInfo struct {
ValidatorSet *types.ValidatorSet ValidatorSet *types.ValidatorSet


+ 34
- 0
state/state_test.go View File

@ -279,6 +279,40 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
} }
} }
func TestABCIResults(t *testing.T) {
a := ABCIResult{Code: 0, Data: nil}
b := ABCIResult{Code: 0, Data: []byte{}}
c := ABCIResult{Code: 0, Data: []byte("one")}
d := ABCIResult{Code: 14, Data: nil}
e := ABCIResult{Code: 14, Data: []byte("foo")}
f := ABCIResult{Code: 14, Data: []byte("bar")}
// nil and []byte{} should produce same hash
assert.Equal(t, a.Hash(), b.Hash())
// a and b should be the same, don't go in results
results := ABCIResults{a, c, d, e, f}
// make sure each result hashes properly
var last []byte
for i, res := range results {
h := res.Hash()
assert.NotEqual(t, last, h, "%d", i)
last = h
}
// make sure that we can get a root hash from results
// and verify proofs
root := results.Hash()
assert.NotEmpty(t, root)
for i, res := range results {
proof := results.ProveResult(i)
valid := proof.Verify(i, len(results), res.Hash(), root)
assert.True(t, valid, "%d", i)
}
}
func makeParams(blockBytes, blockTx, blockGas, txBytes, func makeParams(blockBytes, blockTx, blockGas, txBytes,
txGas, partSize int) types.ConsensusParams { txGas, partSize int) types.ConsensusParams {


Loading…
Cancel
Save