forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
query.go
111 lines (97 loc) · 3.02 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package client
import (
"github.com/pkg/errors"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/iavl"
lc "github.com/tendermint/light-client"
"github.com/tendermint/light-client/certifiers"
"github.com/tendermint/tendermint/rpc/client"
)
// GetWithProof will query the key on the given node, and verify it has
// a valid proof, as defined by the certifier.
//
// If there is any error in checking, returns an error.
// If val is non-empty, proof should be KeyExistsProof
// If val is empty, proof should be KeyMissingProof
func GetWithProof(key []byte, node client.Client, cert certifiers.Certifier) (
val data.Bytes, height uint64, proof iavl.KeyProof, err error) {
resp, err := node.ABCIQuery("/key", key, true)
if err != nil {
return
}
// make sure the proof is the proper height
if !resp.Code.IsOK() {
err = errors.Errorf("Query error %d: %s", resp.Code, resp.Code.String())
return
}
if len(resp.Key) == 0 || len(resp.Proof) == 0 {
err = lc.ErrNoData()
return
}
if resp.Height == 0 {
err = errors.New("Height returned is zero")
return
}
check, err := GetCertifiedCheckpoint(int(resp.Height), node, cert)
if err != nil {
return
}
if len(resp.Value) > 0 {
// The key was found, construct a proof of existence.
var eproof *iavl.KeyExistsProof
eproof, err = iavl.ReadKeyExistsProof(resp.Proof)
if err != nil {
err = errors.Wrap(err, "Error reading proof")
return
}
// Validate the proof against the certified header to ensure data integrity.
err = eproof.Verify(resp.Key, resp.Value, check.Header.AppHash)
if err != nil {
err = errors.Wrap(err, "Couldn't verify proof")
return
}
val = data.Bytes(resp.Value)
proof = eproof
} else {
// The key wasn't found, construct a proof of non-existence.
var aproof *iavl.KeyAbsentProof
aproof, err = iavl.ReadKeyAbsentProof(resp.Proof)
if err != nil {
err = errors.Wrap(err, "Error reading proof")
return
}
// Validate the proof against the certified header to ensure data integrity.
err = proof.Verify(resp.Key, nil, check.Header.AppHash)
if err != nil {
err = errors.Wrap(err, "Couldn't verify proof")
return
}
err = lc.ErrNoData()
proof = aproof
}
height = resp.Height
return
}
// GetCertifiedCheckpoint gets the signed header for a given height
// and certifies it. Returns error if unable to get a proven header.
func GetCertifiedCheckpoint(h int, node client.Client,
cert certifiers.Certifier) (empty lc.Checkpoint, err error) {
// FIXME: cannot use cert.GetByHeight for now, as it also requires
// Validators and will fail on querying tendermint for non-current height.
// When this is supported, we should use it instead...
client.WaitForHeight(node, h, nil)
commit, err := node.Commit(&h)
if err != nil {
return
}
check := lc.Checkpoint{
Header: commit.Header,
Commit: commit.Commit,
}
// validate downloaded checkpoint with our request and trust store.
if check.Height() != h {
return empty, lc.ErrHeightMismatch(h, check.Height())
}
err = cert.Certify(check)
return check, nil
}