Skip to content

Commit

Permalink
feat: implement querying for commit hash and proofs (#156)
Browse files Browse the repository at this point in the history
* implement store proof and commit hash query

* move proods query to a separate function

* update multistore Query comment
  • Loading branch information
p0mvn authored Mar 25, 2022
1 parent 888cabe commit 2ed314c
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
39 changes: 33 additions & 6 deletions store/rootmulti/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ import (
"github.com/pkg/errors"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/proto/tendermint/crypto"
dbm "github.com/tendermint/tm-db"
)

const (
latestVersionKey = "s/latest"
commitInfoKeyFmt = "s/%d" // s/<version>

proofsPath = "proofs"

// Do not change chunk size without new snapshot format (must be uniform across nodes)
snapshotChunkSize = uint64(10e6)
snapshotBufferSize = int(snapshotChunkSize)
Expand Down Expand Up @@ -564,22 +567,28 @@ func (rs *Store) getStoreByName(name string) types.Store {
// Query calls substore.Query with the same `req` where `req.Path` is
// modified to remove the substore prefix.
// Ie. `req.Path` here is `/<substore>/<path>`, and trimmed to `/<path>` for the substore.
// TODO: add proof for `multistore -> substore`.
// Special case: if `req.Path` is `/proofs`, the commit hash is included
// as response value. In addition, proofs of every store are appended to the response for
// the requested height
func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery {
path := req.Path
storeName, subpath, err := parsePath(path)
firstPath, subpath, err := parsePath(path)
if err != nil {
return sdkerrors.QueryResult(err)
}

store := rs.getStoreByName(storeName)
if firstPath == proofsPath {
return rs.doProofsQuery(req)
}

store := rs.getStoreByName(firstPath)
if store == nil {
return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no such store: %s", storeName))
return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no such store: %s", firstPath))
}

queryable, ok := store.(types.Queryable)
if !ok {
return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "store %s (type %T) doesn't support queries", storeName, store))
return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "store %s (type %T) doesn't support queries", firstPath, store))
}

// trim the path and make the query
Expand Down Expand Up @@ -609,7 +618,7 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery {
}

// Restore origin path and append proof op.
res.ProofOps.Ops = append(res.ProofOps.Ops, commitInfo.ProofOp(storeName))
res.ProofOps.Ops = append(res.ProofOps.Ops, commitInfo.ProofOp(firstPath))

return res
}
Expand Down Expand Up @@ -1005,6 +1014,24 @@ type storeParams struct {
initialVersion uint64
}

func (rs *Store) doProofsQuery(req abci.RequestQuery) abci.ResponseQuery {
commitInfo, err := getCommitInfo(rs.db, req.Height)
if err != nil {
return sdkerrors.QueryResult(err)
}
res := abci.ResponseQuery{
Height: req.Height,
Key: []byte(proofsPath),
Value: commitInfo.CommitID().Hash,
ProofOps: &crypto.ProofOps{Ops: make([]crypto.ProofOp, 0, len(commitInfo.StoreInfos))},
}

for _, storeInfo := range commitInfo.StoreInfos {
res.ProofOps.Ops = append(res.ProofOps.Ops, commitInfo.ProofOp(storeInfo.Name))
}
return res
}

func getLatestVersion(db dbm.DB) int64 {
bz, err := db.Get([]byte(latestVersionKey))
if err != nil {
Expand Down
26 changes: 23 additions & 3 deletions store/rootmulti/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ func TestMultiStoreQuery(t *testing.T) {
k2, v2 := []byte("water"), []byte("flows")
// v3 := []byte("is cold")

cid := multi.Commit()
cid1 := multi.Commit()

// Make sure we can get by name.
garbage := multi.getStoreByName("bad-name")
Expand All @@ -431,8 +431,8 @@ func TestMultiStoreQuery(t *testing.T) {
store2.Set(k2, v2)

// Commit the multistore.
cid = multi.Commit()
ver := cid.Version
cid2 := multi.Commit()
ver := cid2.Version

// Reload multistore from database
multi = newMultiStoreWithMounts(db, pruningTypes.NewPruningOptions(pruningTypes.PruningNothing))
Expand Down Expand Up @@ -474,6 +474,26 @@ func TestMultiStoreQuery(t *testing.T) {
qres = multi.Query(query)
require.EqualValues(t, 0, qres.Code)
require.Equal(t, v2, qres.Value)

// Test proofs latest height
query.Path = fmt.Sprintf("/%s", proofsPath)
qres = multi.Query(query)
require.EqualValues(t, 0, qres.Code)
require.NotNil(t, qres.ProofOps)
require.Equal(t, []byte(proofsPath), qres.Key)
require.Equal(t, cid2.Hash, qres.Value)
require.Equal(t, cid2.Version, qres.Height)
require.Equal(t, 3, len(qres.ProofOps.Ops)) // 3 mounted stores

// Test proofs second latest height
query.Height = query.Height - 1
qres = multi.Query(query)
require.EqualValues(t, 0, qres.Code)
require.NotNil(t, qres.ProofOps)
require.Equal(t, []byte(proofsPath), qres.Key)
require.Equal(t, cid1.Hash, qres.Value)
require.Equal(t, cid1.Version, qres.Height)
require.Equal(t, 3, len(qres.ProofOps.Ops)) // 3 mounted stores
}

func TestMultiStore_Pruning(t *testing.T) {
Expand Down

0 comments on commit 2ed314c

Please sign in to comment.