diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cf86e3f7ac..a04a50cdf7 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -16,7 +16,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var mainHeaderKey = []byte("header") +// Key to store the header in the DB itself. +// Use the db directly instead of a store to avoid +// conflicts with handlers writing to the store +// and to avoid affecting the Merkle root. +var dbHeaderKey = []byte("header") // The ABCI application type BaseApp struct { @@ -126,19 +130,20 @@ func (app *BaseApp) LastBlockHeight() int64 { // initializes the remaining logic from app.cms func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { var lastCommitID = app.cms.LastCommitID() - var main = app.cms.GetKVStore(mainKey) var header abci.Header // main store should exist. + // TODO: we don't actually need the main store here + main := app.cms.GetKVStore(mainKey) if main == nil { return errors.New("BaseApp expects MultiStore with 'main' KVStore") } - // if we've committed before, we expect main:// + // if we've committed before, we expect to exist in the db if !lastCommitID.IsZero() { - headerBytes := main.Get(mainHeaderKey) + headerBytes := app.db.Get(dbHeaderKey) if len(headerBytes) == 0 { - errStr := fmt.Sprintf("Version > 0 but missing key %s", mainHeaderKey) + errStr := fmt.Sprintf("Version > 0 but missing key %s", dbHeaderKey) return errors.New(errStr) } err := proto.Unmarshal(headerBytes, &header) @@ -147,7 +152,7 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { } lastVersion := lastCommitID.Version if header.Height != lastVersion { - errStr := fmt.Sprintf("Expected main://%s.Height %v but got %v", mainHeaderKey, lastVersion, header.Height) + errStr := fmt.Sprintf("Expected main://%s.Height %v but got %v", dbHeaderKey, lastVersion, header.Height) return errors.New(errStr) } } @@ -369,6 +374,14 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc // Implements ABCI func (app *BaseApp) Commit() (res abci.ResponseCommit) { + // Write the latest Header to the store + header := app.ctxDeliver.BlockHeader() + headerBytes, err := proto.Marshal(&header) + if err != nil { + panic(err) + } + app.db.Set(dbHeaderKey, headerBytes) + // Write the Deliver state and commit the MultiStore app.msDeliver.Write() commitID := app.cms.Commit() @@ -379,7 +392,6 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { // Reset the Check state // NOTE: safe because Tendermint holds a lock on the mempool for Commit. // Use the header from this latest block. - header := app.ctxDeliver.BlockHeader() app.msCheck = app.cms.CacheMultiStore() app.ctxCheck = app.NewContext(true, header) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index d228d2761e..3add7a8b21 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -42,19 +42,82 @@ func TestMountStores(t *testing.T) { app.MountStoresIAVL(capKey1, capKey2) - // both stores are mounted + // stores are mounted err := app.LoadLatestVersion(capKey1) assert.Nil(t, err) - err = app.LoadLatestVersion(capKey2) - assert.Nil(t, err) + + // check both stores + store1 := app.cms.GetCommitKVStore(capKey1) + assert.NotNil(t, store1) + store2 := app.cms.GetCommitKVStore(capKey2) + assert.NotNil(t, store2) } // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. func TestLoadVersion(t *testing.T) { - // TODO + logger := defaultLogger() + db := dbm.NewMemDB() + name := t.Name() + app := NewBaseApp(name, logger, db) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + emptyCommitID := sdk.CommitID{} + + lastHeight := app.LastBlockHeight() + lastID := app.LastCommitID() + assert.Equal(t, int64(0), lastHeight) + assert.Equal(t, emptyCommitID, lastID) + + // execute some blocks + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res := app.Commit() + commitID := sdk.CommitID{1, res.Data} + + // reload + app = NewBaseApp(name, logger, db) + app.MountStoresIAVL(capKey) + err = app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + lastHeight = app.LastBlockHeight() + lastID = app.LastCommitID() + assert.Equal(t, int64(1), lastHeight) + assert.Equal(t, commitID, lastID) } +// Test that the app hash is static +// TODO: https://github.com/cosmos/cosmos-sdk/issues/520 +/*func TestStaticAppHash(t *testing.T) { + app := newBaseApp(t.Name()) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + // execute some blocks + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res := app.Commit() + commitID1 := sdk.CommitID{1, res.Data} + + header = abci.Header{Height: 2} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res = app.Commit() + commitID2 := sdk.CommitID{2, res.Data} + + assert.Equal(t, commitID1.Hash, commitID2.Hash) +} +*/ + // Test that txs can be unmarshalled and read and that // correct error codes are returned when not func TestTxDecoder(t *testing.T) { @@ -246,7 +309,8 @@ func TestValidatorChange(t *testing.T) { // Create app. app := newBaseApp(t.Name()) - storeKeys := createMounts(app.cms) + capKey := sdk.NewKVStoreKey("key") + app.MountStoresIAVL(capKey) app.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) { var ttx testUpdatePowerTx fromJSON(txBytes, &ttx) @@ -260,7 +324,7 @@ func TestValidatorChange(t *testing.T) { }) // Load latest state, which should be empty. - err := app.LoadLatestVersion(storeKeys["main"]) + err := app.LoadLatestVersion(capKey) assert.Nil(t, err) assert.Equal(t, app.LastBlockHeight(), int64(0)) @@ -369,17 +433,3 @@ func fromJSON(bz []byte, ptr interface{}) { panic(err) } } - -// Mounts stores to CommitMultiStore and returns a map of keys. -func createMounts(ms sdk.CommitMultiStore) map[string]sdk.StoreKey { - dbMain := dbm.NewMemDB() - dbXtra := dbm.NewMemDB() - keyMain := sdk.NewKVStoreKey("main") - keyXtra := sdk.NewKVStoreKey("xtra") - ms.MountStoreWithDB(keyMain, sdk.StoreTypeIAVL, dbMain) - ms.MountStoreWithDB(keyXtra, sdk.StoreTypeIAVL, dbXtra) - return map[string]sdk.StoreKey{ - "main": keyMain, - "xtra": keyXtra, - } -}