From bc325c4d1cdf499f219c79596db0b4a58c1523f5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 30 Jan 2018 15:20:38 +0100 Subject: [PATCH] Add Query routing to rootMultiStore --- store/rootmultistore.go | 52 ++++++++++++++++++++++++++++++++++++++++- store/types.go | 1 + types/errors.go | 3 ++- types/result.go | 8 +++++++ types/store.go | 9 +++++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 2a0c831d81..3e6fde5c34 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -2,10 +2,13 @@ package store import ( "fmt" + "strings" + "golang.org/x/crypto/ripemd160" + + abci "github.com/tendermint/abci/types" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/merkle" - "golang.org/x/crypto/ripemd160" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -28,6 +31,7 @@ type rootMultiStore struct { } var _ CommitMultiStore = (*rootMultiStore)(nil) +var _ Queryable = (*rootMultiStore)(nil) func NewCommitMultiStore(db dbm.DB) *rootMultiStore { return &rootMultiStore{ @@ -185,6 +189,52 @@ func (rs *rootMultiStore) GetStoreByName(name string) Store { return rs.stores[key] } +//---------------------- Query ------------------ + +func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { + // Query just routes this to a substore. + path := req.Path + storeName, subpath, err := parsePath(path) + if err != nil { + return err.Result().ToQuery() + } + + store := rs.GetStoreByName(storeName) + if store == nil { + msg := fmt.Sprintf("no such store: %s", storeName) + return sdk.ErrUnknownRequest(msg).Result().ToQuery() + } + query, ok := store.(Queryable) + if !ok { + msg := fmt.Sprintf("store %s doesn't support queries", storeName) + return sdk.ErrUnknownRequest(msg).Result().ToQuery() + } + + // trim the path and make the query + req.Path = subpath + res := query.Query(req) + + // Note: later we have to think about adding information about + // the multistore -> store path to the proof + return res +} + +// parsePath expects a format like /[/] +// Must start with /, subpath may be empty +// Returns error if it doesn't start with / +func parsePath(path string) (storeName string, subpath string, err sdk.Error) { + if !strings.HasPrefix(path, "/") { + err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) + return + } + paths := strings.SplitN(path[1:], "/", 2) + storeName = paths[0] + if len(paths) == 2 { + subpath = paths[1] + } + return +} + //---------------------------------------- func (rs *rootMultiStore) loadCommitStoreFromParams(id CommitID, params storeParams) (store CommitStore, err error) { diff --git a/store/types.go b/store/types.go index e6946fb4d5..ec842d9cf8 100644 --- a/store/types.go +++ b/store/types.go @@ -19,3 +19,4 @@ type CacheWrap = types.CacheWrap type CommitID = types.CommitID type StoreKey = types.StoreKey type StoreType = types.StoreType +type Queryable = types.Queryable diff --git a/types/errors.go b/types/errors.go index 3f93045f69..a878149313 100644 --- a/types/errors.go +++ b/types/errors.go @@ -2,8 +2,9 @@ package types import ( "fmt" - "github.com/tendermint/go-crypto" "runtime" + + "github.com/tendermint/go-crypto" ) type CodeType uint32 diff --git a/types/result.go b/types/result.go index 412a9778de..c1afec00ce 100644 --- a/types/result.go +++ b/types/result.go @@ -38,3 +38,11 @@ type Result struct { func (res Result) IsOK() bool { return res.Code.IsOK() } + +// ToQuery allows us to return sdk.Error.Result() in query responses +func (res Result) ToQuery() abci.ResponseQuery { + return abci.ResponseQuery{ + Code: uint32(res.Code), + Log: res.Log, + } +} diff --git a/types/store.go b/types/store.go index 5847091a41..e9894d0d61 100644 --- a/types/store.go +++ b/types/store.go @@ -3,6 +3,7 @@ package types import ( "fmt" + abci "github.com/tendermint/abci/types" dbm "github.com/tendermint/tmlibs/db" ) @@ -25,6 +26,14 @@ type CommitStore interface { Store } +// Queryable allows a Store to expose internal state to the abci.Query +// interface. Multistore can route requests to the proper Store. +// +// This is an optional, but useful extension to any CommitStore +type Queryable interface { + Query(abci.RequestQuery) abci.ResponseQuery +} + //---------------------------------------- // MultiStore