Move proof logic away from viper
This commit is contained in:
parent
521503026e
commit
e0ddecc229
|
@ -10,12 +10,10 @@ import (
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
lc "github.com/tendermint/light-client"
|
|
||||||
"github.com/tendermint/light-client/certifiers"
|
|
||||||
"github.com/tendermint/light-client/proofs"
|
"github.com/tendermint/light-client/proofs"
|
||||||
"github.com/tendermint/merkleeyes/iavl"
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
"github.com/tendermint/tendermint/rpc/client"
|
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/commands"
|
"github.com/cosmos/cosmos-sdk/client/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,98 +65,7 @@ func GetWithProof(key []byte) (data.Bytes, uint64,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, nil, err
|
return nil, 0, nil, nil, err
|
||||||
}
|
}
|
||||||
return getWithProof(key, node, cert)
|
return client.GetWithProof(key, node, cert)
|
||||||
}
|
|
||||||
|
|
||||||
func getWithProof(key []byte, node client.Client, cert certifiers.Certifier) (
|
|
||||||
val data.Bytes, height uint64, eproof *iavl.KeyExistsProof, neproof *iavl.KeyNotExistsProof, 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.
|
|
||||||
eproof = new(iavl.KeyExistsProof)
|
|
||||||
err = wire.ReadBinaryBytes(resp.Proof, &eproof)
|
|
||||||
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)
|
|
||||||
} else {
|
|
||||||
// The key wasn't found, construct a proof of non-existence.
|
|
||||||
neproof = new(iavl.KeyNotExistsProof)
|
|
||||||
err = wire.ReadBinaryBytes(resp.Proof, &neproof)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "Error reading proof")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the proof against the certified header to ensure data integrity.
|
|
||||||
err = neproof.Verify(resp.Key, check.Header.AppHash)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "Couldn't verify proof")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = lc.ErrNoData()
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseHexKey parses the key flag as hex and converts to bytes or returns error
|
// ParseHexKey parses the key flag as hex and converts to bytes or returns error
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/commands"
|
"github.com/cosmos/cosmos-sdk/client/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ func txQueryCmd(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
check, err := getCertifiedCheckpoint(res.Height, node, cert)
|
check, err := client.GetCertifiedCheckpoint(res.Height, node, cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
certclient "github.com/tendermint/light-client/certifiers/client"
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||||
"github.com/tendermint/tendermint/rpc/client"
|
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/commands"
|
"github.com/cosmos/cosmos-sdk/client/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,15 +39,14 @@ func init() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSecureNode() (client.Client, error) {
|
func getSecureNode() (rpcclient.Client, error) {
|
||||||
// First, connect a client
|
// First, connect a client
|
||||||
c := commands.GetNode()
|
c := commands.GetNode()
|
||||||
cert, err := commands.GetCertifier()
|
cert, err := commands.GetCertifier()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sc := certclient.Wrap(c, cert)
|
return client.GetSecureNode(c, cert), nil
|
||||||
return sc, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// printResult just writes the struct to the console, returns an error if it can't
|
// printResult just writes the struct to the console, returns an error if it can't
|
||||||
|
|
|
@ -47,6 +47,7 @@ func exportSeed(cmd *cobra.Command, args []string) error {
|
||||||
return writeSeed(seed, path)
|
return writeSeed(seed, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO use certifiers function
|
||||||
func writeSeed(seed certifiers.Seed, path string) (err error) {
|
func writeSeed(seed certifiers.Seed, path string) (err error) {
|
||||||
f, err := os.Create(path)
|
f, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -46,3 +46,9 @@ func GetCertifier(chainID string, trust certifiers.Provider,
|
||||||
cert := certifiers.NewInquiring(chainID, seed, trust, source)
|
cert := certifiers.NewInquiring(chainID, seed, trust, source)
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSecureNode uses a given certifier to wrap an connection to an untrusted
|
||||||
|
// host and return a cryptographically secure rpc client.
|
||||||
|
func GetSecureNode(c rpcclient.Client, cert *certifiers.InquiringCertifier) rpcclient.Client {
|
||||||
|
return certclient.Wrap(c, cert)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/go-wire/data"
|
||||||
|
lc "github.com/tendermint/light-client"
|
||||||
|
"github.com/tendermint/light-client/certifiers"
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
|
||||||
|
"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, eproof will be non-nil
|
||||||
|
// If val is empty, neproof will be non-nil
|
||||||
|
func GetWithProof(key []byte, node client.Client, cert certifiers.Certifier) (
|
||||||
|
val data.Bytes, height uint64, eproof *iavl.KeyExistsProof,
|
||||||
|
neproof *iavl.KeyNotExistsProof, 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.
|
||||||
|
eproof = new(iavl.KeyExistsProof)
|
||||||
|
err = wire.ReadBinaryBytes(resp.Proof, &eproof)
|
||||||
|
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)
|
||||||
|
} else {
|
||||||
|
// The key wasn't found, construct a proof of non-existence.
|
||||||
|
neproof = new(iavl.KeyNotExistsProof)
|
||||||
|
err = wire.ReadBinaryBytes(resp.Proof, &neproof)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "Error reading proof")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the proof against the certified header to ensure data integrity.
|
||||||
|
err = neproof.Verify(resp.Key, check.Header.AppHash)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "Couldn't verify proof")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = lc.ErrNoData()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package query
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
@ -69,7 +69,7 @@ func TestAppProofs(t *testing.T) {
|
||||||
// Test existing key.
|
// Test existing key.
|
||||||
var data eyes.Data
|
var data eyes.Data
|
||||||
|
|
||||||
bs, height, proofExists, _, err := getWithProof(k, cl, cert)
|
bs, height, proofExists, _, err := GetWithProof(k, cl, cert)
|
||||||
require.NoError(err, "%+v", err)
|
require.NoError(err, "%+v", err)
|
||||||
require.NotNil(proofExists)
|
require.NotNil(proofExists)
|
||||||
require.True(height >= uint64(latest.Header.Height))
|
require.True(height >= uint64(latest.Header.Height))
|
||||||
|
@ -87,7 +87,7 @@ func TestAppProofs(t *testing.T) {
|
||||||
|
|
||||||
// Test non-existing key.
|
// Test non-existing key.
|
||||||
missing := []byte("my-missing-key")
|
missing := []byte("my-missing-key")
|
||||||
bs, _, proofExists, proofNotExists, err := getWithProof(missing, cl, cert)
|
bs, _, proofExists, proofNotExists, err := GetWithProof(missing, cl, cert)
|
||||||
require.True(lc.IsNoDataErr(err))
|
require.True(lc.IsNoDataErr(err))
|
||||||
require.Nil(bs)
|
require.Nil(bs)
|
||||||
require.Nil(proofExists)
|
require.Nil(proofExists)
|
||||||
|
@ -119,7 +119,7 @@ func TestTxProofs(t *testing.T) {
|
||||||
|
|
||||||
// First let's make sure a bogus transaction hash returns a valid non-existence proof.
|
// First let's make sure a bogus transaction hash returns a valid non-existence proof.
|
||||||
key := types.Tx([]byte("bogus")).Hash()
|
key := types.Tx([]byte("bogus")).Hash()
|
||||||
bs, _, proofExists, proofNotExists, err := getWithProof(key, cl, cert)
|
bs, _, proofExists, proofNotExists, err := GetWithProof(key, cl, cert)
|
||||||
assert.Nil(bs, "value should be nil")
|
assert.Nil(bs, "value should be nil")
|
||||||
require.True(lc.IsNoDataErr(err), "error should signal 'no data'")
|
require.True(lc.IsNoDataErr(err), "error should signal 'no data'")
|
||||||
assert.Nil(proofExists, "existence proof should be nil")
|
assert.Nil(proofExists, "existence proof should be nil")
|
Loading…
Reference in New Issue