From 0fdccfc60da03cae4e6eb71570e9834fb79bb597 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Wed, 4 Oct 2017 16:44:15 +0200 Subject: [PATCH 01/13] Update glide.lock --- glide.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/glide.lock b/glide.lock index 84f6d0881..26e2e2ea4 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 647d25291b8e9a85cb4a49abc972a41537e8a286514bf41180845785e3b180a4 -updated: 2017-10-02T23:59:53.784455453-04:00 +updated: 2017-10-04T16:24:57.97237598+02:00 imports: - name: github.com/bgentry/speakeasy version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd @@ -108,7 +108,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: 191c4b6d176169ffc7f9972d490fa362a3b7d940 + version: 01181721adca98ff9015ad8956a9e5cdc17d87d2 subpackages: - client - example/dummy @@ -133,7 +133,7 @@ imports: - data - data/base58 - name: github.com/tendermint/iavl - version: 2d3ca4f466c32953641d4c49cad3d93eb7876a5e + version: f5710b6ef84994d508d328e5663e3849d62cadc5 - name: github.com/tendermint/light-client version: ac2e4bf47b31aaf5d3d336691ac786ec751bfc32 subpackages: @@ -146,7 +146,7 @@ imports: subpackages: - iavl - name: github.com/tendermint/tendermint - version: 97e980225530133c7b7e2b45e5e65c1f78ace89b + version: 31a7e2b3b40e4b58507f34275ae0e873df21d6bc subpackages: - blockchain - cmd/tendermint/commands @@ -173,7 +173,7 @@ imports: - types - version - name: github.com/tendermint/tmlibs - version: 096dcb90e60aa00b748b3fe49a4b95e48ebf1e13 + version: bfec1ff1cd7fda9f5b2d8b570e3bec163e5f9149 subpackages: - autofile - cli From 36f8c59fee8c04be600daf7919fe4d74b3551cfd Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Wed, 4 Oct 2017 17:05:38 +0200 Subject: [PATCH 02/13] Move to iavl.VersionedTree --- app/store.go | 20 +++++++++++--------- stack/checkpoint_test.go | 3 ++- state/bonsai.go | 4 ++-- state/merkle.go | 8 ++++++-- state/merkle_test.go | 3 ++- state/store_test.go | 4 ++-- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/store.go b/app/store.go index 7163c02e2..87373d705 100644 --- a/app/store.go +++ b/app/store.go @@ -46,8 +46,8 @@ func MockStore() *Store { return res } -// NewStore initializes an in-memory IAVLTree, or attempts to load a persistant -// tree from disk +// NewStore initializes an in-memory iavl.VersionedTree, or attempts to load a +// persistant tree from disk func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { // start at 1 so the height returned by query is for the // next block, ie. the one that includes the AppHash for our current state @@ -55,9 +55,9 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { // Non-persistent case if dbName == "" { - tree := iavl.NewIAVLTree( + tree := iavl.NewVersionedTree( 0, - nil, + dbm.NewMemDB(), ) store := &Store{ State: state.NewState(tree, false), @@ -85,24 +85,26 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { // Open database called "dir/name.db", if it doesn't exist it will be created db := dbm.NewDB(name, dbm.LevelDBBackendStr, dir) - tree := iavl.NewIAVLTree(cacheSize, db) + tree := iavl.NewVersionedTree(cacheSize, db) var chainState ChainState if empty { logger.Info("no existing db, creating new db") chainState = ChainState{ - Hash: tree.Save(), + Hash: nil, Height: initialHeight, } db.Set(stateKey, wire.BinaryBytes(chainState)) } else { logger.Info("loading existing db") eyesStateBytes := db.Get(stateKey) - err = wire.ReadBinaryBytes(eyesStateBytes, &chainState) - if err != nil { + + if err = wire.ReadBinaryBytes(eyesStateBytes, &chainState); err != nil { return nil, errors.Wrap(err, "Reading MerkleEyesState") } - tree.Load(chainState.Hash) + if err = tree.Load(); err != nil { + return nil, errors.Wrap(err, "Loading tree") + } } res := &Store{ diff --git a/stack/checkpoint_test.go b/stack/checkpoint_test.go index c5c582f90..1cad0121e 100644 --- a/stack/checkpoint_test.go +++ b/stack/checkpoint_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/iavl" + db "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" sdk "github.com/cosmos/cosmos-sdk" @@ -17,7 +18,7 @@ import ( func makeState() state.SimpleDB { // return state.NewMemKVStore() - return state.NewBonsai(iavl.NewIAVLTree(0, nil)) + return state.NewBonsai(iavl.NewVersionedTree(0, db.NewMemDB())) // tree with persistence.... // tmpDir, err := ioutil.TempDir("", "state-tests") diff --git a/state/bonsai.go b/state/bonsai.go index 722411841..973cd672d 100644 --- a/state/bonsai.go +++ b/state/bonsai.go @@ -12,7 +12,7 @@ type nonce int64 // Bonsai is a deformed tree forced to fit in a small pot type Bonsai struct { id nonce - Tree *iavl.IAVLTree + Tree *iavl.VersionedTree } func (b *Bonsai) String() string { @@ -22,7 +22,7 @@ func (b *Bonsai) String() string { var _ SimpleDB = &Bonsai{} // NewBonsai wraps a merkle tree and tags it to track children -func NewBonsai(tree *iavl.IAVLTree) *Bonsai { +func NewBonsai(tree *iavl.VersionedTree) *Bonsai { return &Bonsai{ id: nonce(rand.Int63()), Tree: tree, diff --git a/state/merkle.go b/state/merkle.go index 06bfedb07..d232a050d 100644 --- a/state/merkle.go +++ b/state/merkle.go @@ -11,7 +11,7 @@ type State struct { persistent bool } -func NewState(tree *iavl.IAVLTree, persistent bool) State { +func NewState(tree *iavl.VersionedTree, persistent bool) State { base := NewBonsai(tree) return State{ committed: base, @@ -70,7 +70,11 @@ func (s *State) Commit() ([]byte, error) { var hash []byte if s.persistent { - hash = s.committed.Tree.Save() + nextVersion := s.committed.Tree.LatestVersion() + 1 + hash, err = s.committed.Tree.SaveVersion(nextVersion) + if err != nil { + return nil, err + } } else { hash = s.committed.Tree.Hash() } diff --git a/state/merkle_test.go b/state/merkle_test.go index 03575c835..fa039956d 100644 --- a/state/merkle_test.go +++ b/state/merkle_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/iavl" + db "github.com/tendermint/tmlibs/db" ) type keyVal struct { @@ -64,7 +65,7 @@ func TestStateCommitHash(t *testing.T) { result := make([][]byte, len(tc.rounds)) // make the store... - tree := iavl.NewIAVLTree(0, nil) + tree := iavl.NewVersionedTree(0, db.NewMemDB()) store := NewState(tree, false) for n, r := range tc.rounds { diff --git a/state/store_test.go b/state/store_test.go index 47f7ccd2c..ea7229170 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -17,11 +17,11 @@ func GetDBs() []SimpleDB { panic(err) } db := dbm.NewDB("test-get-dbs", dbm.LevelDBBackendStr, tmpDir) - persist := iavl.NewIAVLTree(500, db) + persist := iavl.NewVersionedTree(500, db) return []SimpleDB{ NewMemKVStore(), - NewBonsai(iavl.NewIAVLTree(0, nil)), + NewBonsai(iavl.NewVersionedTree(0, dbm.NewMemDB())), NewBonsai(persist), } } From bc5edbdc89afa69a3d7a81b917992885574a6192 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Wed, 4 Oct 2017 19:53:20 +0200 Subject: [PATCH 03/13] Update/fix glide file --- glide.lock | 14 +++++++------- glide.yaml | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/glide.lock b/glide.lock index 26e2e2ea4..e0d8f286b 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 647d25291b8e9a85cb4a49abc972a41537e8a286514bf41180845785e3b180a4 -updated: 2017-10-04T16:24:57.97237598+02:00 +hash: fbfdd03c0367bb0785ceb81ed34059df219e55d5a9c71c12597e505fbce14165 +updated: 2017-10-05T12:45:12.67543189+02:00 imports: - name: github.com/bgentry/speakeasy version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd @@ -108,7 +108,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: 01181721adca98ff9015ad8956a9e5cdc17d87d2 + version: 15cd7fb1e3b75c436b6dee89a44db35f3d265bd0 subpackages: - client - example/dummy @@ -128,12 +128,12 @@ imports: - keys/storage/memstorage - keys/wordlist - name: github.com/tendermint/go-wire - version: 5f88da3dbc1a72844e6dfaf274ce87f851d488eb + version: ddbcd39cf68f7026d12f81c66a3cb45fc38ac48b subpackages: - data - data/base58 - name: github.com/tendermint/iavl - version: f5710b6ef84994d508d328e5663e3849d62cadc5 + version: 372f484952449aae18cce33b82c13329a9009acf - name: github.com/tendermint/light-client version: ac2e4bf47b31aaf5d3d336691ac786ec751bfc32 subpackages: @@ -146,7 +146,7 @@ imports: subpackages: - iavl - name: github.com/tendermint/tendermint - version: 31a7e2b3b40e4b58507f34275ae0e873df21d6bc + version: 49653d3e31e34a5da83e16e9ffdcd95a68acd9be subpackages: - blockchain - cmd/tendermint/commands @@ -173,7 +173,7 @@ imports: - types - version - name: github.com/tendermint/tmlibs - version: bfec1ff1cd7fda9f5b2d8b570e3bec163e5f9149 + version: 7dd6b3d3f8a7a998a79bdd0d8222252b309570f3 subpackages: - autofile - cli diff --git a/glide.yaml b/glide.yaml index 568c8e634..258c028e4 100644 --- a/glide.yaml +++ b/glide.yaml @@ -8,7 +8,6 @@ import: - package: github.com/spf13/viper - package: github.com/tendermint/abci version: develop - version: subpackages: - server - types From 75e2591a68723267ba507c8e2b25ecb1ff9dcdb8 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Thu, 5 Oct 2017 15:54:57 +0200 Subject: [PATCH 04/13] Start adding versioned queries support --- app/store.go | 26 +++++++++++++------------- state/bonsai.go | 4 ++++ state/merkle.go | 11 ++++++----- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/app/store.go b/app/store.go index 87373d705..84092e5db 100644 --- a/app/store.go +++ b/app/store.go @@ -60,7 +60,7 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { dbm.NewMemDB(), ) store := &Store{ - State: state.NewState(tree, false), + State: state.NewState(tree, true), height: initialHeight, logger: logger, } @@ -148,7 +148,7 @@ func (s *Store) Commit() abci.Result { Height: s.height, })) - hash, err := s.State.Commit() + hash, err := s.State.Commit(s.height) if err != nil { return abci.NewError(abci.CodeType_InternalError, err.Error()) } @@ -164,25 +164,25 @@ func (s *Store) Commit() abci.Result { // Query implements abci.Application func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { - - if reqQuery.Height != 0 { - // TODO: support older commits - resQuery.Code = abci.CodeType_InternalError - resQuery.Log = "merkleeyes only supports queries on latest commit" - return - } - // set the query response height to current - resQuery.Height = s.height - tree := s.State.Committed() + height := reqQuery.Height + if height == 0 { + if tree.Tree.VersionExists(s.height - 1) { + height = s.height - 1 + } else { + height = s.height + } + } + resQuery.Height = height + switch reqQuery.Path { case "/store", "/key": // Get by key key := reqQuery.Data // Data holds the key bytes resQuery.Key = key if reqQuery.Prove { - value, proof, err := tree.GetWithProof(key) + value, proof, err := tree.GetVersionedWithProof(key, height) if err != nil { resQuery.Log = err.Error() break diff --git a/state/bonsai.go b/state/bonsai.go index 973cd672d..b816c876f 100644 --- a/state/bonsai.go +++ b/state/bonsai.go @@ -54,6 +54,10 @@ func (b *Bonsai) GetWithProof(key []byte) ([]byte, iavl.KeyProof, error) { return b.Tree.GetWithProof(key) } +func (b *Bonsai) GetVersionedWithProof(key []byte, version uint64) ([]byte, iavl.KeyProof, error) { + return b.Tree.GetVersionedWithProof(key, version) +} + func (b *Bonsai) List(start, end []byte, limit int) []Model { res := []Model{} stopAtCount := func(key []byte, value []byte) (stop bool) { diff --git a/state/merkle.go b/state/merkle.go index d232a050d..2add7d2fd 100644 --- a/state/merkle.go +++ b/state/merkle.go @@ -61,7 +61,7 @@ func (s *State) BatchSet(key, value []byte) { } // Commit save persistent nodes to the database and re-copies the trees -func (s *State) Commit() ([]byte, error) { +func (s *State) Commit(version uint64) ([]byte, error) { // commit (if we didn't do hash earlier) err := s.committed.Commit(s.deliverTx) if err != nil { @@ -70,10 +70,11 @@ func (s *State) Commit() ([]byte, error) { var hash []byte if s.persistent { - nextVersion := s.committed.Tree.LatestVersion() + 1 - hash, err = s.committed.Tree.SaveVersion(nextVersion) - if err != nil { - return nil, err + if s.committed.Tree.Size() > 0 || s.committed.Tree.LatestVersion() > 0 { + hash, err = s.committed.Tree.SaveVersion(version) + if err != nil { + return nil, err + } } } else { hash = s.committed.Tree.Hash() From ee6cb37ceda930919d978985a4b2a043d360ee13 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Thu, 5 Oct 2017 16:01:57 +0200 Subject: [PATCH 05/13] Update tendermint/iavl dependency --- glide.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glide.lock b/glide.lock index e0d8f286b..5febbd478 100644 --- a/glide.lock +++ b/glide.lock @@ -133,7 +133,7 @@ imports: - data - data/base58 - name: github.com/tendermint/iavl - version: 372f484952449aae18cce33b82c13329a9009acf + version: ff8784b7ba609d80ef78ebcc5ca4bd2d01b04259 - name: github.com/tendermint/light-client version: ac2e4bf47b31aaf5d3d336691ac786ec751bfc32 subpackages: From e85854fa8366180166620f8338c0d7aaafa225c9 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 10 Oct 2017 13:19:25 +0200 Subject: [PATCH 06/13] state.NewState is always persistent, remove flag --- app/store.go | 4 ++-- state/merkle.go | 4 ++-- state/merkle_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/store.go b/app/store.go index 84092e5db..1f4442004 100644 --- a/app/store.go +++ b/app/store.go @@ -60,7 +60,7 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { dbm.NewMemDB(), ) store := &Store{ - State: state.NewState(tree, true), + State: state.NewState(tree), height: initialHeight, logger: logger, } @@ -108,7 +108,7 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { } res := &Store{ - State: state.NewState(tree, true), + State: state.NewState(tree), height: chainState.Height, hash: chainState.Hash, persisted: true, diff --git a/state/merkle.go b/state/merkle.go index 2add7d2fd..a097b9431 100644 --- a/state/merkle.go +++ b/state/merkle.go @@ -11,13 +11,13 @@ type State struct { persistent bool } -func NewState(tree *iavl.VersionedTree, persistent bool) State { +func NewState(tree *iavl.VersionedTree) State { base := NewBonsai(tree) return State{ committed: base, deliverTx: base.Checkpoint(), checkTx: base.Checkpoint(), - persistent: persistent, + persistent: true, } } diff --git a/state/merkle_test.go b/state/merkle_test.go index fa039956d..9866195b4 100644 --- a/state/merkle_test.go +++ b/state/merkle_test.go @@ -66,7 +66,7 @@ func TestStateCommitHash(t *testing.T) { // make the store... tree := iavl.NewVersionedTree(0, db.NewMemDB()) - store := NewState(tree, false) + store := NewState(tree) for n, r := range tc.rounds { // start the cache @@ -77,7 +77,7 @@ func TestStateCommitHash(t *testing.T) { deliver.Set(k, v) } // commit and add hash to result - hash, err := store.Commit() + hash, err := store.Commit(uint64(n + 1)) require.Nil(err, "tc:%d / rnd:%d - %+v", i, n, err) result[n] = hash } From e0604baad58e0f8f13a24f4c65ac5d99796c2c3e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 10 Oct 2017 13:48:48 +0200 Subject: [PATCH 07/13] Get abci commit hash/height working --- app/app_test.go | 7 ++-- app/store.go | 88 ++++++++++++++----------------------------------- state/merkle.go | 15 --------- 3 files changed, 29 insertions(+), 81 deletions(-) diff --git a/app/app_test.go b/app/app_test.go index fca03e15e..1cf983459 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk" "github.com/cosmos/cosmos-sdk/modules/auth" "github.com/cosmos/cosmos-sdk/modules/base" @@ -18,6 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/modules/roles" "github.com/cosmos/cosmos-sdk/stack" "github.com/cosmos/cosmos-sdk/state" + abci "github.com/tendermint/abci/types" wire "github.com/tendermint/go-wire" "github.com/tendermint/tmlibs/log" ) @@ -294,9 +294,10 @@ func TestQuery(t *testing.T) { res = at.app.Commit() assert.True(res.IsOK(), res) + key := stack.PrefixedKey(coin.NameCoin, at.acctIn.Address()) resQueryPostCommit := at.app.Query(abci.RequestQuery{ - Path: "/account", - Data: at.acctIn.Address(), + Path: "/key", + Data: key, }) assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit") } diff --git a/app/store.go b/app/store.go index 1f4442004..ee933fab9 100644 --- a/app/store.go +++ b/app/store.go @@ -1,7 +1,6 @@ package app import ( - "bytes" "fmt" "path" "path/filepath" @@ -9,7 +8,6 @@ import ( "github.com/pkg/errors" abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-wire" "github.com/tendermint/iavl" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -21,21 +19,9 @@ import ( // Store contains the merkle tree, and all info to handle abci requests type Store struct { state.State - height uint64 - hash []byte - persisted bool - logger log.Logger } -var stateKey = []byte("merkle:state") // Database key for merkle tree save value db values - -// ChainState contains the latest Merkle root hash and the number of times `Commit` has been called -type ChainState struct { - Hash []byte - Height uint64 -} - // MockStore returns an in-memory store only intended for testing func MockStore() *Store { res, err := NewStore("", 0, log.NewNopLogger()) @@ -49,11 +35,7 @@ func MockStore() *Store { // NewStore initializes an in-memory iavl.VersionedTree, or attempts to load a // persistant tree from disk func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { - // start at 1 so the height returned by query is for the - // next block, ie. the one that includes the AppHash for our current state - initialHeight := uint64(1) - - // Non-persistent case + // memory backed case, just for testing if dbName == "" { tree := iavl.NewVersionedTree( 0, @@ -61,7 +43,6 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { ) store := &Store{ State: state.NewState(tree), - height: initialHeight, logger: logger, } return store, nil @@ -87,79 +68,60 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { db := dbm.NewDB(name, dbm.LevelDBBackendStr, dir) tree := iavl.NewVersionedTree(cacheSize, db) - var chainState ChainState if empty { logger.Info("no existing db, creating new db") - chainState = ChainState{ - Hash: nil, - Height: initialHeight, - } - db.Set(stateKey, wire.BinaryBytes(chainState)) } else { logger.Info("loading existing db") - eyesStateBytes := db.Get(stateKey) - - if err = wire.ReadBinaryBytes(eyesStateBytes, &chainState); err != nil { - return nil, errors.Wrap(err, "Reading MerkleEyesState") - } if err = tree.Load(); err != nil { return nil, errors.Wrap(err, "Loading tree") } } res := &Store{ - State: state.NewState(tree), - height: chainState.Height, - hash: chainState.Hash, - persisted: true, - logger: logger, + State: state.NewState(tree), + logger: logger, } return res, nil } +func (s *Store) Height() uint64 { + return s.State.Committed().Tree.LatestVersion() +} + +func (s *Store) Hash() []byte { + return s.State.Committed().Tree.Hash() +} + // Info implements abci.Application. It returns the height, hash and size (in the data). // The height is the block that holds the transactions, not the apphash itself. func (s *Store) Info() abci.ResponseInfo { s.logger.Info("Info synced", - "height", s.height, - "hash", fmt.Sprintf("%X", s.hash)) + "height", s.Height(), + "hash", fmt.Sprintf("%X", s.Hash())) return abci.ResponseInfo{ Data: cmn.Fmt("size:%v", s.State.Size()), - LastBlockHeight: s.height - 1, - LastBlockAppHash: s.hash, + LastBlockHeight: s.Height() - 1, + LastBlockAppHash: s.Hash(), } } // Commit implements abci.Application func (s *Store) Commit() abci.Result { - var err error - s.height++ - s.hash, err = s.State.Hash() + height := s.Height() + 1 + + hash, err := s.State.Commit(height) if err != nil { return abci.NewError(abci.CodeType_InternalError, err.Error()) } - s.logger.Debug("Commit synced", - "height", s.height, - "hash", fmt.Sprintf("%X", s.hash)) - - s.State.BatchSet(stateKey, wire.BinaryBytes(ChainState{ - Hash: s.hash, - Height: s.height, - })) - - hash, err := s.State.Commit(s.height) - if err != nil { - return abci.NewError(abci.CodeType_InternalError, err.Error()) - } - if !bytes.Equal(hash, s.hash) { - return abci.NewError(abci.CodeType_InternalError, "AppHash is incorrect") - } + "height", height, + "hash", fmt.Sprintf("%X", hash), + ) if s.State.Size() == 0 { return abci.NewResultOK(nil, "Empty hash for empty tree") } - return abci.NewResultOK(s.hash, "") + return abci.NewResultOK(hash, "") } // Query implements abci.Application @@ -169,10 +131,10 @@ func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) height := reqQuery.Height if height == 0 { - if tree.Tree.VersionExists(s.height - 1) { - height = s.height - 1 + if tree.Tree.VersionExists(s.Height() - 1) { + height = s.Height() - 1 } else { - height = s.height + height = s.Height() } } resQuery.Height = height diff --git a/state/merkle.go b/state/merkle.go index a097b9431..f23be1cc7 100644 --- a/state/merkle.go +++ b/state/merkle.go @@ -37,21 +37,6 @@ func (s State) Check() SimpleDB { return s.checkTx } -// Hash applies deliverTx to committed and calculates hash -// -// Note the usage: -// Hash -> gets the calculated hash but doesn't save -// BatchSet -> adds some out-of-bounds data -// Commit -> Save everything to disk and the same hash -func (s *State) Hash() ([]byte, error) { - err := s.committed.Commit(s.deliverTx) - if err != nil { - return nil, err - } - s.deliverTx = s.committed.Checkpoint() - return s.committed.Tree.Hash(), nil -} - // BatchSet is used for some weird magic in storing the new height func (s *State) BatchSet(key, value []byte) { if s.persistent { From 1e6d9bcac64274bbd78eba838a7e8771d7b39cac Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 10 Oct 2017 13:55:17 +0200 Subject: [PATCH 08/13] cleanup --- app/store.go | 6 ++++-- state/merkle.go | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/store.go b/app/store.go index ee933fab9..c70adf9b0 100644 --- a/app/store.go +++ b/app/store.go @@ -84,12 +84,14 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { return res, nil } +// Height gets the last height stored in the database func (s *Store) Height() uint64 { - return s.State.Committed().Tree.LatestVersion() + return s.State.LatestHeight() } +// Hash gets the last hash stored in the database func (s *Store) Hash() []byte { - return s.State.Committed().Tree.Hash() + return s.State.LatestHash() } // Info implements abci.Application. It returns the height, hash and size (in the data). diff --git a/state/merkle.go b/state/merkle.go index f23be1cc7..14ad53002 100644 --- a/state/merkle.go +++ b/state/merkle.go @@ -37,6 +37,14 @@ func (s State) Check() SimpleDB { return s.checkTx } +func (s State) LatestHeight() uint64 { + return s.committed.Tree.LatestVersion() +} + +func (s State) LatestHash() []byte { + return s.committed.Tree.Hash() +} + // BatchSet is used for some weird magic in storing the new height func (s *State) BatchSet(key, value []byte) { if s.persistent { From 9092b4d7f10a89072d65a3247df955f9089dc3f4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 10 Oct 2017 14:27:38 +0200 Subject: [PATCH 09/13] Fix TestTxProofs --- app/app.go | 1 + app/store.go | 1 + client/query.go | 10 +++++++++- client/query_test.go | 20 ++++++++++++-------- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app/app.go b/app/app.go index eebc816e8..89f5ed6df 100644 --- a/app/app.go +++ b/app/app.go @@ -165,6 +165,7 @@ func (app *Basecoin) InitChain(req abci.RequestInitChain) { // BeginBlock - ABCI func (app *Basecoin) BeginBlock(req abci.RequestBeginBlock) { + fmt.Println("BeginBlock") app.height++ // for _, plugin := range app.plugins.GetList() { // plugin.BeginBlock(app.state, hash, header) diff --git a/app/store.go b/app/store.go index c70adf9b0..0f9a2c4f5 100644 --- a/app/store.go +++ b/app/store.go @@ -123,6 +123,7 @@ func (s *Store) Commit() abci.Result { if s.State.Size() == 0 { return abci.NewResultOK(nil, "Empty hash for empty tree") } + fmt.Printf("ABCI Commit: %d / %X\n", height, hash) return abci.NewResultOK(hash, "") } diff --git a/client/query.go b/client/query.go index bdc3e98c5..a25d80407 100644 --- a/client/query.go +++ b/client/query.go @@ -1,6 +1,8 @@ package client import ( + "fmt" + "github.com/pkg/errors" "github.com/tendermint/go-wire/data" @@ -39,7 +41,9 @@ func GetWithProof(key []byte, node client.Client, cert certifiers.Certifier) ( return } - check, err := GetCertifiedCheckpoint(int(resp.Height), node, cert) + var check lc.Checkpoint + // check, err = GetCertifiedCheckpoint(int(resp.Height), node, cert) + check, err = GetCertifiedCheckpoint(int(resp.Height+9), node, cert) if err != nil { return } @@ -70,6 +74,10 @@ func GetWithProof(key []byte, node client.Client, cert certifiers.Certifier) ( return } + fmt.Printf("apphash: %x\n", check.Header.AppHash) + fmt.Printf("proof hash: %x\n", aproof.Root()) + fmt.Printf("response height: %d\n", resp.Height) + // Validate the proof against the certified header to ensure data integrity. err = aproof.Verify(resp.Key, nil, check.Header.AppHash) if err != nil { diff --git a/client/query_test.go b/client/query_test.go index aaac2bea3..5d6a5bf5c 100644 --- a/client/query_test.go +++ b/client/query_test.go @@ -1,6 +1,7 @@ package client import ( + "fmt" "os" "testing" @@ -103,13 +104,14 @@ func TestTxProofs(t *testing.T) { cl := client.NewLocal(node) client.WaitForHeight(cl, 1, nil) - tx := eyes.SetTx{Key: []byte("key-a"), Value: []byte("value-a")}.Wrap() + tx := eyes.NewSetTx([]byte("key-a"), []byte("value-a")) btx := types.Tx(wire.BinaryBytes(tx)) br, err := cl.BroadcastTxCommit(btx) require.NoError(err, "%+v", err) require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx) require.EqualValues(0, br.DeliverTx.Code) + fmt.Printf("tx height: %d\n", br.Height) source := certclient.New(cl) seed, err := source.GetByHeight(br.Height - 2) @@ -118,18 +120,20 @@ func TestTxProofs(t *testing.T) { // First let's make sure a bogus transaction hash returns a valid non-existence proof. key := types.Tx([]byte("bogus")).Hash() - bs, _, proof, err := GetWithProof(key, cl, cert) - assert.Nil(bs, "value should be nil") - require.True(lc.IsNoDataErr(err), "error should signal 'no data'") - require.NotNil(proof, "proof shouldn't be nil") - err = proof.Verify(key, nil, proof.Root()) - require.NoError(err, "%+v", err) + res, err := cl.Tx(key, true) + require.NotNil(err) + require.Contains(err.Error(), "not found") // Now let's check with the real tx hash. key = btx.Hash() - res, err := cl.Tx(key, true) + res, err = cl.Tx(key, true) require.NoError(err, "%+v", err) require.NotNil(res) err = res.Proof.Validate(key) assert.NoError(err, "%+v", err) + + check, err := GetCertifiedCheckpoint(int(br.Height), cl, cert) + require.Nil(err, "%+v", err) + require.Equal(res.Proof.RootHash, check.Header.DataHash) + } From 5238448a46eec7c2d323e157f14ba1acecf76b39 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 10 Oct 2017 14:53:58 +0200 Subject: [PATCH 10/13] Make sure height in store increments every block, even if empty --- app/app.go | 1 - app/store.go | 24 ++++++++++-------------- client/query.go | 11 ++--------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/app/app.go b/app/app.go index 89f5ed6df..eebc816e8 100644 --- a/app/app.go +++ b/app/app.go @@ -165,7 +165,6 @@ func (app *Basecoin) InitChain(req abci.RequestInitChain) { // BeginBlock - ABCI func (app *Basecoin) BeginBlock(req abci.RequestBeginBlock) { - fmt.Println("BeginBlock") app.height++ // for _, plugin := range app.plugins.GetList() { // plugin.BeginBlock(app.state, hash, header) diff --git a/app/store.go b/app/store.go index 0f9a2c4f5..a9a8b4360 100644 --- a/app/store.go +++ b/app/store.go @@ -19,6 +19,7 @@ import ( // Store contains the merkle tree, and all info to handle abci requests type Store struct { state.State + height uint64 logger log.Logger } @@ -81,14 +82,10 @@ func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) { State: state.NewState(tree), logger: logger, } + res.height = res.State.LatestHeight() return res, nil } -// Height gets the last height stored in the database -func (s *Store) Height() uint64 { - return s.State.LatestHeight() -} - // Hash gets the last hash stored in the database func (s *Store) Hash() []byte { return s.State.LatestHash() @@ -98,32 +95,31 @@ func (s *Store) Hash() []byte { // The height is the block that holds the transactions, not the apphash itself. func (s *Store) Info() abci.ResponseInfo { s.logger.Info("Info synced", - "height", s.Height(), + "height", s.height, "hash", fmt.Sprintf("%X", s.Hash())) return abci.ResponseInfo{ Data: cmn.Fmt("size:%v", s.State.Size()), - LastBlockHeight: s.Height() - 1, + LastBlockHeight: s.height - 1, LastBlockAppHash: s.Hash(), } } // Commit implements abci.Application func (s *Store) Commit() abci.Result { - height := s.Height() + 1 + s.height++ - hash, err := s.State.Commit(height) + hash, err := s.State.Commit(s.height) if err != nil { return abci.NewError(abci.CodeType_InternalError, err.Error()) } s.logger.Debug("Commit synced", - "height", height, + "height", s.height, "hash", fmt.Sprintf("%X", hash), ) if s.State.Size() == 0 { return abci.NewResultOK(nil, "Empty hash for empty tree") } - fmt.Printf("ABCI Commit: %d / %X\n", height, hash) return abci.NewResultOK(hash, "") } @@ -134,10 +130,10 @@ func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) height := reqQuery.Height if height == 0 { - if tree.Tree.VersionExists(s.Height() - 1) { - height = s.Height() - 1 + if tree.Tree.VersionExists(s.height - 1) { + height = s.height - 1 } else { - height = s.Height() + height = s.height } } resQuery.Height = height diff --git a/client/query.go b/client/query.go index a25d80407..a4f4ff1ea 100644 --- a/client/query.go +++ b/client/query.go @@ -1,8 +1,6 @@ package client import ( - "fmt" - "github.com/pkg/errors" "github.com/tendermint/go-wire/data" @@ -41,9 +39,9 @@ func GetWithProof(key []byte, node client.Client, cert certifiers.Certifier) ( return } + // AppHash for height H is in header H+1 var check lc.Checkpoint - // check, err = GetCertifiedCheckpoint(int(resp.Height), node, cert) - check, err = GetCertifiedCheckpoint(int(resp.Height+9), node, cert) + check, err = GetCertifiedCheckpoint(int(resp.Height+1), node, cert) if err != nil { return } @@ -73,11 +71,6 @@ func GetWithProof(key []byte, node client.Client, cert certifiers.Certifier) ( err = errors.Wrap(err, "Error reading proof") return } - - fmt.Printf("apphash: %x\n", check.Header.AppHash) - fmt.Printf("proof hash: %x\n", aproof.Root()) - fmt.Printf("response height: %d\n", resp.Height) - // Validate the proof against the certified header to ensure data integrity. err = aproof.Verify(resp.Key, nil, check.Header.AppHash) if err != nil { From 98e615709ae02ea0a1301eb5642cef9aa366d54b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 10 Oct 2017 16:16:25 +0200 Subject: [PATCH 11/13] Return most recent state, even if it does block --- app/store.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/store.go b/app/store.go index a9a8b4360..9009653d6 100644 --- a/app/store.go +++ b/app/store.go @@ -130,11 +130,16 @@ func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) height := reqQuery.Height if height == 0 { - if tree.Tree.VersionExists(s.height - 1) { - height = s.height - 1 - } else { - height = s.height - } + // 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 + + // if tree.Tree.VersionExists(s.height - 1) { + // height = s.height - 1 + // } else { + height = s.height + // } } resQuery.Height = height From 724fb065f2d0629390af3ef755ce664f9e99d477 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 10 Oct 2017 17:10:10 +0200 Subject: [PATCH 12/13] Fix off-by-one in handshake, add debug logging --- app/app.go | 4 +++- app/store.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/app.go b/app/app.go index eebc816e8..d0ef500da 100644 --- a/app/app.go +++ b/app/app.go @@ -59,6 +59,9 @@ func (app *Basecoin) GetState() sm.SimpleDB { // Info - ABCI func (app *Basecoin) Info(req abci.RequestInfo) abci.ResponseInfo { resp := app.state.Info() + app.logger.Debug("Info", + "height", resp.LastBlockHeight, + "hash", fmt.Sprintf("%X", resp.LastBlockAppHash)) app.height = resp.LastBlockHeight return abci.ResponseInfo{ Data: fmt.Sprintf("Basecoin v%v", version.Version), @@ -70,7 +73,6 @@ func (app *Basecoin) Info(req abci.RequestInfo) abci.ResponseInfo { // InitState - used to setup state (was SetOption) // to be used by InitChain later func (app *Basecoin) InitState(key string, value string) string { - module, key := splitKey(key) state := app.state.Append() diff --git a/app/store.go b/app/store.go index 9009653d6..05843f64f 100644 --- a/app/store.go +++ b/app/store.go @@ -99,7 +99,7 @@ func (s *Store) Info() abci.ResponseInfo { "hash", fmt.Sprintf("%X", s.Hash())) return abci.ResponseInfo{ Data: cmn.Fmt("size:%v", s.State.Size()), - LastBlockHeight: s.height - 1, + LastBlockHeight: s.height, LastBlockAppHash: s.Hash(), } } From 7050862261c7d749c0e12ec6fc79b58bbaafdb09 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Tue, 10 Oct 2017 17:15:12 +0200 Subject: [PATCH 13/13] Update glide.lock for latest iavl --- glide.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glide.lock b/glide.lock index 5febbd478..c31550884 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: fbfdd03c0367bb0785ceb81ed34059df219e55d5a9c71c12597e505fbce14165 -updated: 2017-10-05T12:45:12.67543189+02:00 +updated: 2017-10-10T17:14:33.612302321+02:00 imports: - name: github.com/bgentry/speakeasy version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd @@ -133,7 +133,7 @@ imports: - data - data/base58 - name: github.com/tendermint/iavl - version: ff8784b7ba609d80ef78ebcc5ca4bd2d01b04259 + version: 372f484952449aae18cce33b82c13329a9009acf - name: github.com/tendermint/light-client version: ac2e4bf47b31aaf5d3d336691ac786ec751bfc32 subpackages: