CommitMultiStore, and SetCommitStoreLoader->SetSubstoreLoader

This commit is contained in:
Jae Kwon 2017-12-14 06:28:37 -08:00
parent 6b5f08e918
commit d681049023
4 changed files with 110 additions and 68 deletions

View File

@ -24,10 +24,10 @@ type BaseApp struct {
name string name string
// DeliverTx (main) state // DeliverTx (main) state
ms MultiStore store MultiStore
// CheckTx state // CheckTx state
msCheck CacheMultiStore storeCheck CacheMultiStore
// Current block header // Current block header
header *abci.Header header *abci.Header
@ -42,25 +42,25 @@ type BaseApp struct {
var _ abci.Application = &BaseApp{} var _ abci.Application = &BaseApp{}
// CONTRACT: There exists a "main" KVStore. // CONTRACT: There exists a "main" KVStore.
func NewBaseApp(name string, ms MultiStore) (*BaseApp, error) { func NewBaseApp(name string, store CommitMultiStore) (*BaseApp, error) {
if ms.GetKVStore("main") == nil { if store.GetKVStore("main") == nil {
return nil, errors.New("BaseApp expects MultiStore with 'main' KVStore") return nil, errors.New("BaseApp expects MultiStore with 'main' KVStore")
} }
logger := makeDefaultLogger() logger := makeDefaultLogger()
lastCommitID := ms.LastCommitID() lastCommitID := store.LastCommitID()
curVersion := ms.CurrentVersion() curVersion := store.CurrentVersion()
main := ms.GetKVStore("main") main := store.GetKVStore("main")
header := (*abci.Header)(nil) header := (*abci.Header)(nil)
msCheck := ms.CacheMultiStore() storeCheck := store.CacheMultiStore()
// SANITY // SANITY
if curVersion != lastCommitID.Version+1 { if curVersion != lastCommitID.Version+1 {
panic("CurrentVersion != LastCommitID.Version+1") panic("CurrentVersion != LastCommitID.Version+1")
} }
// If we've committed before, we expect ms.GetKVStore("main").Get("header") // If we've committed before, we expect store.GetKVStore("main").Get("header")
if !lastCommitID.IsZero() { if !lastCommitID.IsZero() {
headerBytes, ok := main.Get(mainKeyHeader) headerBytes, ok := main.Get(mainKeyHeader)
if !ok { if !ok {
@ -81,8 +81,8 @@ func NewBaseApp(name string, ms MultiStore) (*BaseApp, error) {
return &BaseApp{ return &BaseApp{
logger: logger, logger: logger,
name: name, name: name,
ms: ms, store: store,
msCheck: msCheck, storeCheck: storeCheck,
header: header, header: header,
hander: nil, // set w/ .WithHandler() hander: nil, // set w/ .WithHandler()
valSetDiff: nil, valSetDiff: nil,
@ -98,15 +98,26 @@ func (app *BaseApp) WithHandler(handler sdk.Handler) *BaseApp {
// DeliverTx - ABCI - dispatches to the handler // DeliverTx - ABCI - dispatches to the handler
func (app *BaseApp) DeliverTx(txBytes []byte) abci.ResponseDeliverTx { func (app *BaseApp) DeliverTx(txBytes []byte) abci.ResponseDeliverTx {
ctx := sdk.NewContext(app.header, false, txBytes) // Initialize arguments to Handler.
// NOTE: Tx is nil until a decorator parses it. var isCheckTx = false
result := app.handler(ctx, nil) var ctx = sdk.NewContext(app.header, isCheckTx, txBytes)
var store = app.store
var tx Tx = nil // nil until a decorator parses one.
// Run the handler.
var result = app.handler(ctx, app.store, tx)
// After-handler hooks.
// TODO move to app.afterHandler(...).
if result.Code == abci.CodeType_OK { if result.Code == abci.CodeType_OK {
// XXX No longer "diff", we need to replace old entries.
app.ValSetDiff = append(app.ValSetDiff, result.ValSetDiff) app.ValSetDiff = append(app.ValSetDiff, result.ValSetDiff)
} else { } else {
// Even though the Code is not OK, there will be some side effects, // Even though the Code is not OK, there will be some side effects,
// like those caused by fee deductions or sequence incrementations. // like those caused by fee deductions or sequence incrementations.
} }
// Tell the blockchain engine (i.e. Tendermint).
return abci.ResponseDeliverTx{ return abci.ResponseDeliverTx{
Code: result.Code, Code: result.Code,
Data: result.Data, Data: result.Data,
@ -118,10 +129,17 @@ func (app *BaseApp) DeliverTx(txBytes []byte) abci.ResponseDeliverTx {
// CheckTx - ABCI - dispatches to the handler // CheckTx - ABCI - dispatches to the handler
func (app *BaseApp) CheckTx(txBytes []byte) abci.ResponseCheckTx { func (app *BaseApp) CheckTx(txBytes []byte) abci.ResponseCheckTx {
ctx := sdk.NewContext(app.header, true, txBytes) // Initialize arguments to Handler.
// NOTE: Tx is nil until a decorator parses it. var isCheckTx = true
result := app.handler(ctx, nil) var ctx = sdk.NewContext(app.header, isCheckTx, txBytes)
return abci.ResponseCheckTx{ var store = app.store
var tx Tx = nil // nil until a decorator parses one.
// Run the handler.
var result = app.handler(ctx, app.store, tx)
// Tell the blockchain engine (i.e. Tendermint).
return abci.ResponseDeliverTx{
Code: result.Code, Code: result.Code,
Data: result.Data, Data: result.Data,
Log: result.Log, Log: result.Log,
@ -134,7 +152,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) abci.ResponseCheckTx {
// Info - ABCI // Info - ABCI
func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo {
lastCommitID := app.ms.LastCommitID() lastCommitID := app.store.LastCommitID()
return abci.ResponseInfo{ return abci.ResponseInfo{
Data: app.Name, Data: app.Name,
@ -150,55 +168,57 @@ func (app *BaseApp) SetOption(key string, value string) string {
// Query - ABCI // Query - ABCI
func (app *BaseApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { func (app *BaseApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
/* TODO /*
XXX Make this work with MultiStore.
XXX It will require some interfaces updates in store/types.go.
if len(reqQuery.Data) == 0 { if len(reqQuery.Data) == 0 {
resQuery.Log = "Query cannot be zero length" resQuery.Log = "Query cannot be zero length"
resQuery.Code = abci.CodeType_EncodingError resQuery.Code = abci.CodeType_EncodingError
return return
}
// set the query response height to current
tree := app.state.Committed()
height := reqQuery.Height
if height == 0 {
// TODO: once the rpc actually passes in non-zero
// heights we can use to query right after a tx
// we must retrun most recent, even if apphash
// is not yet in the blockchain
withProof := app.CommittedHeight() - 1
if tree.Tree.VersionExists(withProof) {
height = withProof
} else {
height = app.CommittedHeight()
} }
}
resQuery.Height = height
switch reqQuery.Path { // set the query response height to current
case "/store", "/key": // Get by key tree := app.state.Committed()
key := reqQuery.Data // Data holds the key bytes
resQuery.Key = key height := reqQuery.Height
if reqQuery.Prove { if height == 0 {
value, proof, err := tree.GetVersionedWithProof(key, height) // TODO: once the rpc actually passes in non-zero
if err != nil { // heights we can use to query right after a tx
resQuery.Log = err.Error() // we must retrun most recent, even if apphash
break // is not yet in the blockchain
withProof := app.CommittedHeight() - 1
if tree.Tree.VersionExists(withProof) {
height = withProof
} else {
height = app.CommittedHeight()
} }
resQuery.Value = value
resQuery.Proof = proof.Bytes()
} else {
value := tree.Get(key)
resQuery.Value = value
} }
resQuery.Height = height
default: switch reqQuery.Path {
resQuery.Code = abci.CodeType_UnknownRequest case "/store", "/key": // Get by key
resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path) key := reqQuery.Data // Data holds the key bytes
} resQuery.Key = key
return if reqQuery.Prove {
value, proof, err := tree.GetVersionedWithProof(key, height)
if err != nil {
resQuery.Log = err.Error()
break
}
resQuery.Value = value
resQuery.Proof = proof.Bytes()
} else {
value := tree.Get(key)
resQuery.Value = value
}
default:
resQuery.Code = abci.CodeType_UnknownRequest
resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path)
}
return
*/ */
} }

View File

@ -35,14 +35,14 @@ func NewMultiStore(db dbm.DB) *rootMultiStore {
} }
} }
func (rs *rootMultiStore) SetCommitStoreLoader(name string, loader CommitStoreLoader) { func (rs *rootMultiStore) SetSubstoreLoader(name string, loader CommitStoreLoader) {
if _, ok := rs.storeLoaders[name]; ok { if _, ok := rs.storeLoaders[name]; ok {
panic(fmt.Sprintf("rootMultiStore duplicate substore name " + name)) panic(fmt.Sprintf("rootMultiStore duplicate substore name " + name))
} }
rs.storeLoaders[name] = loader rs.storeLoaders[name] = loader
} }
// Call once after all calls to SetCommitStoreLoader are complete. // Call once after all calls to SetSubstoreLoader are complete.
func (rs *rootMultiStore) LoadLatestVersion() error { func (rs *rootMultiStore) LoadLatestVersion() error {
ver := getLatestVersion(rs.db) ver := getLatestVersion(rs.db)
return rs.LoadVersion(ver) return rs.LoadVersion(ver)

View File

@ -71,7 +71,7 @@ func newMultiStoreWithLoaders(db dbm.DB) *rootMultiStore {
"store3": newMockCommitStore, "store3": newMockCommitStore,
} }
for name, loader := range storeLoaders { for name, loader := range storeLoaders {
store.SetCommitStoreLoader(name, loader) store.SetSubstoreLoader(name, loader)
} }
return store return store
} }

View File

@ -30,23 +30,45 @@ type MultiStore interface {
GetKVStore(name string) KVStore GetKVStore(name string) KVStore
} }
// From MultiStore.CacheMultiStore()....
type CacheMultiStore interface { type CacheMultiStore interface {
MultiStore MultiStore
Write() // Writes operations to underlying KVStore Write() // Writes operations to underlying KVStore
} }
// Substores of MultiStore must implement CommitStore.
type CommitStore interface { type CommitStore interface {
Committer Committer
CacheWrapper CacheWrapper
} }
type CommitStoreLoader func(id CommitID) (CommitStore, error) // A non-cache store that can commit (persist) and get a Merkle root.
type Committer interface { type Committer interface {
// Commit persists the state to disk.
Commit() CommitID Commit() CommitID
} }
// A non-cache MultiStore.
type CommitMultiStore interface {
CommitStore
MultiStore
// Add a substore loader.
SetSubstoreLoader(name string, loader CommitStoreLoader)
// Load the latest persisted version.
LoadLatestVersion() error
// Load a specific persisted version. When you load an old version, or
// when the last commit attempt didn't complete, the next commit after
// loading must be idempotent (return the same commit id). Otherwise the
// behavior is undefined.
LoadVersion(ver int64) error
}
// These must be added to the MultiStore before calling LoadVersion() or
// LoadLatest().
type CommitStoreLoader func(id CommitID) (CommitStore, error)
//---------------------------------------- //----------------------------------------
// KVStore // KVStore