Merge pull request #248 from cosmos/feature/separate-out-viper

Separate out viper
This commit is contained in:
Ethan Frey 2017-09-08 21:01:48 +02:00 committed by GitHub
commit 7c4cc3785e
48 changed files with 1157 additions and 353 deletions

1
.gitignore vendored
View File

@ -4,4 +4,3 @@ vendor
merkleeyes.db
build
docs/guide/*.sh
keys/

View File

@ -3,7 +3,7 @@ GOTOOLS = github.com/mitchellh/gox \
github.com/rigelrozanski/shelldown/cmd/shelldown
TUTORIALS=$(shell find docs/guide -name "*md" -type f)
EXAMPLES := counter eyes basecoin
EXAMPLES := counter eyes basecoin
INSTALL_EXAMPLES := $(addprefix install_,${EXAMPLES})
TEST_EXAMPLES := $(addprefix testex_,${EXAMPLES})
@ -37,7 +37,6 @@ test_unit:
@go test `glide novendor`
test_cli: $(TEST_EXAMPLES)
./tests/cli/init-server.sh
# sudo apt-get install jq
# wget "https://raw.githubusercontent.com/kward/shunit2/master/source/2.1/src/shunit2"

View File

@ -10,7 +10,7 @@ import (
"github.com/pkg/errors"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-wire"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -180,18 +180,13 @@ 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, proofExists, proofNotExists, err := tree.GetWithProof(key)
value, proof, 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.Bytes()
} else {
value := tree.Get(key)
resQuery.Value = value

View File

@ -13,14 +13,13 @@ import (
"github.com/spf13/viper"
"github.com/tendermint/light-client/certifiers"
"github.com/tendermint/light-client/certifiers/client"
"github.com/tendermint/light-client/certifiers/files"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
rpcclient "github.com/tendermint/tendermint/rpc/client"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/modules/auth"
)
@ -47,41 +46,40 @@ func GetChainID() string {
// GetNode prepares a simple rpc.Client from the flags
func GetNode() rpcclient.Client {
return rpcclient.NewHTTP(viper.GetString(NodeFlag), "/websocket")
return client.GetNode(viper.GetString(NodeFlag))
}
// GetSourceProvider returns a provider pointing to an rpc handler
func GetSourceProvider() certifiers.Provider {
if sourceProv == nil {
node := viper.GetString(NodeFlag)
sourceProv = client.GetRPCProvider(node)
}
return sourceProv
}
// GetTrustedProvider returns a reference to a local store with cache
func GetTrustedProvider() certifiers.Provider {
if trustedProv == nil {
rootDir := viper.GetString(cli.HomeFlag)
trustedProv = client.GetLocalProvider(rootDir)
}
return trustedProv
}
// GetProviders creates a trusted (local) seed provider and a remote
// provider based on configuration.
func GetProviders() (trusted certifiers.Provider, source certifiers.Provider) {
if trustedProv == nil || sourceProv == nil {
// initialize provider with files stored in homedir
rootDir := viper.GetString(cli.HomeFlag)
trustedProv = certifiers.NewCacheProvider(
certifiers.NewMemStoreProvider(),
files.NewProvider(rootDir),
)
node := viper.GetString(NodeFlag)
sourceProv = client.NewHTTP(node)
}
return trustedProv, sourceProv
return GetTrustedProvider(), GetSourceProvider()
}
// GetCertifier constructs a dynamic certifier from the config info
func GetCertifier() (*certifiers.InquiringCertifier, error) {
// load up the latest store....
trust, source := GetProviders()
// this gets the most recent verified seed
seed, err := certifiers.LatestSeed(trust)
if certifiers.IsSeedNotFoundErr(err) {
return nil, errors.New("Please run init first to establish a root of trust")
}
if err != nil {
return nil, err
}
cert := certifiers.NewInquiring(
viper.GetString(ChainFlag), seed, trust, source)
return cert, nil
trust := GetTrustedProvider()
source := GetSourceProvider()
chainID := GetChainID()
return client.GetCertifier(chainID, trust, source)
}
// ParseActor parses an address of form:

View File

@ -0,0 +1,117 @@
# Keys CLI
This is as much an example how to expose cobra/viper, as for a cli itself
(I think this code is overkill for what go-keys needs). But please look at
the commands, and give feedback and changes.
`RootCmd` calls some initialization functions (`cobra.OnInitialize` and `RootCmd.PersistentPreRunE`) which serve to connect environmental variables and cobra flags, as well as load the config file. It also validates the flags registered on root and creates the cryptomanager, which will be used by all subcommands.
## Help info
```
# keys help
Keys allows you to manage your local keystore for tendermint.
These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that
needs to sign with a private key.
Usage:
keys [command]
Available Commands:
get Get details of one key
list List all keys
new Create a new public/private key pair
serve Run the key manager as an http server
update Change the password for a private key
Flags:
--keydir string Directory to store private keys (subdir of root) (default "keys")
-o, --output string Output format (text|json) (default "text")
-r, --root string root directory for config and data (default "/Users/ethan/.tlc")
Use "keys [command] --help" for more information about a command.
```
## Getting the config file
The first step is to load in root, by checking the following in order:
* -r, --root command line flag
* TM_ROOT environmental variable
* default ($HOME/.tlc evaluated at runtime)
Once the `rootDir` is established, the script looks for a config file named `keys.{json,toml,yaml,hcl}` in that directory and parses it. These values will provide defaults for flags of the same name.
There is an example config file for testing out locally, which writes keys to `./.mykeys`. You can
## Getting/Setting variables
When we want to get the value of a user-defined variable (eg. `output`), we can call `viper.GetString("output")`, which will do the following checks, until it finds a match:
* Is `--output` command line flag present?
* Is `TM_OUTPUT` environmental variable set?
* Was a config file found and does it have an `output` variable?
* Is there a default set on the command line flag?
If no variable is set and there was no default, we get back "".
This setup allows us to have powerful command line flags, but use env variables or config files (local or 12-factor style) to avoid passing these arguments every time.
## Nesting structures
Sometimes we don't just need key-value pairs, but actually a multi-level config file, like
```
[mail]
from = "no-reply@example.com"
server = "mail.example.com"
port = 567
password = "XXXXXX"
```
This CLI is too simple to warant such a structure, but I think eg. tendermint could benefit from such an approach. Here are some pointers:
* [Accessing nested keys from config files](https://github.com/spf13/viper#accessing-nested-keys)
* [Overriding nested values with envvars](https://www.netlify.com/blog/2016/09/06/creating-a-microservice-boilerplate-in-go/#nested-config-values) - the mentioned outstanding PR is already merged into master!
* Overriding nested values with cli flags? (use `--log_config.level=info` ??)
I'd love to see an example of this fully worked out in a more complex CLI.
## Have your cake and eat it too
It's easy to render data different ways. Some better for viewing, some better for importing to other programs. You can just add some global (persistent) flags to control the output formatting, and everyone gets what they want.
```
# keys list -e hex
All keys:
betty d0789984492b1674e276b590d56b7ae077f81adc
john b77f4720b220d1411a649b6c7f1151eb6b1c226a
# keys list -e btc
All keys:
betty 3uTF4r29CbtnzsNHZoPSYsE4BDwH
john 3ZGp2Md35iw4XVtRvZDUaAEkCUZP
# keys list -e b64 -o json
[
{
"name": "betty",
"address": "0HiZhEkrFnTidrWQ1Wt64Hf4Gtw=",
"pubkey": {
"type": "secp256k1",
"data": "F83WvhT0KwttSoqQqd_0_r2ztUUaQix5EXdO8AZyREoV31Og780NW59HsqTAb2O4hZ-w-j0Z-4b2IjfdqqfhVQ=="
}
},
{
"name": "john",
"address": "t39HILIg0UEaZJtsfxFR62scImo=",
"pubkey": {
"type": "ed25519",
"data": "t1LFmbg_8UTwj-n1wkqmnTp6NfaOivokEhlYySlGYCY="
}
}
]
```

View File

@ -0,0 +1,49 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// deleteCmd represents the delete command
var deleteCmd = &cobra.Command{
Use: "delete [name]",
Short: "DANGER: Delete a private key from your system",
RunE: runDeleteCmd,
}
func runDeleteCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
oldpass, err := getPassword("DANGER - enter password to permanently delete key:")
if err != nil {
return err
}
err = GetKeyManager().Delete(name, oldpass)
if err != nil {
return err
}
fmt.Println("Password deleted forever (uh oh!)")
return nil
}

View File

@ -0,0 +1,42 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// getCmd represents the get command
var getCmd = &cobra.Command{
Use: "get [name]",
Short: "Get details of one key",
Long: `Return public details of one local key.`,
RunE: runGetCmd,
}
func runGetCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
info, err := GetKeyManager().Get(name)
if err == nil {
printInfo(info)
}
return err
}

View File

@ -0,0 +1,34 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import "github.com/spf13/cobra"
// listCmd represents the list command
var listCmd = &cobra.Command{
Use: "list",
Short: "List all keys",
Long: `Return a list of all public keys stored by this key manager
along with their associated name and address.`,
RunE: runListCmd,
}
func runListCmd(cmd *cobra.Command, args []string) error {
infos, err := GetKeyManager().List()
if err == nil {
printInfos(infos)
}
return err
}

View File

@ -0,0 +1,94 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"fmt"
"github.com/pkg/errors"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
flagType = "type"
flagNoBackup = "no-backup"
)
// newCmd represents the new command
var newCmd = &cobra.Command{
Use: "new [name]",
Short: "Create a new public/private key pair",
Long: `Add a public/private key pair to the key store.
The password muts be entered in the terminal and not
passed as a command line argument for security.`,
RunE: runNewCmd,
}
func init() {
newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1)")
newCmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
}
func runNewCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
algo := viper.GetString(flagType)
pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:")
if err != nil {
return err
}
info, seed, err := GetKeyManager().Create(name, pass, algo)
if err == nil {
printCreate(info, seed)
}
return err
}
type NewOutput struct {
Key keys.Info `json:"key"`
Seed string `json:"seed"`
}
func printCreate(info keys.Info, seed string) {
switch viper.Get(cli.OutputFlag) {
case "text":
printInfo(info)
// print seed unless requested not to.
if !viper.GetBool(flagNoBackup) {
fmt.Println("**Important** write this seed phrase in a safe place.")
fmt.Println("It is the only way to recover your account if you ever forget your password.\n")
fmt.Println(seed)
}
case "json":
out := NewOutput{Key: info}
if !viper.GetBool(flagNoBackup) {
out.Seed = seed
}
json, err := data.ToJSON(out)
if err != nil {
panic(err) // really shouldn't happen...
}
fmt.Println(string(json))
}
}

View File

@ -0,0 +1,61 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// recoverCmd represents the recover command
var recoverCmd = &cobra.Command{
Use: "recover [name]",
Short: "Recover a private key from a seed phrase",
Long: `Recover a private key from a seed phrase.
I really hope you wrote this down when you created the new key.
The seed is only displayed on creation, never again.
You can also use this to copy a key between multiple testnets,
simply by "recovering" the key in the other nets you want to copy
to. Of course, it has no coins on the other nets, just the same address.`,
RunE: runRecoverCmd,
}
func runRecoverCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
pass, err := getPassword("Enter the new passphrase:")
if err != nil {
return err
}
// not really a password... huh?
seed, err := getSeed("Enter your recovery seed phrase:")
if err != nil {
return err
}
info, err := GetKeyManager().Recover(name, pass, seed)
if err != nil {
return err
}
printInfo(info)
return nil
}

View File

@ -0,0 +1,44 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"github.com/spf13/cobra"
keys "github.com/tendermint/go-crypto/keys"
)
var (
manager keys.Manager
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "keys",
Short: "Key manager for tendermint clients",
Long: `Keys allows you to manage your local keystore for tendermint.
These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that
needs to sign with a private key.`,
}
func init() {
RootCmd.AddCommand(getCmd)
RootCmd.AddCommand(listCmd)
RootCmd.AddCommand(newCmd)
RootCmd.AddCommand(updateCmd)
RootCmd.AddCommand(deleteCmd)
RootCmd.AddCommand(recoverCmd)
}

View File

@ -0,0 +1,53 @@
// Copyright © 2017 Ethan Frey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keys
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// updateCmd represents the update command
var updateCmd = &cobra.Command{
Use: "update [name]",
Short: "Change the password for a private key",
RunE: runUpdateCmd,
}
func runUpdateCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
oldpass, err := getPassword("Enter the current passphrase:")
if err != nil {
return err
}
newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:")
if err != nil {
return err
}
err = GetKeyManager().Update(name, oldpass, newpass)
if err != nil {
return err
}
fmt.Println("Password successfully updated!")
return nil
}

View File

@ -0,0 +1,131 @@
package keys
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/bgentry/speakeasy"
isatty "github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/spf13/viper"
keys "github.com/tendermint/go-crypto/keys"
data "github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client"
)
const MinPassLength = 10
// GetKeyManager initializes a key manager based on the configuration
func GetKeyManager() keys.Manager {
if manager == nil {
rootDir := viper.GetString(cli.HomeFlag)
manager = client.GetKeyManager(rootDir)
}
return manager
}
// if we read from non-tty, we just need to init the buffer reader once,
// in case we try to read multiple passwords (eg. update)
var buf *bufio.Reader
func inputIsTty() bool {
return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
}
func stdinPassword() (string, error) {
if buf == nil {
buf = bufio.NewReader(os.Stdin)
}
pass, err := buf.ReadString('\n')
if err != nil {
return "", err
}
return strings.TrimSpace(pass), nil
}
func getPassword(prompt string) (pass string, err error) {
if inputIsTty() {
pass, err = speakeasy.Ask(prompt)
} else {
pass, err = stdinPassword()
}
if err != nil {
return "", err
}
if len(pass) < MinPassLength {
return "", errors.Errorf("Password must be at least %d characters", MinPassLength)
}
return pass, nil
}
func getSeed(prompt string) (seed string, err error) {
if inputIsTty() {
fmt.Println(prompt)
}
seed, err = stdinPassword()
seed = strings.TrimSpace(seed)
return
}
func getCheckPassword(prompt, prompt2 string) (string, error) {
// simple read on no-tty
if !inputIsTty() {
return getPassword(prompt)
}
// TODO: own function???
pass, err := getPassword(prompt)
if err != nil {
return "", err
}
pass2, err := getPassword(prompt2)
if err != nil {
return "", err
}
if pass != pass2 {
return "", errors.New("Passphrases don't match")
}
return pass, nil
}
func printInfo(info keys.Info) {
switch viper.Get(cli.OutputFlag) {
case "text":
addr, err := data.ToText(info.Address)
if err != nil {
panic(err) // really shouldn't happen...
}
sep := "\t\t"
if len(info.Name) > 7 {
sep = "\t"
}
fmt.Printf("%s%s%s\n", info.Name, sep, addr)
case "json":
json, err := data.ToJSON(info)
if err != nil {
panic(err) // really shouldn't happen...
}
fmt.Println(string(json))
}
}
func printInfos(infos keys.Infos) {
switch viper.Get(cli.OutputFlag) {
case "text":
fmt.Println("All keys:")
for _, i := range infos {
printInfo(i)
}
case "json":
json, err := data.ToJSON(infos)
if err != nil {
panic(err) // really shouldn't happen...
}
fmt.Println(string(json))
}
}

View File

@ -1,19 +1,15 @@
package proxy
import (
"net/http"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
certclient "github.com/tendermint/light-client/certifiers/client"
"github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/core"
rpc "github.com/tendermint/tendermint/rpc/lib/server"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
)
@ -32,8 +28,7 @@ just with added trust and running locally.`,
}
const (
bindFlag = "serve"
wsEndpoint = "/websocket"
bindFlag = "serve"
)
func init() {
@ -50,24 +45,15 @@ func init() {
func runProxy(cmd *cobra.Command, args []string) error {
// First, connect a client
c := commands.GetNode()
node := commands.GetNode()
bind := viper.GetString(bindFlag)
cert, err := commands.GetCertifier()
if err != nil {
return err
}
sc := certclient.Wrap(c, cert)
sc.Start()
r := routes(sc)
sc := client.SecureClient(node, cert)
// build the handler...
mux := http.NewServeMux()
rpc.RegisterRPCFuncs(mux, r, logger)
wm := rpc.NewWebsocketManager(r, c)
wm.SetLogger(logger)
core.SetLogger(logger)
mux.HandleFunc(wsEndpoint, wm.WebsocketHandler)
_, err = rpc.StartHTTPServer(viper.GetString(bindFlag), mux, logger)
err = client.StartProxy(sc, bind, logger)
if err != nil {
return err
}
@ -78,33 +64,3 @@ func runProxy(cmd *cobra.Command, args []string) error {
return nil
}
// First step, proxy with no checks....
func routes(c client.Client) map[string]*rpc.RPCFunc {
return map[string]*rpc.RPCFunc{
// Subscribe/unsubscribe are reserved for websocket events.
// We can just use the core tendermint impl, which uses the
// EventSwitch we registered in NewWebsocketManager above
"subscribe": rpc.NewWSRPCFunc(core.Subscribe, "event"),
"unsubscribe": rpc.NewWSRPCFunc(core.Unsubscribe, "event"),
// info API
"status": rpc.NewRPCFunc(c.Status, ""),
"blockchain": rpc.NewRPCFunc(c.BlockchainInfo, "minHeight,maxHeight"),
"genesis": rpc.NewRPCFunc(c.Genesis, ""),
"block": rpc.NewRPCFunc(c.Block, "height"),
"commit": rpc.NewRPCFunc(c.Commit, "height"),
"tx": rpc.NewRPCFunc(c.Tx, "hash,prove"),
"validators": rpc.NewRPCFunc(c.Validators, ""),
// broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(c.BroadcastTxCommit, "tx"),
"broadcast_tx_sync": rpc.NewRPCFunc(c.BroadcastTxSync, "tx"),
"broadcast_tx_async": rpc.NewRPCFunc(c.BroadcastTxAsync, "tx"),
// abci API
"abci_query": rpc.NewRPCFunc(c.ABCIQuery, "path,data,prove"),
"abci_info": rpc.NewRPCFunc(c.ABCIInfo, ""),
}
}

View File

@ -10,12 +10,10 @@ import (
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/iavl"
"github.com/tendermint/light-client/proofs"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/tendermint/rpc/client"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
)
@ -52,113 +50,20 @@ 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, *iavl.KeyNotExistsProof, error) {
func GetWithProof(key []byte) (data.Bytes, uint64, iavl.KeyProof, error) {
node := commands.GetNode()
cert, err := commands.GetCertifier()
if err != nil {
return nil, 0, nil, nil, err
return nil, 0, nil, err
}
return 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
return client.GetWithProof(key, node, cert)
}
// ParseHexKey parses the key flag as hex and converts to bytes or returns error

View File

@ -8,6 +8,7 @@ import (
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
)
@ -51,7 +52,7 @@ func txQueryCmd(cmd *cobra.Command, args []string) error {
return err
}
check, err := getCertifiedCheckpoint(res.Height, node, cert)
check, err := client.GetCertifiedCheckpoint(res.Height, node, cert)
if err != nil {
return err
}

View File

@ -59,7 +59,7 @@ var validatorsCmd = &cobra.Command{
func runValidators(cmd *cobra.Command, args []string) error {
c := commands.GetNode()
validators, err := c.Validators()
validators, err := c.Validators(nil)
if err != nil {
return err
}

View File

@ -6,9 +6,9 @@ import (
"github.com/spf13/cobra"
"github.com/tendermint/go-wire/data"
certclient "github.com/tendermint/light-client/certifiers/client"
"github.com/tendermint/tendermint/rpc/client"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/cosmos/cosmos-sdk/client"
"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
c := commands.GetNode()
cert, err := commands.GetCertifier()
if err != nil {
return nil, err
}
sc := certclient.Wrap(c, cert)
return sc, nil
return client.SecureClient(c, cert), nil
}
// printResult just writes the struct to the console, returns an error if it can't

View File

@ -27,7 +27,7 @@ func runBlock(cmd *cobra.Command, args []string) error {
}
h := viper.GetInt(FlagHeight)
block, err := c.Block(h)
block, err := c.Block(&h)
if err != nil {
return err
}
@ -47,7 +47,7 @@ func runCommit(cmd *cobra.Command, args []string) error {
}
h := viper.GetInt(FlagHeight)
commit, err := c.Commit(h)
commit, err := c.Commit(&h)
if err != nil {
return err
}

View File

@ -1,15 +1,11 @@
package seeds
import (
"encoding/json"
"os"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/tendermint/light-client/certifiers"
)
var exportCmd = &cobra.Command{
@ -44,16 +40,5 @@ func exportSeed(cmd *cobra.Command, args []string) error {
}
// now get the output file and write it
return writeSeed(seed, path)
}
func writeSeed(seed certifiers.Seed, path string) (err error) {
f, err := os.Create(path)
if err != nil {
return errors.WithStack(err)
}
defer f.Close()
stream := json.NewEncoder(f)
err = stream.Encode(seed)
return errors.WithStack(err)
return seed.WriteJSON(path)
}

View File

@ -42,7 +42,7 @@ func importSeed(cmd *cobra.Command, args []string) error {
// parse the input file
path := args[0]
seed, err := certifiers.LoadSeed(path)
seed, err := certifiers.LoadSeedJSON(path)
if err != nil {
return err
}

View File

@ -47,7 +47,7 @@ func loadSeed(p certifiers.Provider, h int, hash, file string) (seed certifiers.
seed, err = p.GetByHash(vhash)
}
} else if file != "" {
seed, err = certifiers.LoadSeed(file)
seed, err = certifiers.LoadSeedJSON(file)
} else {
// default is latest seed
seed, err = certifiers.LatestSeed(p)

View File

@ -10,12 +10,11 @@ import (
"strings"
"github.com/bgentry/speakeasy"
"github.com/mattn/go-isatty"
isatty "github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
keycmd "github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/go-crypto/keys"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
@ -24,6 +23,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client/commands"
keycmd "github.com/cosmos/cosmos-sdk/client/commands/keys"
"github.com/cosmos/cosmos-sdk/modules/auth"
)

54
client/common.go Normal file
View File

@ -0,0 +1,54 @@
package client
import (
"errors"
"github.com/tendermint/light-client/certifiers"
certclient "github.com/tendermint/light-client/certifiers/client"
"github.com/tendermint/light-client/certifiers/files"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)
// GetNode prepares a simple rpc.Client for the given endpoint
func GetNode(url string) rpcclient.Client {
return rpcclient.NewHTTP(url, "/websocket")
}
// GetRPCProvider retuns a certifier compatible data source using
// tendermint RPC
func GetRPCProvider(url string) certifiers.Provider {
return certclient.NewHTTP(url)
}
// GetLocalProvider returns a reference to a file store of headers
// wrapped with an in-memory cache
func GetLocalProvider(dir string) certifiers.Provider {
return certifiers.NewCacheProvider(
certifiers.NewMemStoreProvider(),
files.NewProvider(dir),
)
}
// GetCertifier initializes an inquiring certifier given a fixed chainID
// and a local source of trusted data with at least one seed
func GetCertifier(chainID string, trust certifiers.Provider,
source certifiers.Provider) (*certifiers.InquiringCertifier, error) {
// this gets the most recent verified seed
seed, err := certifiers.LatestSeed(trust)
if certifiers.IsSeedNotFoundErr(err) {
return nil, errors.New("Please run init first to establish a root of trust")
}
if err != nil {
return nil, err
}
cert := certifiers.NewInquiring(chainID, seed, trust, source)
return cert, nil
}
// SecureClient uses a given certifier to wrap an connection to an untrusted
// host and return a cryptographically secure rpc client.
func SecureClient(c rpcclient.Client, cert *certifiers.InquiringCertifier) rpcclient.Client {
return certclient.Wrap(c, cert)
}

27
client/keys.go Normal file
View File

@ -0,0 +1,27 @@
package client
import (
"path/filepath"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/cryptostore"
"github.com/tendermint/go-crypto/keys/storage/filestorage"
)
// KeySubdir is the directory name under root where we store the keys
const KeySubdir = "keys"
// GetKeyManager initializes a key manager based on the configuration
func GetKeyManager(rootDir string) keys.Manager {
keyDir := filepath.Join(rootDir, KeySubdir)
// TODO: smarter loading??? with language and fallback?
codec := keys.MustLoadCodec("english")
// and construct the key manager
manager := cryptostore.New(
cryptostore.SecretBox,
filestorage.New(keyDir),
codec,
)
return manager
}

68
client/proxy.go Normal file
View File

@ -0,0 +1,68 @@
package client
import (
"net/http"
"github.com/tendermint/tmlibs/log"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/core"
rpc "github.com/tendermint/tendermint/rpc/lib/server"
)
const (
wsEndpoint = "/websocket"
)
// StartProxy will start the websocket manager on the client,
// set up the rpc routes to proxy via the given client,
// and start up an http/rpc server on the location given by bind (eg. :1234)
func StartProxy(c rpcclient.Client, bind string, logger log.Logger) error {
c.Start()
r := RPCRoutes(c)
// build the handler...
mux := http.NewServeMux()
rpc.RegisterRPCFuncs(mux, r, logger)
wm := rpc.NewWebsocketManager(r, c)
wm.SetLogger(logger)
core.SetLogger(logger)
mux.HandleFunc(wsEndpoint, wm.WebsocketHandler)
_, err := rpc.StartHTTPServer(bind, mux, logger)
return err
}
// RPCRoutes just routes everything to the given client, as if it were
// a tendermint fullnode.
//
// if we want security, the client must implement it as a secure client
func RPCRoutes(c rpcclient.Client) map[string]*rpc.RPCFunc {
return map[string]*rpc.RPCFunc{
// Subscribe/unsubscribe are reserved for websocket events.
// We can just use the core tendermint impl, which uses the
// EventSwitch we registered in NewWebsocketManager above
"subscribe": rpc.NewWSRPCFunc(core.Subscribe, "event"),
"unsubscribe": rpc.NewWSRPCFunc(core.Unsubscribe, "event"),
// info API
"status": rpc.NewRPCFunc(c.Status, ""),
"blockchain": rpc.NewRPCFunc(c.BlockchainInfo, "minHeight,maxHeight"),
"genesis": rpc.NewRPCFunc(c.Genesis, ""),
"block": rpc.NewRPCFunc(c.Block, "height"),
"commit": rpc.NewRPCFunc(c.Commit, "height"),
"tx": rpc.NewRPCFunc(c.Tx, "hash,prove"),
"validators": rpc.NewRPCFunc(c.Validators, ""),
// broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(c.BroadcastTxCommit, "tx"),
"broadcast_tx_sync": rpc.NewRPCFunc(c.BroadcastTxSync, "tx"),
"broadcast_tx_async": rpc.NewRPCFunc(c.BroadcastTxAsync, "tx"),
// abci API
"abci_query": rpc.NewRPCFunc(c.ABCIQuery, "path,data,prove"),
"abci_info": rpc.NewRPCFunc(c.ABCIInfo, ""),
}
}

111
client/query.go Normal file
View File

@ -0,0 +1,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 = aproof.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
}

View File

@ -1,4 +1,4 @@
package query
package client
import (
"os"
@ -69,32 +69,31 @@ func TestAppProofs(t *testing.T) {
// Test existing key.
var data eyes.Data
bs, height, proofExists, _, err := getWithProof(k, cl, cert)
bs, height, proof, err := GetWithProof(k, cl, cert)
require.NoError(err, "%+v", err)
require.NotNil(proofExists)
require.NotNil(proof)
require.True(height >= uint64(latest.Header.Height))
// Alexis there is a bug here, somehow the above code gives us rootHash = nil
// and proofExists.Verify doesn't care, while proofNotExists.Verify fails.
// and proof.Verify doesn't care, while proofNotExists.Verify fails.
// I am hacking this in to make it pass, but please investigate further.
rootHash = proofExists.RootHash
rootHash = proof.Root()
err = wire.ReadBinaryBytes(bs, &data)
require.NoError(err, "%+v", err)
assert.EqualValues(v, data.Value)
err = proofExists.Verify(k, bs, rootHash)
err = proof.Verify(k, bs, rootHash)
assert.NoError(err, "%+v", err)
// Test non-existing key.
missing := []byte("my-missing-key")
bs, _, proofExists, proofNotExists, err := getWithProof(missing, cl, cert)
bs, _, proof, err = GetWithProof(missing, cl, cert)
require.True(lc.IsNoDataErr(err))
require.Nil(bs)
require.Nil(proofExists)
require.NotNil(proofNotExists)
err = proofNotExists.Verify(missing, rootHash)
require.NotNil(proof)
err = proof.Verify(missing, nil, rootHash)
assert.NoError(err, "%+v", err)
err = proofNotExists.Verify(k, rootHash)
err = proof.Verify(k, nil, rootHash)
assert.Error(err)
}
@ -119,12 +118,11 @@ func TestTxProofs(t *testing.T) {
// First let's make sure a bogus transaction hash returns a valid non-existence proof.
key := types.Tx([]byte("bogus")).Hash()
bs, _, proofExists, proofNotExists, err := getWithProof(key, cl, cert)
bs, _, proof, err := GetWithProof(key, cl, cert)
assert.Nil(bs, "value should be nil")
require.True(lc.IsNoDataErr(err), "error should signal 'no data'")
assert.Nil(proofExists, "existence proof should be nil")
require.NotNil(proofNotExists, "non-existence proof shouldn't be nil")
err = proofNotExists.Verify(key, proofNotExists.RootHash)
require.NotNil(proof, "proof shouldn't be nil")
err = proof.Verify(key, nil, proof.Root())
require.NoError(err, "%+v", err)
// Now let's check with the real tx hash.

View File

@ -6,10 +6,11 @@ import (
"github.com/gorilla/mux"
"github.com/pkg/errors"
sdk "github.com/cosmos/cosmos-sdk"
keysutils "github.com/tendermint/go-crypto/cmd"
keys "github.com/tendermint/go-crypto/keys"
"github.com/tendermint/tmlibs/common"
sdk "github.com/cosmos/cosmos-sdk"
keycmd "github.com/cosmos/cosmos-sdk/client/commands/keys"
)
type Keys struct {
@ -18,7 +19,7 @@ type Keys struct {
}
func DefaultKeysManager() keys.Manager {
return keysutils.GetKeyManager()
return keycmd.GetKeyManager()
}
func NewDefaultKeysManager(algo string) *Keys {

View File

@ -1,7 +1,6 @@
package rest
import (
keycmd "github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/go-crypto/keys"
wire "github.com/tendermint/go-wire"
@ -9,6 +8,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client/commands"
keycmd "github.com/cosmos/cosmos-sdk/client/commands/keys"
)
// PostTx is same as a tx

View File

@ -1,35 +0,0 @@
package rest
import (
"github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/core"
rpc "github.com/tendermint/tendermint/rpc/lib/server"
)
func Routes(c client.Client) map[string]*rpc.RPCFunc {
return map[string]*rpc.RPCFunc{
// subscribe/unsubscribe are reserved for websocket events.
// We can just the core Tendermint implementation, which uses
// the EventSwitch that we registered in NewWebsocketManager above.
"subscribe": rpc.NewWSRPCFunc(core.Subscribe, "event"),
"unsubscribe": rpc.NewWSRPCFunc(core.Unsubscribe, "event"),
// info API
"status": rpc.NewRPCFunc(c.Status, ""),
"blockchain": rpc.NewRPCFunc(c.BlockchainInfo, "minHeight,maxHeight"),
"genesis": rpc.NewRPCFunc(c.Genesis, ""),
"block": rpc.NewRPCFunc(c.Block, "height"),
"commit": rpc.NewRPCFunc(c.Commit, "height"),
"tx": rpc.NewRPCFunc(c.Tx, "hash.prove"),
"validators": rpc.NewRPCFunc(c.Validators, ""),
// broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(c.BroadcastTxCommit, "tx"),
"broadcast_tx_sync": rpc.NewRPCFunc(c.BroadcastTxSync, "tx"),
"broadcast_tx_async": rpc.NewRPCFunc(c.BroadcastTxAsync, "tx"),
// abci API
"abci_query": rpc.NewRPCFunc(c.ABCIQuery, "path,data,prove"),
"abci_info": rpc.NewRPCFunc(c.ABCIInfo, ""),
}
}

View File

@ -12,6 +12,7 @@ test_cli:
./tests/cli/keys.sh
./tests/cli/rpc.sh
./tests/cli/init.sh
./tests/cli/init-server.sh
./tests/cli/basictx.sh
./tests/cli/roles.sh
./tests/cli/restart.sh

View File

@ -5,11 +5,11 @@ import (
"github.com/spf13/cobra"
keycmd "github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/auto"
"github.com/cosmos/cosmos-sdk/client/commands/keys"
"github.com/cosmos/cosmos-sdk/client/commands/proxy"
"github.com/cosmos/cosmos-sdk/client/commands/query"
rpccmd "github.com/cosmos/cosmos-sdk/client/commands/rpc"
@ -76,7 +76,7 @@ func main() {
BaseCli.AddCommand(
commands.InitCmd,
commands.ResetCmd,
keycmd.RootCmd,
keys.RootCmd,
seeds.RootCmd,
rpccmd.RootCmd,
query.RootCmd,

View File

@ -38,6 +38,8 @@ oneTimeTearDown() {
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2

View File

@ -1,29 +1,144 @@
#!/bin/bash
CLIENT_EXE=basecli
EXE=basecli
oneTimeSetUp() {
PASS=qwertyuiop
export BCHOME=$HOME/.bc_keys_test
${CLIENT_EXE} reset_all
${EXE} reset_all
assertTrue "line ${LINENO}" $?
}
newKey(){
assertNotNull "keyname required" "$1"
KEYPASS=${2:-qwertyuiop}
echo $KEYPASS | ${CLIENT_EXE} keys new $1 >/dev/null 2>&1
assertTrue "line ${LINENO}, created $1" $?
assertNotNull "keyname required" "$1"
KEYPASS=${2:-qwertyuiop}
KEY=$(echo $KEYPASS | ${EXE} keys new $1 -o json)
if ! assertTrue "line ${LINENO}: created $1" $?; then return 1; fi
assertEquals "$1" $(echo $KEY | jq .key.name | tr -d \")
return $?
}
testMakeKeys() {
USER=demouser
assertFalse "line ${LINENO}, already user $USER" "${CLIENT_EXE} keys get $USER"
newKey $USER
assertTrue "line ${LINENO}, no user $USER" "${CLIENT_EXE} keys get $USER"
# updateKey <name> <oldkey> <newkey>
updateKey() {
(echo $2; echo $3) | ${EXE} keys update $1 > /dev/null
return $?
}
test00MakeKeys() {
USER=demouser
assertFalse "line ${LINENO}: already user $USER" "${EXE} keys get $USER"
newKey $USER
assertTrue "line ${LINENO}: no user $USER" "${EXE} keys get $USER"
# make sure bad password not accepted
assertFalse "accepts short password" "echo 123 | ${EXE} keys new badpass"
}
test01ListKeys() {
# one line plus the number of keys
assertEquals "2" $(${EXE} keys list | wc -l)
newKey foobar
assertEquals "3" $(${EXE} keys list | wc -l)
# we got the proper name here...
assertEquals "foobar" $(${EXE} keys list -o json | jq .[1].name | tr -d \" )
# we get all names in normal output
EXPECTEDNAMES=$(echo demouser; echo foobar)
TEXTNAMES=$(${EXE} keys list | tail -n +2 | cut -f1)
assertEquals "$EXPECTEDNAMES" "$TEXTNAMES"
# let's make sure the addresses match!
assertEquals "line ${LINENO}: text and json addresses don't match" $(${EXE} keys list | tail -1 | cut -f3) $(${EXE} keys list -o json | jq .[1].address | tr -d \")
}
test02updateKeys() {
USER=changer
PASS1=awsedrftgyhu
PASS2=S4H.9j.D9S7hso
PASS3=h8ybO7GY6d2
newKey $USER $PASS1
assertFalse "line ${LINENO}: accepts invalid pass" "updateKey $USER $PASS2 $PASS2"
assertTrue "line ${LINENO}: doesn't update" "updateKey $USER $PASS1 $PASS2"
assertTrue "line ${LINENO}: takes new key after update" "updateKey $USER $PASS2 $PASS3"
}
test03recoverKeys() {
USER=sleepy
PASS1=S4H.9j.D9S7hso
USER2=easy
PASS2=1234567890
# make a user and check they exist
KEY=$(echo $PASS1 | ${EXE} keys new $USER -o json)
if ! assertTrue "created $USER" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY; echo; fi
SEED=$(echo $KEY | jq .seed | tr -d \")
ADDR=$(echo $KEY | jq .key.address | tr -d \")
PUBKEY=$(echo $KEY | jq .key.pubkey | tr -d \")
assertTrue "line ${LINENO}" "${EXE} keys get $USER > /dev/null"
# let's delete this key
assertFalse "line ${LINENO}" "echo foo | ${EXE} keys delete $USER > /dev/null"
assertTrue "line ${LINENO}" "echo $PASS1 | ${EXE} keys delete $USER > /dev/null"
assertFalse "line ${LINENO}" "${EXE} keys get $USER > /dev/null"
# fails on short password
assertFalse "line ${LINENO}" "echo foo; echo $SEED | ${EXE} keys recover $USER2 -o json > /dev/null"
# fails on bad seed
assertFalse "line ${LINENO}" "echo $PASS2; echo \"silly white whale tower bongo\" | ${EXE} keys recover $USER2 -o json > /dev/null"
# now we got it
KEY2=$((echo $PASS2; echo $SEED) | ${EXE} keys recover $USER2 -o json)
if ! assertTrue "recovery failed: $KEY2" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY2; echo; fi
# make sure it looks the same
NAME2=$(echo $KEY2 | jq .name | tr -d \")
ADDR2=$(echo $KEY2 | jq .address | tr -d \")
PUBKEY2=$(echo $KEY2 | jq .pubkey | tr -d \")
assertEquals "line ${LINENO}: wrong username" "$USER2" "$NAME2"
assertEquals "line ${LINENO}: address doesn't match" "$ADDR" "$ADDR2"
assertEquals "line ${LINENO}: pubkey doesn't match" "$PUBKEY" "$PUBKEY2"
# and we can find the info
assertTrue "line ${LINENO}" "${EXE} keys get $USER2 > /dev/null"
}
# try recovery with secp256k1 keys
test03recoverSecp() {
USER=dings
PASS1=Sbub-U9byS7hso
USER2=booms
PASS2=1234567890
KEY=$(echo $PASS1 | ${EXE} keys new $USER -o json -t secp256k1)
if ! assertTrue "created $USER" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY; echo; fi
SEED=$(echo $KEY | jq .seed | tr -d \")
ADDR=$(echo $KEY | jq .key.address | tr -d \")
PUBKEY=$(echo $KEY | jq .key.pubkey | tr -d \")
assertTrue "line ${LINENO}" "${EXE} keys get $USER > /dev/null"
# now we got it
KEY2=$((echo $PASS2; echo $SEED) | ${EXE} keys recover $USER2 -o json)
if ! assertTrue "recovery failed: $KEY2" $?; then return 1; fi
if [ -n "$DEBUG" ]; then echo $KEY2; echo; fi
# make sure it looks the same
NAME2=$(echo $KEY2 | jq .name | tr -d \")
ADDR2=$(echo $KEY2 | jq .address | tr -d \")
PUBKEY2=$(echo $KEY2 | jq .pubkey | tr -d \")
assertEquals "line ${LINENO}: wrong username" "$USER2" "$NAME2"
assertEquals "line ${LINENO}: address doesn't match" "$ADDR" "$ADDR2"
assertEquals "line ${LINENO}: pubkey doesn't match" "$PUBKEY" "$PUBKEY2"
# and we can find the info
assertTrue "line ${LINENO}" "${EXE} keys get $USER2 > /dev/null"
}
# load and run these tests with shunit2!
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
CLI_DIR=$GOPATH/src/github.com/cosmos/cosmos-sdk/tests/cli

View File

@ -5,10 +5,10 @@ import (
"github.com/spf13/cobra"
keycmd "github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/keys"
"github.com/cosmos/cosmos-sdk/client/commands/proxy"
"github.com/cosmos/cosmos-sdk/client/commands/query"
"github.com/cosmos/cosmos-sdk/client/commands/seeds"
@ -73,7 +73,7 @@ func main() {
commands.InitCmd,
commands.ResetCmd,
commands.VersionCmd,
keycmd.RootCmd,
keys.RootCmd,
seeds.RootCmd,
query.RootCmd,
txcmd.RootCmd,

View File

@ -5,12 +5,10 @@ import (
"github.com/spf13/cobra"
keycmd "github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/auto"
"github.com/cosmos/cosmos-sdk/client/commands/proxy"
"github.com/cosmos/cosmos-sdk/client/commands/query"
rpccmd "github.com/cosmos/cosmos-sdk/client/commands/rpc"
"github.com/cosmos/cosmos-sdk/client/commands/seeds"
@ -52,12 +50,10 @@ func main() {
// we use out own init command to not require address arg
commands.InitCmd,
commands.ResetCmd,
keycmd.RootCmd,
seeds.RootCmd,
rpccmd.RootCmd,
query.RootCmd,
txcmd.RootCmd,
proxy.RootCmd,
commands.VersionCmd,
auto.AutoCompleteCmd,
)

77
glide.lock generated
View File

@ -1,20 +1,22 @@
hash: 7ba2bf40f622c7b95538278cb615b267cf4b53dcda8d47489d250b09bc08d0b4
updated: 2017-08-22T13:09:54.55565241+02:00
hash: ba9b44c722e57f103b8b6b861aabaeb37ad59c706160b6281817945f95dfbc1e
updated: 2017-09-08T20:43:55.154928875+02:00
imports:
- name: github.com/bgentry/speakeasy
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
- name: github.com/btcsuite/btcd
version: 47885ab8702485be6b6f87a03d4f3be0bc5c982c
version: b8df516b4b267acf2de46be593a9d948d1d2c420
subpackages:
- btcec
- name: github.com/btcsuite/fastsha256
version: 637e656429416087660c84436a2a035d69d54e2e
- name: github.com/BurntSushi/toml
version: a368813c5e648fee92e5f6c30e3944ff9d5e8895
version: b26d9c308763d68093482582cea63d69be07a0f0
- name: github.com/ebuchman/fail-test
version: 95f809107225be108efcf10a3509e4ea6ceef3c4
- name: github.com/fsnotify/fsnotify
version: 4da3e2cfbabc9f751898f250b49f2439785783a1
- name: github.com/go-kit/kit
version: 69950137349e3e6781c1a54e4f7fd3803bd36765
version: d67bb4c202e3b91377d1079b110a6c9ce23ab2f8
subpackages:
- log
- log/level
@ -28,9 +30,9 @@ imports:
- name: github.com/go-playground/universal-translator
version: 71201497bace774495daed26a3874fd339e0b538
- name: github.com/go-stack/stack
version: 54be5f394ed2c3e19dac9134a40a95ba5a017f7b
version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
- name: github.com/golang/protobuf
version: 0a4f71a498b7c4812f64969510bcb4eca251e33a
version: 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8
subpackages:
- proto
- ptypes/any
@ -38,12 +40,10 @@ imports:
version: 553a641470496b2327abcac10b36396bd98e45c9
- name: github.com/gorilla/context
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
- name: github.com/gorilla/handlers
version: a4043c62cc2329bacda331d33fc908ab11ef0ec3
- name: github.com/gorilla/mux
version: bcd8bc72b08df0f70df986b97f95590779502d31
- name: github.com/gorilla/websocket
version: a69d9f6de432e2c6b296a947d8a5ee88f68522cf
version: a91eba7f97777409bc2c443f5534d41dd20c5720
- name: github.com/hashicorp/hcl
version: 392dba7d905ed5d04a5794ba89f558b27e2ba1ca
subpackages:
@ -56,7 +56,7 @@ imports:
- json/scanner
- json/token
- name: github.com/howeyc/crc16
version: 96a97a1abb579c7ff1a8ffa77f2e72d1c314b57f
version: 58da63c846043d0bea709c8d47039df06577d6d9
- name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/jmhodges/levigo
@ -64,15 +64,19 @@ imports:
- name: github.com/kr/logfmt
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
- name: github.com/magiconair/properties
version: be5ece7dd465ab0765a9682137865547526d1dfb
version: 51463bfca2576e06c62a8504b5c0f06d61312647
- name: github.com/mattn/go-isatty
version: 57fdcb988a5c543893cc61bce354a6e24ab70022
version: 9622e0cc9d8f9be434ca605520ff9a16808fee47
- name: github.com/mitchellh/mapstructure
version: d0303fe809921458f417bcf828397a65db30a7e4
version: cc8532a8e9a55ea36402aa21efdf403a60d34096
- name: github.com/pelletier/go-buffruneio
version: c37440a7cf42ac63b919c752ca73a85067e05992
- name: github.com/pelletier/go-toml
version: 4692b8f9babfc93db58cc592ba2689d8736781de
version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a
- name: github.com/pkg/errors
version: 645ef00459ed84a119197bfb8d8205042c6df63d
- name: github.com/rcrowley/go-metrics
version: 1f30fe9094a513ce4c700b9a54458bbb0c96996c
- name: github.com/spf13/afero
version: 9be650865eab0c12963d8753212f4f9c66cdcf12
subpackages:
@ -80,13 +84,13 @@ imports:
- name: github.com/spf13/cast
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
- name: github.com/spf13/cobra
version: 4a7b7e65864c064d48dce82efbbfed2bdc0bf2aa
version: 4cdb38c072b86bf795d2c81de50784d9fdd6eb77
- name: github.com/spf13/jwalterweatherman
version: 0efa5202c04663c757d84f90f5219c1250baf94f
version: 8f07c835e5cc1450c082fe3a439cf87b0cbb2d99
- name: github.com/spf13/pflag
version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
- name: github.com/spf13/viper
version: 25b30aa063fc18e48662b86996252eabdcf2f0c7
version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2
- name: github.com/syndtr/goleveldb
version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4
subpackages:
@ -103,7 +107,7 @@ imports:
- leveldb/table
- leveldb/util
- name: github.com/tendermint/abci
version: 7f5f48b6b9ec3964de4b07b6c3cd05d7c91aeee5
version: f1094f760b80ed35d709ed446646b716ded7a6f2
subpackages:
- client
- example/dummy
@ -115,13 +119,11 @@ imports:
- edwards25519
- extra25519
- name: github.com/tendermint/go-crypto
version: 03d2b2446e8587f159e52070b1f1e0def971a2c7
version: 1bc8de4caa844f8b64c120e65b047898f22b7f3e
subpackages:
- cmd
- keys
- keys/cryptostore
- keys/server
- keys/server/types
- keys/storage/filestorage
- keys/storage/memstorage
- keys/wordlist
@ -130,20 +132,21 @@ imports:
subpackages:
- data
- data/base58
- name: github.com/tendermint/iavl
version: a3ec7707c4eacabd33b7c510df364bfce1eb33e6
- name: github.com/tendermint/light-client
version: b2afece9635d11e77dd404019b9cf3885d34f4e5
version: ac2e4bf47b31aaf5d3d336691ac786ec751bfc32
subpackages:
- certifiers
- certifiers/client
- certifiers/files
- proofs
- name: github.com/tendermint/merkleeyes
version: da3842e33b138b387aec3208c3044a936a0aac41
version: 2f6e5d31e7a35045d8d0a5895cb1fec33dd4d32b
subpackages:
- client
- iavl
- name: github.com/tendermint/tendermint
version: 2d4c86dfe8f78568abd8e3847ce703d2085ed929
version: aa78fc14b5824725f8a14dd2c2ad727a9ab89cc3
subpackages:
- blockchain
- cmd/tendermint/commands
@ -170,7 +173,7 @@ imports:
- types
- version
- name: github.com/tendermint/tmlibs
version: 956966e6587aa6b8dd3a375d35c3744291c38d60
version: bfec1ff1cd7fda9f5b2d8b570e3bec163e5f9149
subpackages:
- autofile
- cli
@ -184,7 +187,7 @@ imports:
- logger
- merkle
- name: golang.org/x/crypto
version: 7f7c0c2d75ebb4e32a21396ce36e87b6dadc91c9
version: c7af5bf2638a1164f2eb5467c39c6cffbd13a02e
subpackages:
- curve25519
- nacl/box
@ -195,7 +198,7 @@ imports:
- ripemd160
- salsa20/salsa
- name: golang.org/x/net
version: 02ac38e2528ff4adea90f184d71a3faa04b4b1b0
version: feeb485667d1fdabe727840fe00adc22431bc86e
subpackages:
- context
- http2
@ -205,22 +208,22 @@ imports:
- lex/httplex
- trace
- name: golang.org/x/sys
version: cd2c276457edda6df7fb04895d3fd6a6add42926
version: e62c3de784db939836898e5c19ffd41bece347da
subpackages:
- unix
- name: golang.org/x/text
version: 836efe42bb4aa16aaa17b9c155d8813d336ed720
version: 470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4
subpackages:
- secure/bidirule
- transform
- unicode/bidi
- unicode/norm
- name: google.golang.org/genproto
version: b0a3dcfcd1a9bd48e63634bd8802960804cf8315
version: 411e09b969b1170a9f0c467558eb4c4c110d9c77
subpackages:
- googleapis/rpc/status
- name: google.golang.org/grpc
version: 71260d21715bff9c2ac999d546349d4f4b77ef1d
version: 844f573616520565fdc6fb4db242321b5456fd6d
subpackages:
- codes
- credentials
@ -236,12 +239,12 @@ imports:
- tap
- transport
- name: gopkg.in/go-playground/validator.v9
version: d529ee1b0f30352444f507cc6cdac96bfd12decc
version: 6d8c18553ea1ac493d049edd6f102f52e618f085
- name: gopkg.in/yaml.v2
version: eb3733d160e74a9c7e442f435eb3bea458e1d19f
version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
testImports:
- name: github.com/davecgh/go-spew
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
subpackages:
- spew
- name: github.com/pmezard/go-difflib
@ -249,7 +252,7 @@ testImports:
subpackages:
- difflib
- name: github.com/stretchr/testify
version: 05e8a0eda380579888eb53c394909df027f06991
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
subpackages:
- assert
- require

View File

@ -15,26 +15,22 @@ import:
- package: github.com/tendermint/go-crypto
version: develop
subpackages:
- cmd
- keys
- package: github.com/tendermint/go-wire
version: develop
subpackages:
- data
- package: github.com/tendermint/light-client
version: unstable
version: develop
subpackages:
- proofs
- certifiers
- certifiers/client
- certifiers/files
- package: github.com/tendermint/merkleeyes
version: origin/unstable
subpackages:
- client
- iavl
- package: github.com/tendermint/iavl
version: develop
- package: github.com/tendermint/tendermint
version: no-internet
version: develop
subpackages:
- config
- node

View File

@ -13,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/stack"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/iavl"
)
// TODO: query seeds (register/update)
@ -197,22 +198,26 @@ func packetQueryCmd(cmd *cobra.Command, args []string) error {
}
// output queue, create a post packet
bs, height, proof, _, err := query.GetWithProof(key)
bs, height, proof, err := query.GetWithProof(key)
if err != nil {
return err
}
if len(bs) == 0 {
// TODO: what info here?
return errors.New("no such packet")
}
err = wire.ReadBinaryBytes(bs, &packet)
if err != nil {
return err
}
// create the post packet here.
post := ibc.PostPacketTx{
FromChainID: commands.GetChainID(),
FromChainHeight: height,
Key: key,
Packet: packet,
Proof: proof,
Proof: proof.(*iavl.KeyExistsProof),
}
// print json direct, as we don't need to wrap with the height

View File

@ -3,8 +3,8 @@ package ibc
import (
"fmt"
"github.com/tendermint/iavl"
"github.com/tendermint/light-client/certifiers"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
@ -59,7 +59,7 @@ 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, _, err := tree.GetWithProof(key)
_, proof, err := tree.GetWithProof(key)
if err != nil {
panic(err)
}
@ -70,7 +70,7 @@ func makePostPacket(tree *iavl.IAVLTree, packet Packet, fromID string, fromHeigh
return PostPacketTx{
FromChainID: fromID,
FromChainHeight: uint64(fromHeight),
Proof: proof,
Proof: proof.(*iavl.KeyExistsProof),
Key: key,
Packet: packet,
}

View File

@ -2,8 +2,8 @@ package ibc
import (
"github.com/tendermint/go-wire/data"
"github.com/tendermint/iavl"
"github.com/tendermint/light-client/certifiers"
"github.com/tendermint/merkleeyes/iavl"
sdk "github.com/cosmos/cosmos-sdk"
)
@ -86,7 +86,7 @@ func (u UpdateChainTx) Wrap() sdk.Tx {
// If must have the special `AllowIBC` permission from the app
// that can send this packet (so only coins can request SendTx packet)
type CreatePacketTx struct {
DestChain string `json:"dest_chain"`
DestChain string `json:"dest_chain"`
Permissions sdk.Actors `json:"permissions"`
Tx sdk.Tx `json:"tx"`
}

View File

@ -14,7 +14,7 @@ package commands
// // "github.com/tendermint/tmlibs/log"
// "github.com/tendermint/go-wire"
// "github.com/tendermint/merkleeyes/iavl"
// "github.com/tendermint/iavl"
// cmn "github.com/tendermint/tmlibs/common"
// "github.com/cosmos/cosmos-sdk/plugins/ibc"

View File

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/iavl"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"

View File

@ -3,7 +3,7 @@ package state
import (
"math/rand"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/iavl"
)
// store nonce as it's own type so no one can even try to fake it
@ -50,7 +50,7 @@ func (b *Bonsai) Remove(key []byte) (value []byte) {
return
}
func (b *Bonsai) GetWithProof(key []byte) ([]byte, *iavl.KeyExistsProof, *iavl.KeyNotExistsProof, error) {
func (b *Bonsai) GetWithProof(key []byte) ([]byte, iavl.KeyProof, error) {
return b.Tree.GetWithProof(key)
}

View File

@ -1,6 +1,6 @@
package state
import "github.com/tendermint/merkleeyes/iavl"
import "github.com/tendermint/iavl"
// State represents the app states, separating the commited state (for queries)
// from the working state (for CheckTx and AppendTx)

View File

@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/iavl"
)
type keyVal struct {

View File

@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/iavl"
dbm "github.com/tendermint/tmlibs/db"
)