119 lines
3.2 KiB
Go
119 lines
3.2 KiB
Go
package proofs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"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
|
|
}
|
|
ph := int(proof.BlockHeight())
|
|
// 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...
|
|
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(heightFlag)
|
|
}
|
|
|
|
type proof struct {
|
|
Height uint64 `json:"height"`
|
|
Data interface{} `json:"data"`
|
|
}
|
|
|
|
// 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(info interface{}, height uint64) error {
|
|
wrap := proof{height, info}
|
|
res, err := data.ToJSON(wrap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(res))
|
|
return nil
|
|
}
|