diff --git a/Gopkg.lock b/Gopkg.lock index f9be7bd97a9e..f868142edfce 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -435,7 +435,7 @@ version = "v0.11.1" [[projects]] - digest = "1:92d7d1678577fd1a6f3348168cef87880bbc710ef5f4e9a1216f45c56567d734" + digest = "1:395820b381043b9d2204e181ddf0f9147397c4a7b8f5dc3162de4cfcddf4589a" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -501,7 +501,8 @@ "version", ] pruneopts = "UT" - revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" + revision = "03e42d2e3866f01a00625f608e3bbfaeb30690de" + version = "v0.26.1-rc0" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" diff --git a/Gopkg.toml b/Gopkg.toml index a66574ea62f8..5726008858c4 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -36,7 +36,7 @@ [[override]] name = "github.com/tendermint/tendermint" - revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" # TODO replace w/ 0.26.1 + version = "v0.26.1-rc0" # TODO replace w/ 0.26.1 ## deps without releases: @@ -84,4 +84,3 @@ [prune] go-tests = true unused-packages = true - diff --git a/PENDING.md b/PENDING.md index 11c13ea75f00..f2ad9bd28da0 100644 --- a/PENDING.md +++ b/PENDING.md @@ -51,6 +51,7 @@ IMPROVEMENTS - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - \#2610 [x/stake] Block redelegation to and from the same validator - \#2652 [x/auth] Add benchmark for get and set account + - \#2685 [x/store] Add general merkle absence proof (also for empty substores) * Tendermint diff --git a/store/iavlstore.go b/store/iavlstore.go index bb588f029b65..fccde38e27e0 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -225,8 +225,21 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Log = err.Error() break } - res.Value = value - res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(key, proof).ProofOp()}} + if proof == nil { + // Proof == nil implies that the store is empty. + if value != nil { + panic("unexpected value for an empty proof") + } + } + if value != nil { + // value was found + res.Value = value + res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(key, proof).ProofOp()}} + } else { + // value wasn't found + res.Value = nil + res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLAbsenceOp(key, proof).ProofOp()}} + } } else { _, res.Value = tree.GetVersioned(key, res.Height) } diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index 19feef160023..db3b65cad53d 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -106,3 +106,69 @@ func TestVerifyMultiStoreQueryProof(t *testing.T) { err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte(nil)) require.NotNil(t, err) } + +func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + store := NewCommitMultiStore(db) + iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.LoadVersion(0) + cid := store.Commit() // Commit with empty iavl store. + + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) + + // Verify proof. + prt := DefaultProofRuntime() + err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY") + require.Nil(t, err) + + // Verify (bad) proof. + prt = DefaultProofRuntime() + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) +} + +func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + store := NewCommitMultiStore(db) + iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.LoadVersion(0) + + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() // Commit with empty iavl store. + + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYABSENTKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) + + // Verify proof. + prt := DefaultProofRuntime() + err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY") + require.Nil(t, err) + + // Verify (bad) proof. + prt = DefaultProofRuntime() + err = prt.VerifyAbsence(res.Proof, cid.Hash, "/MYABSENTKEY") + require.NotNil(t, err) + + // Verify (bad) proof. + prt = DefaultProofRuntime() + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY", []byte("")) + require.NotNil(t, err) +} diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 6c491bda1380..cd2d0135f113 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -295,6 +295,10 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { return res } + if res.Proof == nil || len(res.Proof.Ops) == 0 { + return sdk.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() + } + commitInfo, errMsg := getCommitInfo(rs.db, res.Height) if errMsg != nil { return sdk.ErrInternal(errMsg.Error()).QueryResult()