Skip to content
This repository has been archived by the owner on Jul 15, 2018. It is now read-only.

Commit

Permalink
Merge pull request #15 from tendermint/develop
Browse files Browse the repository at this point in the history
v0.2.0
  • Loading branch information
ebuchman authored May 18, 2017
2 parents 0ca2c6f + c61497b commit 7dff409
Show file tree
Hide file tree
Showing 61 changed files with 3,342 additions and 317 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.swp
*.swo
vendor
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Changelog

## 0.2.0 (May 18, 2017)

BREAKING CHANGES:

- [hd] The following functions no longer take a `coin string` as argument: `ComputeAddress`, `AddrFromPubKeyBytes`, `ComputeAddressForPrivKey`, `ComputeWIF`, `WIFFromPrivKeyBytes`
- Changes to `PrivKey`, `PubKey`, and `Signature` (denoted `Xxx` below):
- interfaces are renamed `XxxInner`, and are not for use outside the package, though they must be exposed for sake of serialization.
- `Xxx` is now a struct that wraps the corresponding `XxxInner` interface

FEATURES:

- `github.com/tendermint/go-keys -> github.com/tendermint/go-crypto/keys` - command and lib for generating and managing encrypted keys
- [hd] New function `WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string`
- Changes to `PrivKey`, `PubKey`, and `Signature` (denoted `Xxx` below):
- Expose a new method `Unwrap() XxxInner` on the `Xxx` struct which returns the corresponding `XxxInner` interface
- Expose a new method `Wrap() Xxx` on the `XxxInner` interface which returns the corresponding `Xxx` struct

IMPROVEMENTS:

- Update to use new `tmlibs` repository

## 0.1.0 (April 14, 2017)

Initial release

31 changes: 29 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
.PHONY: docs
.PHONEY: all docs test install get_vendor_deps ensure_tools codegen

GOTOOLS = \
github.com/Masterminds/glide
REPO:=github.com/tendermint/go-crypto

docs:
@go get github.com/davecheney/godoc2md
godoc2md $(REPO) > README.md

all: install test

install:
go install ./cmd/keys

test:
go test ./...
go test `glide novendor`

get_vendor_deps: ensure_tools
@rm -rf vendor/
@echo "--> Running glide install"
@glide install

ensure_tools:
go get $(GOTOOLS)

prepgen: install
go install ./vendor/github.com/btcsuite/btcutil/base58
go install ./vendor/github.com/stretchr/testify/assert
go install ./vendor/github.com/stretchr/testify/require
go install ./vendor/golang.org/x/crypto/bcrypt

codegen:
@echo "--> regenerating all interface wrappers"
@gen
@echo "Done!"
6 changes: 6 additions & 0 deletions _gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package main

import (
_ "github.com/tendermint/go-wire/gen"
_ "github.com/clipperhouse/stringer"
)
2 changes: 1 addition & 1 deletion armor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"bytes"
"io/ioutil"

. "github.com/tendermint/go-common"
. "github.com/tendermint/tmlibs/common"
"golang.org/x/crypto/openpgp/armor"
)

Expand Down
21 changes: 21 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
machine:
environment:
GOPATH: /home/ubuntu/.go_workspace
PROJECT_PARENT_PATH: "$GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME"
PROJECT_PATH: $GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
GO15VENDOREXPERIMENT: 1
hosts:
circlehost: 127.0.0.1
localhost: 127.0.0.1

dependencies:
override:
- mkdir -p "$PROJECT_PARENT_PATH"
- ln -sf "$HOME/$CIRCLE_PROJECT_REPONAME/" "$PROJECT_PATH"
post:
- go version

test:
override:
- "go version"
- "cd $PROJECT_PATH && make get_vendor_deps && make test"
117 changes: 117 additions & 0 deletions cmd/README.md
Original file line number Diff line number Diff line change
@@ -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 = "[email protected]"
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="
}
}
]
```
44 changes: 44 additions & 0 deletions cmd/get.go
Original file line number Diff line number Diff line change
@@ -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 cmd

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: func(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
},
}

func init() {
RootCmd.AddCommand(getCmd)
}
27 changes: 27 additions & 0 deletions cmd/keys/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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 main

import (
"os"

"github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/tmlibs/cli"
)

func main() {
root := cli.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc"))
root.Execute()
}
36 changes: 36 additions & 0 deletions cmd/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 cmd

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: func(cmd *cobra.Command, args []string) error {
infos, err := GetKeyManager().List()
if err == nil {
printInfos(infos)
}
return err
},
}

func init() {
RootCmd.AddCommand(listCmd)
}
56 changes: 56 additions & 0 deletions cmd/new.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 cmd

import (
"github.com/pkg/errors"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// 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: newPassword,
}

func init() {
RootCmd.AddCommand(newCmd)
newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1)")
}

func newPassword(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("type")

pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:")
if err != nil {
return err
}

info, err := GetKeyManager().Create(name, pass, algo)
if err == nil {
printInfo(info)
}
return err
}
Loading

0 comments on commit 7dff409

Please sign in to comment.