diff --git a/app/app.go b/app/app.go index 6f107f2b7..12fea597f 100644 --- a/app/app.go +++ b/app/app.go @@ -8,12 +8,13 @@ import ( "github.com/golang/protobuf/proto" "github.com/pkg/errors" abci "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" "github.com/cosmos/cosmos-sdk/types" ) -var mainHeaderKey = "header" +var mainHeaderKey = []byte("header") // App - The ABCI application type App struct { @@ -23,16 +24,16 @@ type App struct { name string // Main (uncached) state - store types.CommitMultiStore + ms types.CommitMultiStore - // CheckTx state, a cache-wrap of store. - storeCheck types.CacheMultiStore + // CheckTx state, a cache-wrap of `.ms`. + msCheck types.CacheMultiStore - // DeliverTx state, a cache-wrap of store. - storeDeliver types.CacheMultiStore + // DeliverTx state, a cache-wrap of `.ms`. + msDeliver types.CacheMultiStore // Current block header - header *abci.Header + header abci.Header // Handler for CheckTx and DeliverTx. handler types.Handler @@ -54,8 +55,8 @@ func (app *App) Name() string { return app.name } -func (app *App) SetCommitStore(store types.CommitMultiStore) { - app.store = store +func (app *App) SetCommitMultiStore(ms types.CommitMultiStore) { + app.ms = ms } func (app *App) SetHandler(handler types.Handler) { @@ -63,26 +64,33 @@ func (app *App) SetHandler(handler types.Handler) { } func (app *App) LoadLatestVersion() error { - store := app.store - store.LoadLatestVersion() + app.ms.LoadLatestVersion() return app.initFromStore() } func (app *App) LoadVersion(version int64) error { - store := app.store - store.LoadVersion(version) + app.ms.LoadVersion(version) return app.initFromStore() } -// Initializes the remaining logic from app.store. +// The last CommitID of the multistore. +func (app *App) LastCommitID() types.CommitID { + return app.ms.LastCommitID() +} + +// The last commited block height. +func (app *App) LastBlockHeight() int64 { + return app.ms.LastCommitID().Version +} + +// Initializes the remaining logic from app.ms. func (app *App) initFromStore() error { - store := app.store - lastCommitID := store.LastCommitID() - main := store.GetKVStore("main") - header := (*abci.Header)(nil) + lastCommitID := app.ms.LastCommitID() + main := app.ms.GetKVStore("main") + header := abci.Header{} // Main store should exist. - if store.GetKVStore("main") == nil { + if app.ms.GetKVStore("main") == nil { return errors.New("App expects MultiStore with 'main' KVStore") } @@ -93,7 +101,7 @@ func (app *App) initFromStore() error { errStr := fmt.Sprintf("Version > 0 but missing key %s", mainHeaderKey) return errors.New(errStr) } - err := proto.Unmarshal(headerBytes, header) + err := proto.Unmarshal(headerBytes, &header) if err != nil { return errors.Wrap(err, "Failed to parse Header") } @@ -106,8 +114,8 @@ func (app *App) initFromStore() error { // Set App state. app.header = header - app.storeCheck = nil - app.storeDeliver = nil + app.msCheck = nil + app.msDeliver = nil app.valUpdates = nil return nil @@ -118,7 +126,7 @@ func (app *App) initFromStore() error { // Implements ABCI func (app *App) Info(req abci.RequestInfo) abci.ResponseInfo { - lastCommitID := app.store.LastCommitID() + lastCommitID := app.ms.LastCommitID() return abci.ResponseInfo{ Data: app.name, @@ -148,8 +156,8 @@ func (app *App) Query(req abci.RequestQuery) (res abci.ResponseQuery) { // Implements ABCI func (app *App) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { app.header = req.Header - app.storeDeliver = app.store.CacheMultiStore() - app.storeCheck = app.store.CacheMultiStore() + app.msDeliver = app.ms.CacheMultiStore() + app.msCheck = app.ms.CacheMultiStore() return } @@ -159,20 +167,22 @@ func (app *App) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { // Initialize arguments to Handler. var isCheckTx = true var ctx = types.NewContext(app.header, isCheckTx, txBytes) - var store = app.store - var tx Tx = nil // nil until a decorator parses one. + var tx types.Tx = nil // nil until a decorator parses one. // Run the handler. - var result = app.handler(ctx, app.store, tx) + var result = app.handler(ctx, app.ms, tx) // Tell the blockchain engine (i.e. Tendermint). - return abci.ResponseDeliverTx{ + return abci.ResponseCheckTx{ Code: result.Code, Data: result.Data, Log: result.Log, - Gas: result.Gas, - FeeDenom: result.FeeDenom, - FeeAmount: result.FeeAmount, + GasWanted: result.GasWanted, + Fee: cmn.KI64Pair{ + []byte(result.FeeDenom), + result.FeeAmount, + }, + Tags: result.Tags, } } @@ -182,15 +192,14 @@ func (app *App) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { // Initialize arguments to Handler. var isCheckTx = false var ctx = types.NewContext(app.header, isCheckTx, txBytes) - var store = app.store - var tx Tx = nil // nil until a decorator parses one. + var tx types.Tx = nil // nil until a decorator parses one. // Run the handler. - var result = app.handler(ctx, app.store, tx) + var result = app.handler(ctx, app.ms, tx) // After-handler hooks. - if result.Code == abci.CodeType_OK { - app.valUpdates = append(app.valUpdates, result.ValUpdate) + if result.Code == abci.CodeTypeOK { + app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...) } else { // Even though the Code is not OK, there will be some side effects, // like those caused by fee deductions or sequence incrementations. @@ -198,10 +207,12 @@ func (app *App) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { // Tell the blockchain engine (i.e. Tendermint). return abci.ResponseDeliverTx{ - Code: result.Code, - Data: result.Data, - Log: result.Log, - Tags: result.Tags, + Code: result.Code, + Data: result.Data, + Log: result.Log, + GasWanted: result.GasWanted, + GasUsed: result.GasUsed, + Tags: result.Tags, } } @@ -214,12 +225,14 @@ func (app *App) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { // Implements ABCI func (app *App) Commit() (res abci.ResponseCommit) { - app.storeDeliver.Write() - commitID := app.store.Commit() + app.msDeliver.Write() + commitID := app.ms.Commit() app.logger.Debug("Commit synced", "commit", commitID, ) - return abci.NewResultOK(hash, "") + return abci.ResponseCommit{ + Data: commitID.Hash, + } } //---------------------------------------- diff --git a/app/app_test.go b/app/app_test.go index 3ac550357..befb25145 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -7,12 +7,13 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk" + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" ) func TestBasic(t *testing.T) { @@ -24,31 +25,33 @@ func TestBasic(t *testing.T) { } // Create app. - app := sdk.NewApp(t.Name()) - app.SetStore(mockMultiStore()) - app.SetHandler(func(ctx Context, store MultiStore, tx Tx) Result { + app := NewApp(t.Name()) + app.SetCommitMultiStore(newCommitMultiStore()) + app.SetHandler(func(ctx types.Context, store types.MultiStore, tx types.Tx) types.Result { // This could be a decorator. - fromJSON(ctx.TxBytes(), &tx) + var ttx testTx + fromJSON(ctx.TxBytes(), &ttx) - fmt.Println(">>", tx) + // XXX + return types.Result{} }) // Load latest state, which should be empty. err := app.LoadLatestVersion() assert.Nil(t, err) - assert.Equal(t, app.NextVersion(), 1) + assert.Equal(t, app.LastBlockHeight(), int64(0)) // Create the validators var numVals = 3 - var valSet = make([]*abci.Validator, numVals) + var valSet = make([]abci.Validator, numVals) for i := 0; i < numVals; i++ { valSet[i] = makeVal(secret(i)) } // Initialize the chain app.InitChain(abci.RequestInitChain{ - Validators: valset, + Validators: valSet, }) // Simulate the start of a block. @@ -62,24 +65,26 @@ func TestBasic(t *testing.T) { } txBytes := toJSON(tx) res := app.DeliverTx(txBytes) - require.True(res.IsOK(), "%#v", res) + assert.True(t, res.IsOK(), "%#v", res) } // Simulate the end of a block. // Get the summary of validator updates. - res := app.EndBlock(app.height) + res := app.EndBlock(abci.RequestEndBlock{}) valUpdates := res.ValidatorUpdates // Assert that validator updates are correct. for _, val := range valSet { + // Sanity + assert.NotEqual(t, len(val.PubKey), 0) // Find matching update and splice it out. for j := 0; j < len(valUpdates); { - assert.NotEqual(len(valUpdates.PubKey), 0) + valUpdate := valUpdates[j] // Matched. if bytes.Equal(valUpdate.PubKey, val.PubKey) { - assert.Equal(valUpdate.NewPower, val.Power+1) + assert.Equal(t, valUpdate.Power, val.Power+1) if j < len(valUpdates)-1 { // Splice it out. valUpdates = append(valUpdates[:j], valUpdates[j+1:]...) @@ -100,9 +105,9 @@ func randPower() int64 { return cmn.RandInt64() } -func makeVal(secret string) *abci.Validator { - return &abci.Validator{ - PubKey: makePubKey(string).Bytes(), +func makeVal(secret string) abci.Validator { + return abci.Validator{ + PubKey: makePubKey(secret).Bytes(), Power: randPower(), } } @@ -112,29 +117,43 @@ func makePubKey(secret string) crypto.PubKey { } func makePrivKey(secret string) crypto.PrivKey { - return crypto.GenPrivKeyEd25519FromSecret([]byte(id)) + privKey := crypto.GenPrivKeyEd25519FromSecret([]byte(secret)) + return privKey.Wrap() } -func secret(index int) []byte { - return []byte(fmt.Sprintf("secret%d", index)) +func secret(index int) string { + return fmt.Sprintf("secret%d", index) } -func copyVal(val *abci.Validator) *abci.Validator { - val2 := *val - return &val2 +func copyVal(val abci.Validator) abci.Validator { + // val2 := *val + // return &val2 + return val } func toJSON(o interface{}) []byte { - bytes, err := json.Marshal(o) + bz, err := json.Marshal(o) if err != nil { panic(err) } - return bytes + // fmt.Println(">> toJSON:", string(bz)) + return bz } -func fromJSON(bytes []byte, ptr interface{}) { - err := json.Unmarshal(bytes, ptr) +func fromJSON(bz []byte, ptr interface{}) { + // fmt.Println(">> fromJSON:", string(bz)) + err := json.Unmarshal(bz, ptr) if err != nil { panic(err) } } + +// Creates a sample CommitMultiStore +func newCommitMultiStore() types.CommitMultiStore { + dbMain := dbm.NewMemDB() + dbXtra := dbm.NewMemDB() + ms := store.NewMultiStore(dbMain) // Also store rootMultiStore metadata here (it shouldn't clash) + ms.SetSubstoreLoader("main", store.NewIAVLStoreLoader(dbMain, 0, 0)) + ms.SetSubstoreLoader("xtra", store.NewIAVLStoreLoader(dbXtra, 0, 0)) + return ms +} diff --git a/glide.lock b/glide.lock index bb1cdd8cb..b2a9eef33 100644 --- a/glide.lock +++ b/glide.lock @@ -112,7 +112,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: 4274f8eca234325d2fbc9a434f9bc099bd2c40d4 + version: 8f87efd7f86c2bae9df69aff69d715593d5d79f7 subpackages: - client - example/dummy @@ -174,7 +174,7 @@ imports: - types - version - name: github.com/tendermint/tmlibs - version: b70ae4919befb6ae3e5cb40ae8174e122e771d08 + version: b25df389db3c98f4b964bd39511c199f02d07715 subpackages: - autofile - cli diff --git a/store/iavlstore.go b/store/iavlstore.go index 0c333ab92..c349358c7 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -48,6 +48,7 @@ type iavlStore struct { tree *iavl.VersionedTree // How many old versions we hold onto. + // A value of 0 means keep all history. numHistory int64 } @@ -71,7 +72,7 @@ func (st *iavlStore) Commit() CommitID { } // Release an old version of history - if st.numHistory < st.tree.Version64() { + if st.numHistory > 0 && (st.numHistory < st.tree.Version64()) { toRelease := version - st.numHistory st.tree.DeleteVersion(toRelease) } diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 21b399946..7508d3c05 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -26,6 +26,8 @@ type rootMultiStore struct { substores map[string]CommitStore } +var _ CommitMultiStore = (*rootMultiStore)(nil) + func NewMultiStore(db dbm.DB) *rootMultiStore { return &rootMultiStore{ db: db, @@ -35,6 +37,7 @@ func NewMultiStore(db dbm.DB) *rootMultiStore { } } +// Implements CommitMultiStore. func (rs *rootMultiStore) SetSubstoreLoader(name string, loader CommitStoreLoader) { if _, ok := rs.storeLoaders[name]; ok { panic(fmt.Sprintf("rootMultiStore duplicate substore name " + name)) @@ -42,17 +45,18 @@ func (rs *rootMultiStore) SetSubstoreLoader(name string, loader CommitStoreLoade rs.storeLoaders[name] = loader } -// Call once after all calls to SetSubstoreLoader are complete. +// Implements CommitMultiStore. +func (rs *rootMultiStore) GetSubstore(name string) CommitStore { + return rs.substores[name] +} + +// Implements CommitMultiStore. func (rs *rootMultiStore) LoadLatestVersion() error { ver := getLatestVersion(rs.db) return rs.LoadVersion(ver) } -// NOTE: Returns 0 unless LoadVersion() or LoadLatestVersion() is called. -func (rs *rootMultiStore) NextVersion() int64 { - return rs.nextVersion -} - +// Implements CommitMultiStore. func (rs *rootMultiStore) LoadVersion(ver int64) error { // Special logic for version 0 @@ -106,10 +110,13 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { return nil } -// Implements CommitStore +//---------------------------------------- +// +CommitStore + +// Implements CommitStore. func (rs *rootMultiStore) Commit() CommitID { - // Commit substores + // Commit substores. version := rs.nextVersion state := commitSubstores(version, rs.substores) @@ -129,27 +136,36 @@ func (rs *rootMultiStore) Commit() CommitID { return commitID } -// Implements CommitStore +// Implements CommitStore. func (rs *rootMultiStore) CacheWrap() CacheWrap { return rs.CacheMultiStore().(CacheWrap) } -// Get the last committed CommitID +//---------------------------------------- +// +MultiStore + +// Implements MultiStore. func (rs *rootMultiStore) LastCommitID() CommitID { return rs.lastCommitID } -// Implements MultiStore +// Implements MultiStore. +// NOTE: Returns 0 unless LoadVersion() or LoadLatestVersion() is called. +func (rs *rootMultiStore) NextVersion() int64 { + return rs.nextVersion +} + +// Implements MultiStore. func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore { return newCacheMultiStoreFromRMS(rs) } -// Implements MultiStore -func (rs *rootMultiStore) GetCommitStore(name string) CommitStore { +// Implements MultiStore. +func (rs *rootMultiStore) GetStore(name string) interface{} { return rs.substores[name] } -// Implements MultiStore +// Implements MultiStore. func (rs *rootMultiStore) GetKVStore(name string) KVStore { return rs.substores[name].(KVStore) } @@ -208,6 +224,7 @@ func (sc substoreCore) Hash() []byte { } //---------------------------------------- +// Misc. func getLatestVersion(db dbm.DB) int64 { var latest int64 diff --git a/types/result.go b/types/result.go index d56e5ceb1..3ce194c25 100644 --- a/types/result.go +++ b/types/result.go @@ -17,8 +17,8 @@ type Result struct { // Log is just debug information. NOTE: nondeterministic. Log string - // GasAllocated is the maximum units of work we allow this tx to perform. - GasAllocated int64 + // GasWanted is the maximum units of work we allow this tx to perform. + GasWanted int64 // GasUsed is the amount of gas actually consumed. NOTE: not used. GasUsed int64 @@ -28,7 +28,7 @@ type Result struct { FeeDenom string // Changes to the validator set. - ValSetDiff []abci.Validator + ValidatorUpdates []abci.Validator // Tags are used for transaction indexing and pubsub. Tags []cmn.KVPair diff --git a/types/store.go b/types/store.go index 73a0e848a..adeba6f44 100644 --- a/types/store.go +++ b/types/store.go @@ -26,7 +26,7 @@ type MultiStore interface { // call CacheMultiStore.Write(). CacheMultiStore() CacheMultiStore - // Convenience + // Convenience for fetching substores. GetStore(name string) interface{} GetKVStore(name string) KVStore } @@ -56,7 +56,11 @@ type CommitMultiStore interface { // Add a substore loader. SetSubstoreLoader(name string, loader CommitStoreLoader) + // Gets the substore, which is a CommitSubstore. + GetSubstore(name string) CommitStore + // Load the latest persisted version. + // Called once after all calls to SetSubstoreLoader are complete. LoadLatestVersion() error // Load a specific persisted version. When you load an old version, or