Merge pull request #210 from tendermint/feature/40-overhaul-proofs
Overhaul proofs and light-client / basecoin separation
This commit is contained in:
commit
c87174875a
16
app/store.go
16
app/store.go
|
@ -180,12 +180,18 @@ func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery)
|
|||
key := reqQuery.Data // Data holds the key bytes
|
||||
resQuery.Key = key
|
||||
if reqQuery.Prove {
|
||||
value, proof, exists := tree.Proof(key)
|
||||
if !exists {
|
||||
resQuery.Log = "Key not found"
|
||||
value, proofExists, proofNotExists, err := tree.GetWithProof(key)
|
||||
if err != nil {
|
||||
resQuery.Log = err.Error()
|
||||
break
|
||||
}
|
||||
|
||||
if value != nil {
|
||||
resQuery.Value = value
|
||||
resQuery.Proof = wire.BinaryBytes(proofExists)
|
||||
} else {
|
||||
resQuery.Proof = wire.BinaryBytes(proofNotExists)
|
||||
}
|
||||
resQuery.Value = value
|
||||
resQuery.Proof = proof
|
||||
} else {
|
||||
value := tree.Get(key)
|
||||
resQuery.Value = value
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
package proofs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
lc "github.com/tendermint/light-client"
|
||||
"github.com/tendermint/light-client/proofs"
|
||||
"github.com/tendermint/tendermint/rpc/client"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
)
|
||||
|
||||
// GetAndParseAppProof does most of the work of the query commands, but is quite
|
||||
// opinionated, so if you want more control, set up the items and call GetProof
|
||||
// directly. Notably, it always uses go-wire.ReadBinaryBytes to deserialize,
|
||||
// and Height and Node from standard flags.
|
||||
//
|
||||
// It will try to get the proof for the given key. If it is successful,
|
||||
// it will return the proof and also unserialize proof.Data into the data
|
||||
// argument (so pass in a pointer to the appropriate struct)
|
||||
func GetAndParseAppProof(key []byte, data interface{}) (lc.Proof, error) {
|
||||
height := GetHeight()
|
||||
node := commands.GetNode()
|
||||
|
||||
prover := proofs.NewAppProver(node)
|
||||
|
||||
proof, err := GetProof(node, prover, key, height)
|
||||
if err != nil {
|
||||
return proof, err
|
||||
}
|
||||
|
||||
err = wire.ReadBinaryBytes(proof.Data(), data)
|
||||
return proof, err
|
||||
}
|
||||
|
||||
// GetProof performs the get command directly from the proof (not from the CLI)
|
||||
func GetProof(node client.Client, prover lc.Prover, key []byte, height int) (proof lc.Proof, err error) {
|
||||
proof, err = prover.Get(key, uint64(height))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// short-circuit with no proofs
|
||||
if viper.GetBool(commands.FlagTrustNode) {
|
||||
return proof, err
|
||||
}
|
||||
|
||||
// here is the certifier, root of all knowledge
|
||||
cert, err := commands.GetCertifier()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get and validate a signed header for this proof
|
||||
|
||||
// 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...
|
||||
ph := int(proof.BlockHeight())
|
||||
client.WaitForHeight(node, ph, nil)
|
||||
commit, err := node.Commit(ph)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
check := lc.Checkpoint{
|
||||
Header: commit.Header,
|
||||
Commit: commit.Commit,
|
||||
}
|
||||
err = cert.Certify(check)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// validate the proof against the certified header to ensure data integrity
|
||||
err = proof.Validate(check)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return proof, err
|
||||
}
|
||||
|
||||
// ParseHexKey parses the key flag as hex and converts to bytes or returns error
|
||||
// argname is used to customize the error message
|
||||
func ParseHexKey(args []string, argname string) ([]byte, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, errors.Errorf("Missing required argument [%s]", argname)
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return nil, errors.Errorf("Only accepts one argument [%s]", argname)
|
||||
}
|
||||
rawkey := args[0]
|
||||
if rawkey == "" {
|
||||
return nil, errors.Errorf("[%s] argument must be non-empty ", argname)
|
||||
}
|
||||
// with tx, we always just parse key as hex and use to lookup
|
||||
return proofs.ParseHexKey(rawkey)
|
||||
}
|
||||
|
||||
func GetHeight() int {
|
||||
return viper.GetInt(FlagHeight)
|
||||
}
|
||||
|
||||
type proof struct {
|
||||
Height uint64 `json:"height"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// FoutputProof writes the output of wrapping height and info
|
||||
// in the form {"data": <the_data>, "height": <the_height>}
|
||||
// to the provider io.Writer
|
||||
func FoutputProof(w io.Writer, v interface{}, height uint64) error {
|
||||
wrap := &proof{height, v}
|
||||
blob, err := data.ToJSON(wrap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprintf(w, "%s\n", blob)
|
||||
return err
|
||||
}
|
||||
|
||||
// OutputProof prints the proof to stdout
|
||||
// reuse this for printing proofs and we should enhance this for text/json,
|
||||
// better presentation of height
|
||||
func OutputProof(data interface{}, height uint64) error {
|
||||
return FoutputProof(os.Stdout, data, height)
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package proofs
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/light-client/proofs"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
)
|
||||
|
||||
//nolint TODO add description
|
||||
var TxPresenters = proofs.NewPresenters()
|
||||
|
||||
// TxQueryCmd - CLI command to query a transaction with proof
|
||||
var TxQueryCmd = &cobra.Command{
|
||||
Use: "tx [txhash]",
|
||||
Short: "Handle proofs of commited txs",
|
||||
Long: `Proofs allows you to validate abci state with merkle proofs.
|
||||
|
||||
These proofs tie the data to a checkpoint, which is managed by "seeds".
|
||||
Here we can validate these proofs and import/export them to prove specific
|
||||
data to other peers as needed.
|
||||
`,
|
||||
RunE: commands.RequireInit(txQueryCmd),
|
||||
}
|
||||
|
||||
func txQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
// parse cli
|
||||
height := GetHeight()
|
||||
bkey, err := ParseHexKey(args, "txhash")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the proof -> this will be used by all prover commands
|
||||
node := commands.GetNode()
|
||||
prover := proofs.NewTxProver(node)
|
||||
proof, err := GetProof(node, prover, bkey, height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// auto-determine which tx it was, over all registered tx types
|
||||
info, err := TxPresenters.BruteForce(proof.Data())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// we can reuse this output for other commands for text/json
|
||||
// unless they do something special like store a file to disk
|
||||
return OutputProof(info, proof.BlockHeight())
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
lc "github.com/tendermint/light-client"
|
||||
"github.com/tendermint/light-client/proofs"
|
||||
"github.com/tendermint/merkleeyes/iavl"
|
||||
"github.com/tendermint/tendermint/rpc/client"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
)
|
||||
|
||||
// GetParsed does most of the work of the query commands, but is quite
|
||||
// opinionated, so if you want more control about parsing, call Get
|
||||
// directly.
|
||||
//
|
||||
// It will try to get the proof for the given key. If it is successful,
|
||||
// it will return the height and also unserialize proof.Data into the data
|
||||
// argument (so pass in a pointer to the appropriate struct)
|
||||
func GetParsed(key []byte, data interface{}, prove bool) (uint64, error) {
|
||||
bs, h, err := Get(key, prove)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = wire.ReadBinaryBytes(bs, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Get queries the given key and returns the value stored there and the
|
||||
// height we checked at.
|
||||
//
|
||||
// If prove is true (and why shouldn't it be?),
|
||||
// the data is fully verified before returning. If prove is false,
|
||||
// we just repeat whatever any (potentially malicious) node gives us.
|
||||
// Only use that if you are running the full node yourself,
|
||||
// and it is localhost or you have a secure connection (not HTTP)
|
||||
func Get(key []byte, prove bool) (data.Bytes, uint64, error) {
|
||||
if !prove {
|
||||
node := commands.GetNode()
|
||||
resp, err := node.ABCIQuery("/key", key, false)
|
||||
return data.Bytes(resp.Value), resp.Height, err
|
||||
}
|
||||
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) {
|
||||
node := commands.GetNode()
|
||||
|
||||
resp, err := node.ABCIQuery("/key", key, true)
|
||||
if err != nil {
|
||||
return nil, 0, 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())
|
||||
}
|
||||
// TODO: Handle null proofs
|
||||
if len(resp.Key) == 0 || len(resp.Value) == 0 || len(resp.Proof) == 0 {
|
||||
return nil, 0, nil, lc.ErrNoData()
|
||||
}
|
||||
if ph != 0 && ph != int(resp.Height) {
|
||||
return nil, 0, nil, lc.ErrHeightMismatch(ph, int(resp.Height))
|
||||
}
|
||||
|
||||
check, err := GetCertifiedCheckpoint(ph)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
|
||||
proof := new(iavl.KeyExistsProof)
|
||||
err = wire.ReadBinaryBytes(resp.Proof, &proof)
|
||||
if err != nil {
|
||||
return nil, 0, 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, err
|
||||
}
|
||||
|
||||
return data.Bytes(resp.Value), resp.Height, proof, nil
|
||||
}
|
||||
|
||||
// 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) (empty lc.Checkpoint, err error) {
|
||||
// here is the certifier, root of all trust
|
||||
node := commands.GetNode()
|
||||
cert, err := commands.GetCertifier()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get the checkpoint for this height
|
||||
|
||||
// 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
|
||||
// argname is used to customize the error message
|
||||
func ParseHexKey(args []string, argname string) ([]byte, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, errors.Errorf("Missing required argument [%s]", argname)
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return nil, errors.Errorf("Only accepts one argument [%s]", argname)
|
||||
}
|
||||
rawkey := args[0]
|
||||
if rawkey == "" {
|
||||
return nil, errors.Errorf("[%s] argument must be non-empty ", argname)
|
||||
}
|
||||
// with tx, we always just parse key as hex and use to lookup
|
||||
return proofs.ParseHexKey(rawkey)
|
||||
}
|
||||
|
||||
// GetHeight reads the viper config for the query height
|
||||
func GetHeight() int {
|
||||
return viper.GetInt(FlagHeight)
|
||||
}
|
||||
|
||||
type proof struct {
|
||||
Height uint64 `json:"height"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// FoutputProof writes the output of wrapping height and info
|
||||
// in the form {"data": <the_data>, "height": <the_height>}
|
||||
// to the provider io.Writer
|
||||
func FoutputProof(w io.Writer, v interface{}, height uint64) error {
|
||||
wrap := &proof{height, v}
|
||||
blob, err := data.ToJSON(wrap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprintf(w, "%s\n", blob)
|
||||
return err
|
||||
}
|
||||
|
||||
// OutputProof prints the proof to stdout
|
||||
// reuse this for printing proofs and we should enhance this for text/json,
|
||||
// better presentation of height
|
||||
func OutputProof(data interface{}, height uint64) error {
|
||||
return FoutputProof(os.Stdout, data, height)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package proofs
|
||||
package query
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
|
@ -1,10 +1,8 @@
|
|||
package proofs
|
||||
package query
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/go-wire/data"
|
||||
"github.com/tendermint/light-client/proofs"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
)
|
||||
|
@ -24,24 +22,15 @@ If you want json output, use an app-specific command that knows key and value st
|
|||
// parse the object, but rather return the raw bytes
|
||||
func keyQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
// parse cli
|
||||
height := GetHeight()
|
||||
key, err := ParseHexKey(args, "key")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
|
||||
// get the proof -> this will be used by all prover commands
|
||||
node := commands.GetNode()
|
||||
prover := proofs.NewAppProver(node)
|
||||
proof, err := GetProof(node, prover, key, height)
|
||||
val, h, err := Get(key, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// state just returns raw hex....
|
||||
info := data.Bytes(proof.Data())
|
||||
|
||||
// we can reuse this output for other commands for text/json
|
||||
// unless they do something special like store a file to disk
|
||||
return OutputProof(info, proof.BlockHeight())
|
||||
return OutputProof(val, h)
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
)
|
||||
|
||||
// TxQueryCmd - CLI command to query a transaction with proof
|
||||
var TxQueryCmd = &cobra.Command{
|
||||
Use: "tx [txhash]",
|
||||
Short: "Handle proofs of commited txs",
|
||||
Long: `Proofs allows you to validate abci state with merkle proofs.
|
||||
|
||||
These proofs tie the data to a checkpoint, which is managed by "seeds".
|
||||
Here we can validate these proofs and import/export them to prove specific
|
||||
data to other peers as needed.
|
||||
`,
|
||||
RunE: commands.RequireInit(txQueryCmd),
|
||||
}
|
||||
|
||||
func txQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
// parse cli
|
||||
// TODO: when querying historical heights is allowed... pass it
|
||||
// height := GetHeight()
|
||||
bkey, err := ParseHexKey(args, "txhash")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the proof -> this will be used by all prover commands
|
||||
node := commands.GetNode()
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
res, err := node.Tx(bkey, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// no checks if we don't get a proof
|
||||
if !prove {
|
||||
return showTx(res.Height, res.Tx)
|
||||
}
|
||||
|
||||
check, err := GetCertifiedCheckpoint(res.Height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = res.Proof.Validate(check.Header.DataHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// note that we return res.Proof.Data, not res.Tx,
|
||||
// as res.Proof.Validate only verifies res.Proof.Data
|
||||
return showTx(res.Height, res.Proof.Data)
|
||||
}
|
||||
|
||||
// showTx parses anything that was previously registered as basecoin.Tx
|
||||
func showTx(h int, tx types.Tx) error {
|
||||
var info basecoin.Tx
|
||||
err := wire.ReadBinaryBytes(tx, &info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return OutputProof(info, uint64(h))
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package txs
|
||||
|
||||
import (
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/light-client/proofs"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
)
|
||||
|
||||
// BaseTxPresenter this decodes all basecoin tx
|
||||
type BaseTxPresenter struct {
|
||||
proofs.RawPresenter // this handles MakeKey as hex bytes
|
||||
}
|
||||
|
||||
// ParseData - unmarshal raw bytes to a basecoin tx
|
||||
func (BaseTxPresenter) ParseData(raw []byte) (interface{}, error) {
|
||||
var tx basecoin.Tx
|
||||
err := wire.ReadBinaryBytes(raw, &tx)
|
||||
return tx, err
|
||||
}
|
|
@ -10,8 +10,8 @@ import (
|
|||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
"github.com/tendermint/basecoin/client/commands/auto"
|
||||
"github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands/proxy"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
rpccmd "github.com/tendermint/basecoin/client/commands/rpc"
|
||||
"github.com/tendermint/basecoin/client/commands/seeds"
|
||||
txcmd "github.com/tendermint/basecoin/client/commands/txs"
|
||||
|
@ -40,16 +40,15 @@ func main() {
|
|||
commands.AddBasicFlags(BaseCli)
|
||||
|
||||
// Prepare queries
|
||||
proofs.RootCmd.AddCommand(
|
||||
query.RootCmd.AddCommand(
|
||||
// These are default parsers, but optional in your app (you can remove key)
|
||||
proofs.TxQueryCmd,
|
||||
proofs.KeyQueryCmd,
|
||||
query.TxQueryCmd,
|
||||
query.KeyQueryCmd,
|
||||
coincmd.AccountQueryCmd,
|
||||
noncecmd.NonceQueryCmd,
|
||||
rolecmd.RoleQueryCmd,
|
||||
ibccmd.IBCQueryCmd,
|
||||
)
|
||||
proofs.TxPresenters.Register("base", txcmd.BaseTxPresenter{})
|
||||
|
||||
// set up the middleware
|
||||
txcmd.Middleware = txcmd.Wrappers{
|
||||
|
@ -81,7 +80,7 @@ func main() {
|
|||
keycmd.RootCmd,
|
||||
seeds.RootCmd,
|
||||
rpccmd.RootCmd,
|
||||
proofs.RootCmd,
|
||||
query.RootCmd,
|
||||
txcmd.RootCmd,
|
||||
proxy.RootCmd,
|
||||
commands.VersionCmd,
|
||||
|
|
|
@ -2,8 +2,10 @@ package commands
|
|||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
proofcmd "github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
|
||||
"github.com/tendermint/basecoin/docs/guide/counter/plugins/counter"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
|
@ -17,13 +19,14 @@ var CounterQueryCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func counterQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
key := stack.PrefixedKey(counter.NameCounter, counter.StateKey())
|
||||
|
||||
var cp counter.State
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &cp)
|
||||
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
key := stack.PrefixedKey(counter.NameCounter, counter.StateKey())
|
||||
h, err := query.GetParsed(key, &cp, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return proofcmd.OutputProof(cp, proof.BlockHeight())
|
||||
return query.OutputProof(cp, h)
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/tendermint/tmlibs/cli"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
"github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands/proxy"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
"github.com/tendermint/basecoin/client/commands/seeds"
|
||||
txcmd "github.com/tendermint/basecoin/client/commands/txs"
|
||||
bcount "github.com/tendermint/basecoin/docs/guide/counter/cmd/countercli/commands"
|
||||
|
@ -37,10 +37,10 @@ func main() {
|
|||
commands.AddBasicFlags(BaseCli)
|
||||
|
||||
// Prepare queries
|
||||
proofs.RootCmd.AddCommand(
|
||||
query.RootCmd.AddCommand(
|
||||
// These are default parsers, optional in your app
|
||||
proofs.TxQueryCmd,
|
||||
proofs.KeyQueryCmd,
|
||||
query.TxQueryCmd,
|
||||
query.KeyQueryCmd,
|
||||
coincmd.AccountQueryCmd,
|
||||
noncecmd.NonceQueryCmd,
|
||||
|
||||
|
@ -58,7 +58,6 @@ func main() {
|
|||
txcmd.Middleware.Register(txcmd.RootCmd.PersistentFlags())
|
||||
|
||||
// Prepare transactions
|
||||
proofs.TxPresenters.Register("base", txcmd.BaseTxPresenter{})
|
||||
txcmd.RootCmd.AddCommand(
|
||||
// This is the default transaction, optional in your app
|
||||
coincmd.SendTxCmd,
|
||||
|
@ -73,7 +72,7 @@ func main() {
|
|||
commands.ResetCmd,
|
||||
keycmd.RootCmd,
|
||||
seeds.RootCmd,
|
||||
proofs.RootCmd,
|
||||
query.RootCmd,
|
||||
txcmd.RootCmd,
|
||||
proxy.RootCmd,
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
hash: 2848c30b31fb205f846dd7dfca14ebed8a3249cbc5aaa759066b2bab3e4bbf42
|
||||
updated: 2017-07-27T16:46:31.962147949-04:00
|
||||
updated: 2017-08-04T15:38:45.048261895+02:00
|
||||
imports:
|
||||
- name: github.com/bgentry/speakeasy
|
||||
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
|
||||
|
@ -119,7 +119,7 @@ imports:
|
|||
- edwards25519
|
||||
- extra25519
|
||||
- name: github.com/tendermint/go-crypto
|
||||
version: bf355d1b58b27d4e98d8fb237eb14887b93a88f7
|
||||
version: 03d2b2446e8587f159e52070b1f1e0def971a2c7
|
||||
subpackages:
|
||||
- cmd
|
||||
- keys
|
||||
|
@ -142,7 +142,7 @@ imports:
|
|||
- certifiers/files
|
||||
- proofs
|
||||
- name: github.com/tendermint/merkleeyes
|
||||
version: 0310013053953eef80def3619aeb1e3a3254f452
|
||||
version: 44c4c64c731db5be4261ff3971b01b7e19729419
|
||||
subpackages:
|
||||
- client
|
||||
- iavl
|
||||
|
|
|
@ -3,11 +3,12 @@ package commands
|
|||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
lc "github.com/tendermint/light-client"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
proofcmd "github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
"github.com/tendermint/basecoin/modules/coin"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
)
|
||||
|
@ -32,12 +33,13 @@ func accountQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
key := stack.PrefixedKey(coin.NameCoin, act.Bytes())
|
||||
|
||||
acc := coin.Account{}
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &acc)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
height, err := query.GetParsed(key, &acc, prove)
|
||||
if lc.IsNoDataErr(err) {
|
||||
return errors.Errorf("Account bytes are empty for address %s ", addr)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return proofcmd.OutputProof(acc, proof.BlockHeight())
|
||||
return query.OutputProof(acc, height)
|
||||
}
|
||||
|
|
|
@ -6,10 +6,11 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
"github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
"github.com/tendermint/basecoin/modules/auth"
|
||||
"github.com/tendermint/basecoin/modules/base"
|
||||
"github.com/tendermint/basecoin/modules/coin"
|
||||
|
@ -37,8 +38,8 @@ type SendInput struct {
|
|||
// doQueryAccount is the HTTP handlerfunc to query an account
|
||||
// It expects a query string with
|
||||
func doQueryAccount(w http.ResponseWriter, r *http.Request) {
|
||||
query := mux.Vars(r)
|
||||
signature := query["signature"]
|
||||
args := mux.Vars(r)
|
||||
signature := args["signature"]
|
||||
actor, err := commands.ParseActor(signature)
|
||||
if err != nil {
|
||||
common.WriteError(w, err)
|
||||
|
@ -47,7 +48,8 @@ func doQueryAccount(w http.ResponseWriter, r *http.Request) {
|
|||
actor = coin.ChainAddr(actor)
|
||||
key := stack.PrefixedKey(coin.NameCoin, actor.Bytes())
|
||||
account := new(coin.Account)
|
||||
proof, err := proofs.GetAndParseAppProof(key, account)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
height, err := query.GetParsed(key, account, prove)
|
||||
if lightclient.IsNoDataErr(err) {
|
||||
err := fmt.Errorf("account bytes are empty for address: %q", signature)
|
||||
common.WriteError(w, err)
|
||||
|
@ -57,7 +59,7 @@ func doQueryAccount(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := proofs.FoutputProof(w, account, proof.BlockHeight()); err != nil {
|
||||
if err := query.FoutputProof(w, account, height); err != nil {
|
||||
common.WriteError(w, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
proofcmd "github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
"github.com/tendermint/basecoin/modules/ibc"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
"github.com/tendermint/light-client/proofs"
|
||||
"github.com/tendermint/merkleeyes/iavl"
|
||||
)
|
||||
|
||||
// TODO: query seeds (register/update)
|
||||
|
@ -86,17 +85,19 @@ func init() {
|
|||
func ibcQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
var res ibc.HandlerInfo
|
||||
key := stack.PrefixedKey(ibc.NameIBC, ibc.HandlerKey())
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &res)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
h, err := query.GetParsed(key, &res, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return proofcmd.OutputProof(res, proof.BlockHeight())
|
||||
return query.OutputProof(res, h)
|
||||
}
|
||||
|
||||
func chainsQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
list := [][]byte{}
|
||||
key := stack.PrefixedKey(ibc.NameIBC, ibc.ChainsKey())
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &list)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
h, err := query.GetParsed(key, &list, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -107,7 +108,7 @@ func chainsQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
res[i] = string(list[i])
|
||||
}
|
||||
|
||||
return proofcmd.OutputProof(res, proof.BlockHeight())
|
||||
return query.OutputProof(res, h)
|
||||
}
|
||||
|
||||
func chainQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
|
@ -118,12 +119,13 @@ func chainQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
var res ibc.ChainInfo
|
||||
key := stack.PrefixedKey(ibc.NameIBC, ibc.ChainKey(arg))
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &res)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
h, err := query.GetParsed(key, &res, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return proofcmd.OutputProof(res, proof.BlockHeight())
|
||||
return query.OutputProof(res, h)
|
||||
}
|
||||
|
||||
func assertOne(from, to string) error {
|
||||
|
@ -154,12 +156,13 @@ func packetsQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
var res uint64
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &res)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
h, err := query.GetParsed(key, &res, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return proofcmd.OutputProof(res, proof.BlockHeight())
|
||||
return query.OutputProof(res, h)
|
||||
}
|
||||
|
||||
func packetQueryCmd(cmd *cobra.Command, args []string) error {
|
||||
|
@ -174,6 +177,7 @@ func packetQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
if seq < 0 {
|
||||
return errors.Errorf("--%s must be a non-negative number", FlagSequence)
|
||||
}
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
|
||||
var key []byte
|
||||
if from != "" {
|
||||
|
@ -183,26 +187,21 @@ func packetQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
// Input queue just display the results
|
||||
var packet ibc.Packet
|
||||
if from != "" {
|
||||
var packet ibc.Packet
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &packet)
|
||||
h, err := query.GetParsed(key, &packet, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return proofcmd.OutputProof(packet, proof.BlockHeight())
|
||||
return query.OutputProof(packet, h)
|
||||
}
|
||||
|
||||
// output queue, create a post packet
|
||||
var packet ibc.Packet
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &packet)
|
||||
bs, height, proof, err := query.GetWithProof(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: oh so ugly. fix before merge!
|
||||
// wait, i want to change go-merkle too....
|
||||
appProof := proof.(proofs.AppProof)
|
||||
extractedProof, err := iavl.ReadProof(appProof.Proof)
|
||||
err = wire.ReadBinaryBytes(bs, &packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -210,10 +209,10 @@ func packetQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
// create the post packet here.
|
||||
post := ibc.PostPacketTx{
|
||||
FromChainID: commands.GetChainID(),
|
||||
FromChainHeight: proof.BlockHeight(),
|
||||
FromChainHeight: height,
|
||||
Key: key,
|
||||
Packet: packet,
|
||||
Proof: extractedProof,
|
||||
Proof: proof,
|
||||
}
|
||||
|
||||
// print json direct, as we don't need to wrap with the height
|
||||
|
|
|
@ -91,8 +91,11 @@ func IsPacketOutOfOrderErr(err error) bool {
|
|||
func ErrInvalidProof() error {
|
||||
return errors.WithCode(errInvalidProof, IBCCodeInvalidProof)
|
||||
}
|
||||
func ErrInvalidProofWithReason(err error) error {
|
||||
return errors.WithCode(err, IBCCodeInvalidProof)
|
||||
}
|
||||
func IsInvalidProofErr(err error) bool {
|
||||
return errors.IsSameError(errInvalidProof, err)
|
||||
return errors.HasErrorCode(err, IBCCodeInvalidProof)
|
||||
}
|
||||
|
||||
func ErrInvalidCommit(err error) error {
|
||||
|
|
|
@ -102,9 +102,9 @@ func (m Middleware) verifyPost(ctx basecoin.Context, store state.SimpleDB,
|
|||
// verify the merkle hash....
|
||||
root := seed.Header.AppHash
|
||||
pBytes := packet.Bytes()
|
||||
valid := tx.Proof.Verify(tx.Key, pBytes, root)
|
||||
if !valid {
|
||||
return ictx, itx, ErrInvalidProof()
|
||||
err = tx.Proof.Verify(tx.Key, pBytes, root)
|
||||
if err != nil {
|
||||
return ictx, itx, ErrInvalidProofWithReason(err)
|
||||
}
|
||||
|
||||
// add to input queue
|
||||
|
|
|
@ -59,7 +59,10 @@ func genEmptySeed(keys certifiers.ValKeys, chain string, h int,
|
|||
func makePostPacket(tree *iavl.IAVLTree, packet Packet, fromID string, fromHeight int) PostPacketTx {
|
||||
key := []byte(fmt.Sprintf("some-long-prefix-%06d", packet.Sequence))
|
||||
tree.Set(key, packet.Bytes())
|
||||
_, proof := tree.ConstructProof(key)
|
||||
_, proof, _, err := tree.GetWithProof(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if proof == nil {
|
||||
panic("wtf?")
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package ibc
|
|||
import (
|
||||
"github.com/tendermint/go-wire/data"
|
||||
"github.com/tendermint/light-client/certifiers"
|
||||
merkle "github.com/tendermint/merkleeyes/iavl"
|
||||
"github.com/tendermint/merkleeyes/iavl"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
)
|
||||
|
@ -115,9 +115,9 @@ type PostPacketTx struct {
|
|||
// The block height in which Packet was committed, to check Proof
|
||||
FromChainHeight uint64 `json:"src_height"`
|
||||
// this proof must match the header and the packet.Bytes()
|
||||
Proof *merkle.IAVLProof `json:"proof"`
|
||||
Key data.Bytes `json:"key"`
|
||||
Packet Packet `json:"packet"`
|
||||
Proof *iavl.KeyExistsProof `json:"proof"`
|
||||
Key data.Bytes `json:"key"`
|
||||
Packet Packet `json:"packet"`
|
||||
}
|
||||
|
||||
// ValidateBasic makes sure this is consistent - used to satisfy TxInner
|
||||
|
|
|
@ -5,12 +5,13 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
lc "github.com/tendermint/light-client"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
proofcmd "github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
"github.com/tendermint/basecoin/modules/nonce"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
)
|
||||
|
@ -33,23 +34,21 @@ func nonceQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
seq, proof, err := doNonceQuery(signers)
|
||||
seq, height, err := doNonceQuery(signers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return proofcmd.OutputProof(seq, proof.BlockHeight())
|
||||
return query.OutputProof(seq, height)
|
||||
}
|
||||
|
||||
func doNonceQuery(signers []basecoin.Actor) (sequence uint32, proof lc.Proof, err error) {
|
||||
|
||||
func doNonceQuery(signers []basecoin.Actor) (sequence uint32, height uint64, err error) {
|
||||
key := stack.PrefixedKey(nonce.NameNonce, nonce.GetSeqKey(signers))
|
||||
|
||||
proof, err = proofcmd.GetAndParseAppProof(key, &sequence)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
height, err = query.GetParsed(key, &sequence, prove)
|
||||
if lc.IsNoDataErr(err) {
|
||||
// no data, return sequence 0
|
||||
return 0, proof, nil
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@ package commands
|
|||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
proofcmd "github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/client/commands/query"
|
||||
"github.com/tendermint/basecoin/modules/roles"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
)
|
||||
|
@ -28,10 +29,11 @@ func roleQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
var res roles.Role
|
||||
key := stack.PrefixedKey(roles.NameRole, role)
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &res)
|
||||
prove := !viper.GetBool(commands.FlagTrustNode)
|
||||
height, err := query.GetParsed(key, &res, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return proofcmd.OutputProof(res, proof.BlockHeight())
|
||||
return query.OutputProof(res, height)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package state
|
|||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/tendermint/tmlibs/merkle"
|
||||
"github.com/tendermint/merkleeyes/iavl"
|
||||
)
|
||||
|
||||
// store nonce as it's own type so no one can even try to fake it
|
||||
|
@ -12,13 +12,13 @@ type nonce int64
|
|||
// Bonsai is a deformed tree forced to fit in a small pot
|
||||
type Bonsai struct {
|
||||
id nonce
|
||||
Tree merkle.Tree
|
||||
Tree *iavl.IAVLTree
|
||||
}
|
||||
|
||||
var _ SimpleDB = &Bonsai{}
|
||||
|
||||
// NewBonsai wraps a merkle tree and tags it to track children
|
||||
func NewBonsai(tree merkle.Tree) *Bonsai {
|
||||
func NewBonsai(tree *iavl.IAVLTree) *Bonsai {
|
||||
return &Bonsai{
|
||||
id: nonce(rand.Int63()),
|
||||
Tree: tree,
|
||||
|
@ -46,8 +46,8 @@ func (b *Bonsai) Remove(key []byte) (value []byte) {
|
|||
return
|
||||
}
|
||||
|
||||
func (b *Bonsai) Proof(key []byte) (value []byte, proof []byte, exists bool) {
|
||||
return b.Tree.Proof(key)
|
||||
func (b *Bonsai) GetWithProof(key []byte) ([]byte, *iavl.KeyExistsProof, *iavl.KeyNotExistsProof, error) {
|
||||
return b.Tree.GetWithProof(key)
|
||||
}
|
||||
|
||||
func (b *Bonsai) List(start, end []byte, limit int) []Model {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"github.com/tendermint/merkleeyes/iavl"
|
||||
"github.com/tendermint/tmlibs/merkle"
|
||||
)
|
||||
import "github.com/tendermint/merkleeyes/iavl"
|
||||
|
||||
// State represents the app states, separating the commited state (for queries)
|
||||
// from the working state (for CheckTx and AppendTx)
|
||||
|
@ -14,7 +11,7 @@ type State struct {
|
|||
persistent bool
|
||||
}
|
||||
|
||||
func NewState(tree merkle.Tree, persistent bool) State {
|
||||
func NewState(tree *iavl.IAVLTree, persistent bool) State {
|
||||
base := NewBonsai(tree)
|
||||
return State{
|
||||
committed: base,
|
||||
|
@ -59,10 +56,7 @@ func (s *State) Hash() ([]byte, error) {
|
|||
func (s *State) BatchSet(key, value []byte) {
|
||||
if s.persistent {
|
||||
// This is in the batch with the Save, but not in the tree
|
||||
tree, ok := s.committed.Tree.(*iavl.IAVLTree)
|
||||
if ok {
|
||||
tree.BatchSet(key, value)
|
||||
}
|
||||
s.committed.Tree.BatchSet(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue