From 2b735d8968eeb59bc3d5a5c7a9c71e7190fe288f Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Tue, 8 Aug 2017 11:51:15 +0200 Subject: [PATCH] Support non-existence proof --- client/commands/query/get.go | 53 ++++++++++++++++++++--------- client/commands/query/query_test.go | 10 +++--- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/client/commands/query/get.go b/client/commands/query/get.go index f608c71acc..46b6c64c5a 100644 --- a/client/commands/query/get.go +++ b/client/commands/query/get.go @@ -55,62 +55,81 @@ func Get(key []byte, prove bool) (data.Bytes, uint64, error) { resp, err := node.ABCIQuery("/key", key, false) return data.Bytes(resp.Value), resp.Height, err } - val, h, _, err := GetWithProof(key) + val, h, _, _, err := GetWithProof(key) return val, h, err } // GetWithProof returns the values stored under a given key at the named // height as in Get. Additionally, it will return a validated merkle // proof for the key-value pair if it exists, and all checks pass. -func GetWithProof(key []byte) (data.Bytes, uint64, *iavl.KeyExistsProof, error) { +func GetWithProof(key []byte) (data.Bytes, uint64, + *iavl.KeyExistsProof, *iavl.KeyNotExistsProof, error) { + node := commands.GetNode() cert, err := commands.GetCertifier() if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, err } return CustomGetWithProof(key, node, cert) } // TODO: fix this up alexis -func CustomGetWithProof(key []byte, node client.Client, - cert Certifier) (data.Bytes, uint64, *iavl.KeyExistsProof, error) { +func CustomGetWithProof(key []byte, node client.Client, cert Certifier) (data.Bytes, uint64, + *iavl.KeyExistsProof, *iavl.KeyNotExistsProof, error) { resp, err := node.ABCIQuery("/key", key, true) if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, err } ph := int(resp.Height) // make sure the proof is the proper height if !resp.Code.IsOK() { - return nil, 0, nil, errors.Errorf("Query error %d: %s", resp.Code, resp.Code.String()) + return nil, 0, nil, nil, errors.Errorf("Query error %d: %s", resp.Code, resp.Code.String()) } - // TODO: Handle null proofs - if len(resp.Key) == 0 || len(resp.Value) == 0 || len(resp.Proof) == 0 { - return nil, 0, nil, lc.ErrNoData() + if len(resp.Key) == 0 || len(resp.Proof) == 0 { + return nil, 0, nil, nil, lc.ErrNoData() } if ph != 0 && ph != int(resp.Height) { - return nil, 0, nil, lc.ErrHeightMismatch(ph, int(resp.Height)) + return nil, 0, nil, nil, lc.ErrHeightMismatch(ph, int(resp.Height)) } check, err := GetCertifiedCheckpoint(ph, node, cert) if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, err } - proof := new(iavl.KeyExistsProof) + if len(resp.Value) > 0 { + // The key was found, construct a proof of existence. + proof := new(iavl.KeyExistsProof) + err = wire.ReadBinaryBytes(resp.Proof, &proof) + if err != nil { + return nil, 0, nil, nil, err + } + + // validate the proof against the certified header to ensure data integrity + err = proof.Verify(resp.Key, resp.Value, check.Header.AppHash) + if err != nil { + return nil, 0, nil, nil, err + } + + return data.Bytes(resp.Value), resp.Height, proof, nil, nil + } + + // The key wasn't found, construct a proof of non-existence. + proof := new(iavl.KeyNotExistsProof) err = wire.ReadBinaryBytes(resp.Proof, &proof) if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, err } // validate the proof against the certified header to ensure data integrity - err = proof.Verify(resp.Key, resp.Value, check.Header.AppHash) + err = proof.Verify(resp.Key, check.Header.AppHash) if err != nil { - return nil, 0, nil, err + return nil, 0, nil, nil, err } - return data.Bytes(resp.Value), resp.Height, proof, nil + return data.Bytes(resp.Value), resp.Height, nil, proof, nil } // GetCertifiedCheckpoint gets the signed header for a given height diff --git a/client/commands/query/query_test.go b/client/commands/query/query_test.go index 862b4ceb53..07c5716326 100644 --- a/client/commands/query/query_test.go +++ b/client/commands/query/query_test.go @@ -65,9 +65,9 @@ func TestAppProofs(t *testing.T) { // Test existing key. - bs, _, proof, err := CustomGetWithProof(k, cl, cert) + bs, _, proofExists, _, err := CustomGetWithProof(k, cl, cert) require.Nil(err, "%+v", err) - require.NotNil(proof) + require.NotNil(proofExists) var data eyes.Data err = wire.ReadBinaryBytes(bs, &data) @@ -76,10 +76,10 @@ func TestAppProofs(t *testing.T) { // Test non-existing key. - // TODO: This currently fails. missing := []byte("my-missing-key") - bs, _, proof, err = CustomGetWithProof(missing, cl, cert) + bs, _, proofExists, proofNotExists, err := CustomGetWithProof(missing, cl, cert) require.Nil(err, "%+v", err) require.Nil(bs) - require.NotNil(proof) + require.Nil(proofExists) + require.NotNil(proofNotExists) }