From f80957ad3c78170a166180eb05f7ea9c674835a5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 16:30:15 +0100 Subject: [PATCH 01/45] Basic cobra/viper cli framework --- LICENSE | 202 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/keys/main.go | 21 +++++ cmd/list.go | 49 ++++++++++++ cmd/new.go | 49 ++++++++++++ cmd/root.go | 84 ++++++++++++++++++++ cmd/serve.go | 50 ++++++++++++ cmd/update.go | 39 +++++++++ keys.yaml | 2 + 8 files changed, 496 insertions(+) create mode 100644 LICENSE create mode 100644 cmd/keys/main.go create mode 100644 cmd/list.go create mode 100644 cmd/new.go create mode 100644 cmd/root.go create mode 100644 cmd/serve.go create mode 100644 cmd/update.go create mode 100644 keys.yaml diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/cmd/keys/main.go b/cmd/keys/main.go new file mode 100644 index 000000000..8c880ea03 --- /dev/null +++ b/cmd/keys/main.go @@ -0,0 +1,21 @@ +// 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 "github.com/tendermint/go-keys/cmd" + +func main() { + cmd.Execute() +} diff --git a/cmd/list.go b/cmd/list.go new file mode 100644 index 000000000..e2c8a7631 --- /dev/null +++ b/cmd/list.go @@ -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 cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// 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.`, + Run: func(cmd *cobra.Command, args []string) { + // TODO: Work your own magic here + fmt.Println("list called") + fmt.Println(viper.Get("format")) + }, +} + +func init() { + RootCmd.AddCommand(listCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // listCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + listCmd.Flags().StringP("format", "f", "text", "Format to display (text|json)") +} diff --git a/cmd/new.go b/cmd/new.go new file mode 100644 index 000000000..40462e686 --- /dev/null +++ b/cmd/new.go @@ -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 cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// newCmd represents the new command +var newCmd = &cobra.Command{ + Use: "new", + 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.`, + Run: func(cmd *cobra.Command, args []string) { + // TODO: Work your own magic here + fmt.Println("new called") + }, +} + +func init() { + RootCmd.AddCommand(newCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // newCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // newCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 000000000..896b7c666 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,84 @@ +// 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 ( + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var ( + rootDir string +) + +// 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.`, + PersistentPreRunE: bindFlags, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := RootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(-1) + } +} + +func init() { + cobra.OnInitialize(initEnv) + RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data (default is $HOME/.tlc)") +} + +// initEnv sets to use ENV variables if set. +func initEnv() { + // env variables with TM prefix (eg. TM_ROOT) + viper.SetEnvPrefix("TM") + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.AutomaticEnv() +} + +func bindFlags(cmd *cobra.Command, args []string) error { + // cmd.Flags() includes flags from this command and all persistent flags from the parent + if err := viper.BindPFlags(cmd.Flags()); err != nil { + return err + } + + // rootDir is command line flag, env variable, or default $HOME/.tlc + rootDir = viper.GetString("root") + viper.SetConfigName("keys") // name of config file (without extension) + viper.AddConfigPath(rootDir) // search root directory + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Println("Using config file:", viper.ConfigFileUsed()) + } + + return nil +} diff --git a/cmd/serve.go b/cmd/serve.go new file mode 100644 index 000000000..06e923c7b --- /dev/null +++ b/cmd/serve.go @@ -0,0 +1,50 @@ +// 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 ( + "fmt" + + "github.com/spf13/cobra" +) + +// serveCmd represents the serve command +var serveCmd = &cobra.Command{ + Use: "serve", + Short: "Run the key manager as an http server", + Long: `Launch an http server with a rest api to manage the +private keys much more in depth than the cli can perform. +In particular, this will allow you to sign transactions with +the private keys in the store.`, + Run: func(cmd *cobra.Command, args []string) { + // TODO: Work your own magic here + fmt.Println("serve called") + }, +} + +func init() { + RootCmd.AddCommand(serveCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // serveCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // serveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + +} diff --git a/cmd/update.go b/cmd/update.go new file mode 100644 index 000000000..c36bcf25a --- /dev/null +++ b/cmd/update.go @@ -0,0 +1,39 @@ +// 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 ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// updateCmd represents the update command +var updateCmd = &cobra.Command{ + Use: "update", + Short: "Change the password for a private key", + Long: `Change the password for a private key.`, + Run: func(cmd *cobra.Command, args []string) { + // TODO: Work your own magic here + fmt.Println(viper.Get("name")) + fmt.Println("update called") + }, +} + +func init() { + RootCmd.AddCommand(updateCmd) + updateCmd.Flags().StringP("name", "n", "", "Name of key to update") +} diff --git a/keys.yaml b/keys.yaml new file mode 100644 index 000000000..3aec88a7b --- /dev/null +++ b/keys.yaml @@ -0,0 +1,2 @@ +format: super +name: george From d979bfc49ea930134ddedba28eb087716f5c25f8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 16:56:17 +0100 Subject: [PATCH 02/45] Add flag validation --- cmd/root.go | 21 ++++++++++++++++----- keys.yaml | 1 - 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 896b7c666..05fffd489 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,12 +19,14 @@ import ( "os" "strings" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" ) var ( rootDir string + format string ) // RootCmd represents the base command when called without any subcommands @@ -37,9 +39,6 @@ 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.`, PersistentPreRunE: bindFlags, - // Uncomment the following line if your bare application - // has an action associated with it: - // Run: func(cmd *cobra.Command, args []string) { }, } // Execute adds all child commands to the root command sets flags appropriately. @@ -53,7 +52,8 @@ func Execute() { func init() { cobra.OnInitialize(initEnv) - RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data (default is $HOME/.tlc)") + RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data (default is TM_ROOT or $HOME/.tlc)") + RootCmd.PersistentFlags().StringP("format", "f", "text", "Output format (text|json)") } // initEnv sets to use ENV variables if set. @@ -80,5 +80,16 @@ func bindFlags(cmd *cobra.Command, args []string) error { fmt.Println("Using config file:", viper.ConfigFileUsed()) } - return nil + return validateFlags(cmd) +} + +// validateFlags asserts all RootCmd flags are valid +func validateFlags(cmd *cobra.Command) error { + format = viper.GetString("format") + switch format { + case "text", "json": + return nil + default: + return errors.Errorf("Unsupported format: %s", format) + } } diff --git a/keys.yaml b/keys.yaml index 3aec88a7b..812adb72a 100644 --- a/keys.yaml +++ b/keys.yaml @@ -1,2 +1 @@ -format: super name: george From 78bb9f9cd8ac4dacbb28efe1db37566f88e75209 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 18:07:59 +0100 Subject: [PATCH 03/45] Import keystore logic from light-client --- Makefile | 2 + cryptostore/docs.go | 25 ++++ cryptostore/enc_storage.go | 47 ++++++ cryptostore/encoder.go | 54 +++++++ cryptostore/encoder_test.go | 59 ++++++++ cryptostore/generator.go | 30 ++++ cryptostore/holder.go | 120 +++++++++++++++ cryptostore/holder_test.go | 241 +++++++++++++++++++++++++++++++ cryptostore/storage_test.go | 41 ++++++ keys.toml | 1 + keys.yaml | 1 - storage.go | 10 ++ storage/filestorage/main.go | 171 ++++++++++++++++++++++ storage/filestorage/main_test.go | 95 ++++++++++++ storage/memstorage/main.go | 70 +++++++++ storage/memstorage/main_test.go | 67 +++++++++ transactions.go | 61 ++++++++ 17 files changed, 1094 insertions(+), 1 deletion(-) create mode 100644 Makefile create mode 100644 cryptostore/docs.go create mode 100644 cryptostore/enc_storage.go create mode 100644 cryptostore/encoder.go create mode 100644 cryptostore/encoder_test.go create mode 100644 cryptostore/generator.go create mode 100644 cryptostore/holder.go create mode 100644 cryptostore/holder_test.go create mode 100644 cryptostore/storage_test.go create mode 100644 keys.toml delete mode 100644 keys.yaml create mode 100644 storage.go create mode 100644 storage/filestorage/main.go create mode 100644 storage/filestorage/main_test.go create mode 100644 storage/memstorage/main.go create mode 100644 storage/memstorage/main_test.go create mode 100644 transactions.go diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..7a1f90fd4 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +test: + go test ./... diff --git a/cryptostore/docs.go b/cryptostore/docs.go new file mode 100644 index 000000000..5c66fba2d --- /dev/null +++ b/cryptostore/docs.go @@ -0,0 +1,25 @@ +/* +package cryptostore maintains everything needed for doing public-key signing and +key management in software, based on the go-crypto library from tendermint. + +It is flexible, and allows the user to provide a key generation algorithm +(currently Ed25519 or Secp256k1), an encoder to passphrase-encrypt our keys +when storing them (currently SecretBox from NaCl), and a method to persist +the keys (currently FileStorage like ssh, or MemStorage for tests). +It should be relatively simple to write your own implementation of these +interfaces to match your specific security requirements. + +Note that the private keys are never exposed outside the package, and the +interface of Manager could be implemented by an HSM in the future for +enhanced security. It would require a completely different implementation +however. + +This Manager aims to implement Signer and KeyManager interfaces, along +with some extensions to allow importing/exporting keys and updating the +passphrase. + +Encoder and Generator implementations are currently in this package, +keys.Storage implementations exist as subpackages of +keys/storage +*/ +package cryptostore diff --git a/cryptostore/enc_storage.go b/cryptostore/enc_storage.go new file mode 100644 index 000000000..0a935075f --- /dev/null +++ b/cryptostore/enc_storage.go @@ -0,0 +1,47 @@ +package cryptostore + +import ( + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-keys" +) + +// encryptedStorage needs passphrase to get private keys +type encryptedStorage struct { + coder Encoder + store keys.Storage +} + +func (es encryptedStorage) Put(name, pass string, key crypto.PrivKey) error { + secret, err := es.coder.Encrypt(key, pass) + if err != nil { + return err + } + + ki := info(name, key) + return es.store.Put(name, secret, ki) +} + +func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.KeyInfo, error) { + secret, info, err := es.store.Get(name) + if err != nil { + return nil, info, err + } + key, err := es.coder.Decrypt(secret, pass) + return key, info, err +} + +func (es encryptedStorage) List() ([]keys.KeyInfo, error) { + return es.store.List() +} + +func (es encryptedStorage) Delete(name string) error { + return es.store.Delete(name) +} + +// info hardcodes the encoding of keys +func info(name string, key crypto.PrivKey) keys.KeyInfo { + return keys.KeyInfo{ + Name: name, + PubKey: crypto.PubKeyS{key.PubKey()}, + } +} diff --git a/cryptostore/encoder.go b/cryptostore/encoder.go new file mode 100644 index 000000000..03bc1e4e8 --- /dev/null +++ b/cryptostore/encoder.go @@ -0,0 +1,54 @@ +package cryptostore + +import ( + "github.com/pkg/errors" + crypto "github.com/tendermint/go-crypto" +) + +var ( + // SecretBox uses the algorithm from NaCL to store secrets securely + SecretBox Encoder = secretbox{} + // Noop doesn't do any encryption, should only be used in test code + Noop Encoder = noop{} +) + +// Encoder is used to encrypt any key with a passphrase for storage. +// +// This should use a well-designed symetric encryption algorithm +type Encoder interface { + Encrypt(key crypto.PrivKey, pass string) ([]byte, error) + Decrypt(data []byte, pass string) (crypto.PrivKey, error) +} + +func secret(passphrase string) []byte { + // TODO: Sha256(Bcrypt(passphrase)) + return crypto.Sha256([]byte(passphrase)) +} + +type secretbox struct{} + +func (e secretbox) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) { + s := secret(pass) + cipher := crypto.EncryptSymmetric(key.Bytes(), s) + return cipher, nil +} + +func (e secretbox) Decrypt(data []byte, pass string) (crypto.PrivKey, error) { + s := secret(pass) + private, err := crypto.DecryptSymmetric(data, s) + if err != nil { + return nil, errors.Wrap(err, "Invalid Passphrase") + } + key, err := crypto.PrivKeyFromBytes(private) + return key, errors.Wrap(err, "Invalid Passphrase") +} + +type noop struct{} + +func (n noop) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) { + return key.Bytes(), nil +} + +func (n noop) Decrypt(data []byte, pass string) (crypto.PrivKey, error) { + return crypto.PrivKeyFromBytes(data) +} diff --git a/cryptostore/encoder_test.go b/cryptostore/encoder_test.go new file mode 100644 index 000000000..8b72509ed --- /dev/null +++ b/cryptostore/encoder_test.go @@ -0,0 +1,59 @@ +package cryptostore_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/go-keys/cryptostore" +) + +func TestNoopEncoder(t *testing.T) { + assert, require := assert.New(t), require.New(t) + noop := cryptostore.Noop + + key := cryptostore.GenEd25519.Generate() + key2 := cryptostore.GenSecp256k1.Generate() + + b, err := noop.Encrypt(key, "encode") + require.Nil(err) + assert.NotEmpty(b) + + b2, err := noop.Encrypt(key2, "encode") + require.Nil(err) + assert.NotEmpty(b2) + assert.NotEqual(b, b2) + + // note the decode with a different password works - not secure! + pk, err := noop.Decrypt(b, "decode") + require.Nil(err) + require.NotNil(pk) + assert.Equal(key, pk) + + pk2, err := noop.Decrypt(b2, "kggugougp") + require.Nil(err) + require.NotNil(pk2) + assert.Equal(key2, pk2) +} + +func TestSecretBox(t *testing.T) { + assert, require := assert.New(t), require.New(t) + enc := cryptostore.SecretBox + + key := cryptostore.GenEd25519.Generate() + pass := "some-special-secret" + + b, err := enc.Encrypt(key, pass) + require.Nil(err) + assert.NotEmpty(b) + + // decoding with a different pass is an error + pk, err := enc.Decrypt(b, "decode") + require.NotNil(err) + require.Nil(pk) + + // but decoding with the same passphrase gets us our key + pk, err = enc.Decrypt(b, pass) + require.Nil(err) + assert.Equal(key, pk) +} diff --git a/cryptostore/generator.go b/cryptostore/generator.go new file mode 100644 index 000000000..c6661fb34 --- /dev/null +++ b/cryptostore/generator.go @@ -0,0 +1,30 @@ +package cryptostore + +import crypto "github.com/tendermint/go-crypto" + +var ( + // GenEd25519 produces Ed25519 private keys + GenEd25519 Generator = GenFunc(genEd25519) + // GenSecp256k1 produces Secp256k1 private keys + GenSecp256k1 Generator = GenFunc(genSecp256) +) + +// Generator determines the type of private key the keystore creates +type Generator interface { + Generate() crypto.PrivKey +} + +// GenFunc is a helper to transform a function into a Generator +type GenFunc func() crypto.PrivKey + +func (f GenFunc) Generate() crypto.PrivKey { + return f() +} + +func genEd25519() crypto.PrivKey { + return crypto.GenPrivKeyEd25519() +} + +func genSecp256() crypto.PrivKey { + return crypto.GenPrivKeySecp256k1() +} diff --git a/cryptostore/holder.go b/cryptostore/holder.go new file mode 100644 index 000000000..4beb64160 --- /dev/null +++ b/cryptostore/holder.go @@ -0,0 +1,120 @@ +package cryptostore + +import keys "github.com/tendermint/go-keys" + +// Manager combines encyption and storage implementation to provide +// a full-featured key manager +type Manager struct { + gen Generator + es encryptedStorage +} + +func New(gen Generator, coder Encoder, store keys.Storage) Manager { + return Manager{ + gen: gen, + es: encryptedStorage{ + coder: coder, + store: store, + }, + } +} + +// exists just to make sure we fulfill the Signer interface +func (s Manager) assertSigner() keys.Signer { + return s +} + +// exists just to make sure we fulfill the KeyManager interface +func (s Manager) assertKeyManager() keys.KeyManager { + return s +} + +// Create adds a new key to the storage engine, returning error if +// another key already stored under this name +func (s Manager) Create(name, passphrase string) error { + key := s.gen.Generate() + return s.es.Put(name, passphrase, key) +} + +// List loads the keys from the storage and enforces alphabetical order +func (s Manager) List() (keys.KeyInfos, error) { + k, err := s.es.List() + res := keys.KeyInfos(k) + res.Sort() + return res, err +} + +// Get returns the public information about one key +func (s Manager) Get(name string) (keys.KeyInfo, error) { + _, info, err := s.es.store.Get(name) + return info, err +} + +// Sign will modify the Signable in order to attach a valid signature with +// this public key +// +// If no key for this name, or the passphrase doesn't match, returns an error +func (s Manager) Sign(name, passphrase string, tx keys.Signable) error { + key, _, err := s.es.Get(name, passphrase) + if err != nil { + return err + } + sig := key.Sign(tx.SignBytes()) + pubkey := key.PubKey() + return tx.Sign(pubkey, sig) +} + +// Export decodes the private key with the current password, encodes +// it with a secure one-time password and generates a sequence that can be +// Imported by another Manager +// +// This is designed to copy from one device to another, or provide backups +// during version updates. +func (s Manager) Export(name, oldpass, transferpass string) ([]byte, error) { + key, _, err := s.es.Get(name, oldpass) + if err != nil { + return nil, err + } + + res, err := s.es.coder.Encrypt(key, transferpass) + return res, err +} + +// Import accepts bytes generated by Export along with the same transferpass +// If they are valid, it stores the password under the given name with the +// new passphrase. +func (s Manager) Import(name, newpass, transferpass string, data []byte) error { + key, err := s.es.coder.Decrypt(data, transferpass) + if err != nil { + return err + } + + return s.es.Put(name, newpass, key) +} + +// Delete removes key forever, but we must present the +// proper passphrase before deleting it (for security) +func (s Manager) Delete(name, passphrase string) error { + // verify we have the proper password before deleting + _, _, err := s.es.Get(name, passphrase) + if err != nil { + return err + } + return s.es.Delete(name) +} + +// Update changes the passphrase with which a already stored key is encoded. +// +// oldpass must be the current passphrase used for encoding, newpass will be +// the only valid passphrase from this time forward +func (s Manager) Update(name, oldpass, newpass string) error { + key, _, err := s.es.Get(name, oldpass) + if err != nil { + return err + } + + // we must delete first, as Putting over an existing name returns an error + s.Delete(name, oldpass) + + return s.es.Put(name, newpass, key) +} diff --git a/cryptostore/holder_test.go b/cryptostore/holder_test.go new file mode 100644 index 000000000..fb5ef854f --- /dev/null +++ b/cryptostore/holder_test.go @@ -0,0 +1,241 @@ +package cryptostore_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/go-keys/cryptostore" + "github.com/tendermint/go-keys/storage/memstorage" +) + +// TestKeyManagement makes sure we can manipulate these keys well +func TestKeyManagement(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // make the storage with reasonable defaults + cstore := cryptostore.New( + cryptostore.GenSecp256k1, + cryptostore.SecretBox, + memstorage.New(), + ) + + n1, n2, n3 := "personal", "business", "other" + p1, p2 := "1234", "really-secure!@#$" + + // Check empty state + l, err := cstore.List() + require.Nil(err) + assert.Empty(l) + + // create some keys + _, err = cstore.Get(n1) + assert.NotNil(err) + err = cstore.Create(n1, p1) + require.Nil(err) + err = cstore.Create(n2, p2) + require.Nil(err) + + // we can get these keys + i2, err := cstore.Get(n2) + assert.Nil(err) + _, err = cstore.Get(n3) + assert.NotNil(err) + + // list shows them in order + keys, err := cstore.List() + require.Nil(err) + require.Equal(2, len(keys)) + // note these are in alphabetical order + assert.Equal(n2, keys[0].Name) + assert.Equal(n1, keys[1].Name) + assert.Equal(i2.PubKey, keys[0].PubKey) + + // deleting a key removes it + err = cstore.Delete("bad name", "foo") + require.NotNil(err) + err = cstore.Delete(n1, p1) + require.Nil(err) + keys, err = cstore.List() + require.Nil(err) + assert.Equal(1, len(keys)) + _, err = cstore.Get(n1) + assert.NotNil(err) + + // make sure that it only signs with the right password + // tx := mock.NewSig([]byte("mytransactiondata")) + // err = cstore.Sign(n2, p1, tx) + // assert.NotNil(err) + // err = cstore.Sign(n2, p2, tx) + // assert.Nil(err, "%+v", err) + // sigs, err := tx.Signers() + // assert.Nil(err, "%+v", err) + // if assert.Equal(1, len(sigs)) { + // assert.Equal(i2.PubKey, sigs[0]) + // } +} + +// TestSignVerify does some detailed checks on how we sign and validate +// signatures +// func TestSignVerify(t *testing.T) { +// assert, require := assert.New(t), require.New(t) + +// // make the storage with reasonable defaults +// cstore := cryptostore.New( +// cryptostore.GenSecp256k1, +// cryptostore.SecretBox, +// memstorage.New(), +// ) + +// n1, n2 := "some dude", "a dudette" +// p1, p2 := "1234", "foobar" + +// // create two users and get their info +// err := cstore.Create(n1, p1) +// require.Nil(err) +// i1, err := cstore.Get(n1) +// require.Nil(err) + +// err = cstore.Create(n2, p2) +// require.Nil(err) +// i2, err := cstore.Get(n2) +// require.Nil(err) + +// // let's try to sign some messages +// d1 := []byte("my first message") +// d2 := []byte("some other important info!") + +// // try signing both data with both keys... +// s11, err := cstore.Signature(n1, p1, d1) +// require.Nil(err) +// s12, err := cstore.Signature(n1, p1, d2) +// require.Nil(err) +// s21, err := cstore.Signature(n2, p2, d1) +// require.Nil(err) +// s22, err := cstore.Signature(n2, p2, d2) +// require.Nil(err) + +// // let's try to validate and make sure it only works when everything is proper +// keys := [][]byte{i1.PubKey, i2.PubKey} +// data := [][]byte{d1, d2} +// sigs := [][]byte{s11, s12, s21, s22} + +// // loop over keys and data +// for k := 0; k < 2; k++ { +// for d := 0; d < 2; d++ { +// // make sure only the proper sig works +// good := 2*k + d +// for s := 0; s < 4; s++ { +// err = cstore.Verify(data[d], sigs[s], keys[k]) +// if s == good { +// assert.Nil(err, "%+v", err) +// } else { +// assert.NotNil(err) +// } +// } +// } +// } +// } + +func assertPassword(assert *assert.Assertions, cstore cryptostore.Manager, name, pass, badpass string) { + err := cstore.Update(name, badpass, pass) + assert.NotNil(err) + err = cstore.Update(name, pass, pass) + assert.Nil(err, "%+v", err) +} + +// TestAdvancedKeyManagement verifies update, import, export functionality +func TestAdvancedKeyManagement(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // make the storage with reasonable defaults + cstore := cryptostore.New( + cryptostore.GenSecp256k1, + cryptostore.SecretBox, + memstorage.New(), + ) + + n1, n2 := "old-name", "new name" + p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$" + + // make sure key works with initial password + err := cstore.Create(n1, p1) + require.Nil(err, "%+v", err) + assertPassword(assert, cstore, n1, p1, p2) + + // update password requires the existing password + err = cstore.Update(n1, "jkkgkg", p2) + assert.NotNil(err) + assertPassword(assert, cstore, n1, p1, p2) + + // then it changes the password when correct + err = cstore.Update(n1, p1, p2) + assert.Nil(err) + // p2 is now the proper one! + assertPassword(assert, cstore, n1, p2, p1) + + // exporting requires the proper name and passphrase + _, err = cstore.Export(n2, p2, pt) + assert.NotNil(err) + _, err = cstore.Export(n1, p1, pt) + assert.NotNil(err) + exported, err := cstore.Export(n1, p2, pt) + require.Nil(err, "%+v", err) + + // import fails on bad transfer pass + err = cstore.Import(n2, p3, p2, exported) + assert.NotNil(err) + // import cannot overwrite existing keys + err = cstore.Import(n1, p3, pt, exported) + assert.NotNil(err) + // we can now import under another name + err = cstore.Import(n2, p3, pt, exported) + require.Nil(err, "%+v", err) + + // make sure both passwords are now properly set (not to the transfer pass) + assertPassword(assert, cstore, n1, p2, pt) + assertPassword(assert, cstore, n2, p3, pt) +} + +// func ExampleStore() { +// // Select the encryption and storage for your cryptostore +// cstore := cryptostore.New( +// cryptostore.GenEd25519, +// cryptostore.SecretBox, +// // Note: use filestorage.New(dir) for real data +// memstorage.New(), +// ) + +// // Add keys and see they return in alphabetical order +// cstore.Create("Bob", "friend") +// cstore.Create("Alice", "secret") +// cstore.Create("Carl", "mitm") +// info, _ := cstore.List() +// for _, i := range info { +// fmt.Println(i.Name) +// } + +// // We need to use passphrase to generate a signature +// tx := mock.NewSig([]byte("deadbeef")) +// err := cstore.Sign("Bob", "friend", tx) +// if err != nil { +// fmt.Println("don't accept real passphrase") +// } + +// // and we can validate the signature with publically available info +// binfo, _ := cstore.Get("Bob") +// sigs, err := tx.Signers() +// if err != nil { +// fmt.Println("badly signed") +// } else if bytes.Equal(sigs[0].Bytes(), binfo.PubKey.Bytes()) { +// fmt.Println("signed by Bob") +// } else { +// fmt.Println("signed by someone else") +// } + +// // Output: +// // Alice +// // Bob +// // Carl +// // signed by Bob +// } diff --git a/cryptostore/storage_test.go b/cryptostore/storage_test.go new file mode 100644 index 000000000..6c6ea5e2c --- /dev/null +++ b/cryptostore/storage_test.go @@ -0,0 +1,41 @@ +package cryptostore + +import ( + "testing" + + "github.com/stretchr/testify/assert" + keys "github.com/tendermint/go-keys" +) + +func TestSortKeys(t *testing.T) { + assert := assert.New(t) + + gen := GenEd25519.Generate + assert.NotEqual(gen(), gen()) + + // alphabetical order is n3, n1, n2 + n1, n2, n3 := "john", "mike", "alice" + infos := keys.KeyInfos{ + info(n1, gen()), + info(n2, gen()), + info(n3, gen()), + } + + // make sure they are initialized unsorted + assert.Equal(n1, infos[0].Name) + assert.Equal(n2, infos[1].Name) + assert.Equal(n3, infos[2].Name) + + // now they are sorted + infos.Sort() + assert.Equal(n3, infos[0].Name) + assert.Equal(n1, infos[1].Name) + assert.Equal(n2, infos[2].Name) + + // make sure info put some real data there... + assert.NotEmpty(infos[0].PubKey) + assert.NotEmpty(infos[0].PubKey.Address()) + assert.NotEmpty(infos[1].PubKey) + assert.NotEmpty(infos[1].PubKey.Address()) + assert.NotEqual(infos[0].PubKey, infos[1].PubKey) +} diff --git a/keys.toml b/keys.toml new file mode 100644 index 000000000..41f4d5286 --- /dev/null +++ b/keys.toml @@ -0,0 +1 @@ +format = "text" diff --git a/keys.yaml b/keys.yaml deleted file mode 100644 index 812adb72a..000000000 --- a/keys.yaml +++ /dev/null @@ -1 +0,0 @@ -name: george diff --git a/storage.go b/storage.go new file mode 100644 index 000000000..519ce1aa8 --- /dev/null +++ b/storage.go @@ -0,0 +1,10 @@ +package keys + +// Storage has many implementation, based on security and sharing requirements +// like disk-backed, mem-backed, vault, db, etc. +type Storage interface { + Put(name string, key []byte, info KeyInfo) error + Get(name string) ([]byte, KeyInfo, error) + List() ([]KeyInfo, error) + Delete(name string) error +} diff --git a/storage/filestorage/main.go b/storage/filestorage/main.go new file mode 100644 index 000000000..554a36540 --- /dev/null +++ b/storage/filestorage/main.go @@ -0,0 +1,171 @@ +/* +package filestorage provides a secure on-disk storage of private keys and +metadata. Security is enforced by file and directory permissions, much +like standard ssh key storage. +*/ +package filestorage + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "strings" + + "github.com/pkg/errors" + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-keys" +) + +const ( + BlockType = "Tendermint Light Client" + PrivExt = "tlc" + PubExt = "pub" + keyPerm = os.FileMode(0600) + pubPerm = os.FileMode(0644) + dirPerm = os.FileMode(0700) +) + +type FileStore struct { + keyDir string +} + +// New creates an instance of file-based key storage with tight permissions +// +// dir should be an absolute path of a directory owner by this user. It will +// be created if it doesn't exist already. +func New(dir string) FileStore { + err := os.Mkdir(dir, dirPerm) + if err != nil && !os.IsExist(err) { + panic(err) + } + return FileStore{dir} +} + +// assertStorage just makes sure we implement the proper Storage interface +func (s FileStore) assertStorage() keys.Storage { + return s +} + +// Put creates two files, one with the public info as json, the other +// with the (encoded) private key as gpg ascii-armor style +func (s FileStore) Put(name string, key []byte, info keys.KeyInfo) error { + pub, priv := s.nameToPaths(name) + + // write public info + err := writeInfo(pub, info) + if err != nil { + return err + } + + // write private info + return write(priv, name, key) +} + +// Get loads the keyinfo and (encoded) private key from the directory +// It uses `name` to generate the filename, and returns an error if the +// files don't exist or are in the incorrect format +func (s FileStore) Get(name string) ([]byte, keys.KeyInfo, error) { + pub, priv := s.nameToPaths(name) + + info, err := readInfo(pub) + if err != nil { + return nil, info, err + } + + key, _, err := read(priv) + return key, info, err +} + +// List parses the key directory for public info and returns a list of +// KeyInfo for all keys located in this directory. +func (s FileStore) List() ([]keys.KeyInfo, error) { + dir, err := os.Open(s.keyDir) + if err != nil { + return nil, errors.Wrap(err, "List Keys") + } + names, err := dir.Readdirnames(0) + if err != nil { + return nil, errors.Wrap(err, "List Keys") + } + + // filter names for .pub ending and load them one by one + // half the files is a good guess for pre-allocating the slice + infos := make([]keys.KeyInfo, 0, len(names)/2) + for _, name := range names { + if strings.HasSuffix(name, PubExt) { + p := path.Join(s.keyDir, name) + info, err := readInfo(p) + if err != nil { + return nil, err + } + infos = append(infos, info) + } + } + + return infos, nil +} + +// Delete permanently removes the public and private info for the named key +// The calling function should provide some security checks first. +func (s FileStore) Delete(name string) error { + pub, priv := s.nameToPaths(name) + err := os.Remove(priv) + if err != nil { + return errors.Wrap(err, "Deleting Private Key") + } + err = os.Remove(pub) + return errors.Wrap(err, "Deleting Public Key") +} + +func (s FileStore) nameToPaths(name string) (pub, priv string) { + privName := fmt.Sprintf("%s.%s", name, PrivExt) + pubName := fmt.Sprintf("%s.%s", name, PubExt) + return path.Join(s.keyDir, pubName), path.Join(s.keyDir, privName) +} + +func writeInfo(path string, info keys.KeyInfo) error { + return write(path, info.Name, info.PubKey.Bytes()) +} + +func readInfo(path string) (info keys.KeyInfo, err error) { + var data []byte + data, info.Name, err = read(path) + if err != nil { + return + } + pk, err := crypto.PubKeyFromBytes(data) + info.PubKey = crypto.PubKeyS{pk} + return +} + +func read(path string) ([]byte, string, error) { + f, err := os.Open(path) + if err != nil { + return nil, "", errors.Wrap(err, "Reading data") + } + d, err := ioutil.ReadAll(f) + if err != nil { + return nil, "", errors.Wrap(err, "Reading data") + } + block, headers, key, err := crypto.DecodeArmor(string(d)) + if err != nil { + return nil, "", errors.Wrap(err, "Invalid Armor") + } + if block != BlockType { + return nil, "", errors.Errorf("Unknown key type: %s", block) + } + return key, headers["name"], nil +} + +func write(path, name string, key []byte) error { + f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, keyPerm) + if err != nil { + return errors.Wrap(err, "Writing data") + } + defer f.Close() + headers := map[string]string{"name": name} + text := crypto.EncodeArmor(BlockType, headers, key) + _, err = f.WriteString(text) + return errors.Wrap(err, "Writing data") +} diff --git a/storage/filestorage/main_test.go b/storage/filestorage/main_test.go new file mode 100644 index 000000000..f81ba7198 --- /dev/null +++ b/storage/filestorage/main_test.go @@ -0,0 +1,95 @@ +package filestorage + +import ( + "io/ioutil" + "os" + "path" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-keys" +) + +func TestBasicCRUD(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + dir, err := ioutil.TempDir("", "filestorage-test") + assert.Nil(err) + defer os.RemoveAll(dir) + store := New(dir) + + name := "bar" + key := []byte("secret-key-here") + pubkey := crypto.GenPrivKeyEd25519().PubKey() + info := keys.KeyInfo{ + Name: name, + PubKey: crypto.PubKeyS{pubkey}, + } + + // No data: Get and Delete return nothing + _, _, err = store.Get(name) + assert.NotNil(err) + err = store.Delete(name) + assert.NotNil(err) + // List returns empty list + l, err := store.List() + assert.Nil(err) + assert.Empty(l) + + // Putting the key in the store must work + err = store.Put(name, key, info) + assert.Nil(err) + // But a second time is a failure + err = store.Put(name, key, info) + assert.NotNil(err) + + // Now, we can get and list properly + k, i, err := store.Get(name) + require.Nil(err, "%+v", err) + assert.Equal(key, k) + assert.Equal(info, i) + l, err = store.List() + require.Nil(err, "%+v", err) + assert.Equal(1, len(l)) + assert.Equal(info, l[0]) + + // querying a non-existent key fails + _, _, err = store.Get("badname") + assert.NotNil(err) + + // We can only delete once + err = store.Delete(name) + assert.Nil(err) + err = store.Delete(name) + assert.NotNil(err) + + // and then Get and List don't work + _, _, err = store.Get(name) + assert.NotNil(err) + // List returns empty list + l, err = store.List() + assert.Nil(err) + assert.Empty(l) +} + +func TestDirectoryHandling(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // prepare a temp dir and make sure it is not there + newDir := path.Join(os.TempDir(), "file-test-dir") + _, err := os.Open(newDir) + assert.True(os.IsNotExist(err)) + + // create a new storage, and verify it creates the directory with good permissions + New(newDir) + defer os.RemoveAll(newDir) + d, err := os.Open(newDir) + require.Nil(err) + defer d.Close() + + stat, err := d.Stat() + require.Nil(err) + assert.Equal(dirPerm, stat.Mode()&os.ModePerm) +} diff --git a/storage/memstorage/main.go b/storage/memstorage/main.go new file mode 100644 index 000000000..e6573eb9f --- /dev/null +++ b/storage/memstorage/main.go @@ -0,0 +1,70 @@ +/* +package memstorage provides a simple in-memory key store designed for +use in test cases, particularly to isolate them from the filesystem, +concurrency, and cleanup issues. +*/ +package memstorage + +import ( + "github.com/pkg/errors" + keys "github.com/tendermint/go-keys" +) + +type data struct { + info keys.KeyInfo + key []byte +} + +type MemStore map[string]data + +// New creates an instance of file-based key storage with tight permissions +func New() MemStore { + return MemStore{} +} + +// assertStorage just makes sure we implement the Storage interface +func (s MemStore) assertStorage() keys.Storage { + return s +} + +// Put adds the given key, returns an error if it another key +// is already stored under this name +func (s MemStore) Put(name string, key []byte, info keys.KeyInfo) error { + if _, ok := s[name]; ok { + return errors.Errorf("Key named '%s' already exists", name) + } + s[name] = data{info, key} + return nil +} + +// Get returns the key stored under the name, or returns an error if not present +func (s MemStore) Get(name string) ([]byte, keys.KeyInfo, error) { + var err error + d, ok := s[name] + if !ok { + err = errors.Errorf("Key named '%s' doesn't exist", name) + } + return d.key, d.info, err +} + +// List returns the public info of all keys in the MemStore in unsorted order +func (s MemStore) List() ([]keys.KeyInfo, error) { + res := make([]keys.KeyInfo, len(s)) + i := 0 + for _, d := range s { + res[i] = d.info + i++ + } + return res, nil +} + +// Delete removes the named key from the MemStore, raising an error if it +// wasn't present yet. +func (s MemStore) Delete(name string) error { + _, ok := s[name] + if !ok { + return errors.Errorf("Key named '%s' doesn't exist", name) + } + delete(s, name) + return nil +} diff --git a/storage/memstorage/main_test.go b/storage/memstorage/main_test.go new file mode 100644 index 000000000..01c015a15 --- /dev/null +++ b/storage/memstorage/main_test.go @@ -0,0 +1,67 @@ +package memstorage + +import ( + "testing" + + "github.com/stretchr/testify/assert" + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-keys" +) + +func TestBasicCRUD(t *testing.T) { + assert := assert.New(t) + store := New() + + name := "foo" + key := []byte("secret-key-here") + pubkey := crypto.GenPrivKeyEd25519().PubKey() + info := keys.KeyInfo{ + Name: name, + PubKey: crypto.PubKeyS{pubkey}, + } + + // No data: Get and Delete return nothing + _, _, err := store.Get(name) + assert.NotNil(err) + err = store.Delete(name) + assert.NotNil(err) + // List returns empty list + l, err := store.List() + assert.Nil(err) + assert.Empty(l) + + // Putting the key in the store must work + err = store.Put(name, key, info) + assert.Nil(err) + // But a second time is a failure + err = store.Put(name, key, info) + assert.NotNil(err) + + // Now, we can get and list properly + k, i, err := store.Get(name) + assert.Nil(err) + assert.Equal(key, k) + assert.Equal(info, i) + l, err = store.List() + assert.Nil(err) + assert.Equal(1, len(l)) + assert.Equal(info, l[0]) + + // querying a non-existent key fails + _, _, err = store.Get("badname") + assert.NotNil(err) + + // We can only delete once + err = store.Delete(name) + assert.Nil(err) + err = store.Delete(name) + assert.NotNil(err) + + // and then Get and List don't work + _, _, err = store.Get(name) + assert.NotNil(err) + // List returns empty list + l, err = store.List() + assert.Nil(err) + assert.Empty(l) +} diff --git a/transactions.go b/transactions.go new file mode 100644 index 000000000..f22719f7e --- /dev/null +++ b/transactions.go @@ -0,0 +1,61 @@ +package keys + +import ( + "sort" + + crypto "github.com/tendermint/go-crypto" +) + +// KeyInfo is the public information about a key +type KeyInfo struct { + Name string + PubKey crypto.PubKeyS +} + +// KeyInfos is a wrapper to allows alphabetical sorting of the keys +type KeyInfos []KeyInfo + +func (k KeyInfos) Len() int { return len(k) } +func (k KeyInfos) Less(i, j int) bool { return k[i].Name < k[j].Name } +func (k KeyInfos) Swap(i, j int) { k[i], k[j] = k[j], k[i] } +func (k KeyInfos) Sort() { + if k != nil { + sort.Sort(k) + } +} + +// Signable represents any transaction we wish to send to tendermint core +// These methods allow us to sign arbitrary Tx with the KeyStore +type Signable interface { + // SignBytes is the immutable data, which needs to be signed + SignBytes() []byte + + // Sign will add a signature and pubkey. + // + // Depending on the Signable, one may be able to call this multiple times for multisig + // Returns error if called with invalid data or too many times + Sign(pubkey crypto.PubKey, sig crypto.Signature) error + + // Signers will return the public key(s) that signed if the signature + // is valid, or an error if there is any issue with the signature, + // including if there are no signatures + Signers() ([]crypto.PubKey, error) + + // TxBytes returns the transaction data as well as all signatures + // It should return an error if Sign was never called + TxBytes() ([]byte, error) +} + +// Signer allows one to use a keystore to sign transactions +type Signer interface { + Sign(name, passphrase string, tx Signable) error +} + +// KeyManager allows simple CRUD on a keystore, as an aid to signing +type KeyManager interface { + Create(name, passphrase string) error + List() (KeyInfos, error) + Get(name string) (KeyInfo, error) + Update(name, oldpass, newpass string) error + Delete(name, passphrase string) error +} From 506ff7d85a09b03b2dc726d7a4b466e325c55ec2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 18:52:52 +0100 Subject: [PATCH 04/45] Expose new and list via cli --- Makefile | 3 ++ cmd/list.go | 21 ++++-------- cmd/new.go | 42 +++++++++++++++++------- cmd/root.go | 33 +++++++++++++++---- cmd/utils.go | 56 ++++++++++++++++++++++++++++++++ cryptostore/enc_storage.go | 8 ++--- cryptostore/holder.go | 16 ++++----- cryptostore/holder_test.go | 7 ++-- cryptostore/storage_test.go | 2 +- keys.toml | 3 +- storage.go | 6 ++-- storage/filestorage/main.go | 16 ++++----- storage/filestorage/main_test.go | 2 +- storage/memstorage/main.go | 10 +++--- storage/memstorage/main_test.go | 2 +- transactions.go | 26 +++++++-------- 16 files changed, 173 insertions(+), 80 deletions(-) create mode 100644 cmd/utils.go diff --git a/Makefile b/Makefile index 7a1f90fd4..b15317b25 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,5 @@ test: go test ./... + +install: + go install ./cmd/keys diff --git a/cmd/list.go b/cmd/list.go index e2c8a7631..44fefb8d6 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -18,7 +18,6 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" ) // listCmd represents the list command @@ -28,22 +27,16 @@ var listCmd = &cobra.Command{ Long: `Return a list of all public keys stored by this key manager along with their associated name and address.`, Run: func(cmd *cobra.Command, args []string) { - // TODO: Work your own magic here - fmt.Println("list called") - fmt.Println(viper.Get("format")) + infos, err := manager.List() + if err != nil { + fmt.Println(err.Error()) + return + } + + printInfos(infos) }, } func init() { RootCmd.AddCommand(listCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // listCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - listCmd.Flags().StringP("format", "f", "text", "Format to display (text|json)") } diff --git a/cmd/new.go b/cmd/new.go index 40462e686..38d9762f9 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -22,28 +22,46 @@ import ( // newCmd represents the new command var newCmd = &cobra.Command{ - Use: "new", + Use: "new ", 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.`, - Run: func(cmd *cobra.Command, args []string) { - // TODO: Work your own magic here - fmt.Println("new called") - }, + Run: newPassword, } func init() { RootCmd.AddCommand(newCmd) +} - // Here you will define your flags and configuration settings. +func newPassword(cmd *cobra.Command, args []string) { + if len(args) != 1 || len(args[0]) == 0 { + fmt.Print("You must provide a name for the key") + return + } + name := args[0] - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // newCmd.PersistentFlags().String("foo", "", "A help for foo") + // TODO: own function??? + pass, err := getPassword("Enter a passphrase:") + if err != nil { + fmt.Println(err.Error()) + return + } + pass2, err := getPassword("Repeat the passphrase:") + if err != nil { + fmt.Println(err.Error()) + return + } + if pass != pass2 { + fmt.Println("Passphrases don't match") + return + } - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // newCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + info, err := manager.Create(name, pass) + if err != nil { + fmt.Println(err.Error()) + return + } + printInfo(info) } diff --git a/cmd/root.go b/cmd/root.go index 05fffd489..9d7ae9a9e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,16 +17,22 @@ package cmd import ( "fmt" "os" + "path/filepath" "strings" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + keys "github.com/tendermint/go-keys" + "github.com/tendermint/go-keys/cryptostore" + "github.com/tendermint/go-keys/storage/filestorage" ) var ( rootDir string - format string + output string + keyDir string + manager keys.Manager ) // RootCmd represents the base command when called without any subcommands @@ -53,7 +59,8 @@ func Execute() { func init() { cobra.OnInitialize(initEnv) RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data (default is TM_ROOT or $HOME/.tlc)") - RootCmd.PersistentFlags().StringP("format", "f", "text", "Output format (text|json)") + RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)") + RootCmd.PersistentFlags().StringP("keydir", "", "keys", "Directory to store private keys (subdir of root)") } // initEnv sets to use ENV variables if set. @@ -85,11 +92,25 @@ func bindFlags(cmd *cobra.Command, args []string) error { // validateFlags asserts all RootCmd flags are valid func validateFlags(cmd *cobra.Command) error { - format = viper.GetString("format") - switch format { + // validate output format + output = viper.GetString("output") + switch output { case "text", "json": - return nil default: - return errors.Errorf("Unsupported format: %s", format) + return errors.Errorf("Unsupported output format: %s", output) } + + // store the keys directory + keyDir = viper.GetString("keydir") + if !filepath.IsAbs(keyDir) { + keyDir = filepath.Join(rootDir, keyDir) + } + // and construct the key manager + manager = cryptostore.New( + cryptostore.GenEd25519, // TODO - cli switch??? + cryptostore.SecretBox, + filestorage.New(keyDir), + ) + + return nil } diff --git a/cmd/utils.go b/cmd/utils.go new file mode 100644 index 000000000..bd067f9ca --- /dev/null +++ b/cmd/utils.go @@ -0,0 +1,56 @@ +package cmd + +import ( + "fmt" + + "github.com/bgentry/speakeasy" + "github.com/pkg/errors" + data "github.com/tendermint/go-data" + keys "github.com/tendermint/go-keys" +) + +const PassLength = 10 + +func getPassword(prompt string) (string, error) { + pass, err := speakeasy.Ask(prompt) + if err != nil { + return "", err + } + if len(pass) < PassLength { + return "", errors.Errorf("Password must be at least %d characters", PassLength) + } + return pass, nil +} + +func printInfo(info keys.Info) { + switch output { + case "text": + key, err := data.ToText(info.PubKey) + if err != nil { + panic(err) // really shouldn't happen... + } + fmt.Printf("%s\t%s\n", info.Name, key) + 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 output { + 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)) + } +} diff --git a/cryptostore/enc_storage.go b/cryptostore/enc_storage.go index 0a935075f..759ca90e9 100644 --- a/cryptostore/enc_storage.go +++ b/cryptostore/enc_storage.go @@ -21,7 +21,7 @@ func (es encryptedStorage) Put(name, pass string, key crypto.PrivKey) error { return es.store.Put(name, secret, ki) } -func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.KeyInfo, error) { +func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.Info, error) { secret, info, err := es.store.Get(name) if err != nil { return nil, info, err @@ -30,7 +30,7 @@ func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.KeyInfo, return key, info, err } -func (es encryptedStorage) List() ([]keys.KeyInfo, error) { +func (es encryptedStorage) List() (keys.Infos, error) { return es.store.List() } @@ -39,8 +39,8 @@ func (es encryptedStorage) Delete(name string) error { } // info hardcodes the encoding of keys -func info(name string, key crypto.PrivKey) keys.KeyInfo { - return keys.KeyInfo{ +func info(name string, key crypto.PrivKey) keys.Info { + return keys.Info{ Name: name, PubKey: crypto.PubKeyS{key.PubKey()}, } diff --git a/cryptostore/holder.go b/cryptostore/holder.go index 4beb64160..29682a87d 100644 --- a/cryptostore/holder.go +++ b/cryptostore/holder.go @@ -24,28 +24,28 @@ func (s Manager) assertSigner() keys.Signer { return s } -// exists just to make sure we fulfill the KeyManager interface -func (s Manager) assertKeyManager() keys.KeyManager { +// exists just to make sure we fulfill the Manager interface +func (s Manager) assertKeyManager() keys.Manager { return s } // Create adds a new key to the storage engine, returning error if // another key already stored under this name -func (s Manager) Create(name, passphrase string) error { +func (s Manager) Create(name, passphrase string) (keys.Info, error) { key := s.gen.Generate() - return s.es.Put(name, passphrase, key) + err := s.es.Put(name, passphrase, key) + return info(name, key), err } // List loads the keys from the storage and enforces alphabetical order -func (s Manager) List() (keys.KeyInfos, error) { - k, err := s.es.List() - res := keys.KeyInfos(k) +func (s Manager) List() (keys.Infos, error) { + res, err := s.es.List() res.Sort() return res, err } // Get returns the public information about one key -func (s Manager) Get(name string) (keys.KeyInfo, error) { +func (s Manager) Get(name string) (keys.Info, error) { _, info, err := s.es.store.Get(name) return info, err } diff --git a/cryptostore/holder_test.go b/cryptostore/holder_test.go index fb5ef854f..116b92c4e 100644 --- a/cryptostore/holder_test.go +++ b/cryptostore/holder_test.go @@ -31,9 +31,10 @@ func TestKeyManagement(t *testing.T) { // create some keys _, err = cstore.Get(n1) assert.NotNil(err) - err = cstore.Create(n1, p1) + i, err := cstore.Create(n1, p1) + require.Equal(n1, i.Name) require.Nil(err) - err = cstore.Create(n2, p2) + _, err = cstore.Create(n2, p2) require.Nil(err) // we can get these keys @@ -159,7 +160,7 @@ func TestAdvancedKeyManagement(t *testing.T) { p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$" // make sure key works with initial password - err := cstore.Create(n1, p1) + _, err := cstore.Create(n1, p1) require.Nil(err, "%+v", err) assertPassword(assert, cstore, n1, p1, p2) diff --git a/cryptostore/storage_test.go b/cryptostore/storage_test.go index 6c6ea5e2c..78d107c11 100644 --- a/cryptostore/storage_test.go +++ b/cryptostore/storage_test.go @@ -15,7 +15,7 @@ func TestSortKeys(t *testing.T) { // alphabetical order is n3, n1, n2 n1, n2, n3 := "john", "mike", "alice" - infos := keys.KeyInfos{ + infos := keys.Infos{ info(n1, gen()), info(n2, gen()), info(n3, gen()), diff --git a/keys.toml b/keys.toml index 41f4d5286..f9eb95e1c 100644 --- a/keys.toml +++ b/keys.toml @@ -1 +1,2 @@ -format = "text" +output = "text" +keydir = ".mykeys" diff --git a/storage.go b/storage.go index 519ce1aa8..0c25eb8a5 100644 --- a/storage.go +++ b/storage.go @@ -3,8 +3,8 @@ package keys // Storage has many implementation, based on security and sharing requirements // like disk-backed, mem-backed, vault, db, etc. type Storage interface { - Put(name string, key []byte, info KeyInfo) error - Get(name string) ([]byte, KeyInfo, error) - List() ([]KeyInfo, error) + Put(name string, key []byte, info Info) error + Get(name string) ([]byte, Info, error) + List() (Infos, error) Delete(name string) error } diff --git a/storage/filestorage/main.go b/storage/filestorage/main.go index 554a36540..0396c9532 100644 --- a/storage/filestorage/main.go +++ b/storage/filestorage/main.go @@ -49,7 +49,7 @@ func (s FileStore) assertStorage() keys.Storage { // Put creates two files, one with the public info as json, the other // with the (encoded) private key as gpg ascii-armor style -func (s FileStore) Put(name string, key []byte, info keys.KeyInfo) error { +func (s FileStore) Put(name string, key []byte, info keys.Info) error { pub, priv := s.nameToPaths(name) // write public info @@ -62,10 +62,10 @@ func (s FileStore) Put(name string, key []byte, info keys.KeyInfo) error { return write(priv, name, key) } -// Get loads the keyinfo and (encoded) private key from the directory +// Get loads the info and (encoded) private key from the directory // It uses `name` to generate the filename, and returns an error if the // files don't exist or are in the incorrect format -func (s FileStore) Get(name string) ([]byte, keys.KeyInfo, error) { +func (s FileStore) Get(name string) ([]byte, keys.Info, error) { pub, priv := s.nameToPaths(name) info, err := readInfo(pub) @@ -78,8 +78,8 @@ func (s FileStore) Get(name string) ([]byte, keys.KeyInfo, error) { } // List parses the key directory for public info and returns a list of -// KeyInfo for all keys located in this directory. -func (s FileStore) List() ([]keys.KeyInfo, error) { +// Info for all keys located in this directory. +func (s FileStore) List() (keys.Infos, error) { dir, err := os.Open(s.keyDir) if err != nil { return nil, errors.Wrap(err, "List Keys") @@ -91,7 +91,7 @@ func (s FileStore) List() ([]keys.KeyInfo, error) { // filter names for .pub ending and load them one by one // half the files is a good guess for pre-allocating the slice - infos := make([]keys.KeyInfo, 0, len(names)/2) + infos := make([]keys.Info, 0, len(names)/2) for _, name := range names { if strings.HasSuffix(name, PubExt) { p := path.Join(s.keyDir, name) @@ -124,11 +124,11 @@ func (s FileStore) nameToPaths(name string) (pub, priv string) { return path.Join(s.keyDir, pubName), path.Join(s.keyDir, privName) } -func writeInfo(path string, info keys.KeyInfo) error { +func writeInfo(path string, info keys.Info) error { return write(path, info.Name, info.PubKey.Bytes()) } -func readInfo(path string) (info keys.KeyInfo, err error) { +func readInfo(path string) (info keys.Info, err error) { var data []byte data, info.Name, err = read(path) if err != nil { diff --git a/storage/filestorage/main_test.go b/storage/filestorage/main_test.go index f81ba7198..b27fecfb7 100644 --- a/storage/filestorage/main_test.go +++ b/storage/filestorage/main_test.go @@ -23,7 +23,7 @@ func TestBasicCRUD(t *testing.T) { name := "bar" key := []byte("secret-key-here") pubkey := crypto.GenPrivKeyEd25519().PubKey() - info := keys.KeyInfo{ + info := keys.Info{ Name: name, PubKey: crypto.PubKeyS{pubkey}, } diff --git a/storage/memstorage/main.go b/storage/memstorage/main.go index e6573eb9f..9f671e484 100644 --- a/storage/memstorage/main.go +++ b/storage/memstorage/main.go @@ -11,7 +11,7 @@ import ( ) type data struct { - info keys.KeyInfo + info keys.Info key []byte } @@ -29,7 +29,7 @@ func (s MemStore) assertStorage() keys.Storage { // Put adds the given key, returns an error if it another key // is already stored under this name -func (s MemStore) Put(name string, key []byte, info keys.KeyInfo) error { +func (s MemStore) Put(name string, key []byte, info keys.Info) error { if _, ok := s[name]; ok { return errors.Errorf("Key named '%s' already exists", name) } @@ -38,7 +38,7 @@ func (s MemStore) Put(name string, key []byte, info keys.KeyInfo) error { } // Get returns the key stored under the name, or returns an error if not present -func (s MemStore) Get(name string) ([]byte, keys.KeyInfo, error) { +func (s MemStore) Get(name string) ([]byte, keys.Info, error) { var err error d, ok := s[name] if !ok { @@ -48,8 +48,8 @@ func (s MemStore) Get(name string) ([]byte, keys.KeyInfo, error) { } // List returns the public info of all keys in the MemStore in unsorted order -func (s MemStore) List() ([]keys.KeyInfo, error) { - res := make([]keys.KeyInfo, len(s)) +func (s MemStore) List() (keys.Infos, error) { + res := make([]keys.Info, len(s)) i := 0 for _, d := range s { res[i] = d.info diff --git a/storage/memstorage/main_test.go b/storage/memstorage/main_test.go index 01c015a15..2863bffad 100644 --- a/storage/memstorage/main_test.go +++ b/storage/memstorage/main_test.go @@ -15,7 +15,7 @@ func TestBasicCRUD(t *testing.T) { name := "foo" key := []byte("secret-key-here") pubkey := crypto.GenPrivKeyEd25519().PubKey() - info := keys.KeyInfo{ + info := keys.Info{ Name: name, PubKey: crypto.PubKeyS{pubkey}, } diff --git a/transactions.go b/transactions.go index f22719f7e..13f38b41c 100644 --- a/transactions.go +++ b/transactions.go @@ -6,19 +6,19 @@ import ( crypto "github.com/tendermint/go-crypto" ) -// KeyInfo is the public information about a key -type KeyInfo struct { +// Info is the public information about a key +type Info struct { Name string PubKey crypto.PubKeyS } -// KeyInfos is a wrapper to allows alphabetical sorting of the keys -type KeyInfos []KeyInfo +// Infos is a wrapper to allows alphabetical sorting of the keys +type Infos []Info -func (k KeyInfos) Len() int { return len(k) } -func (k KeyInfos) Less(i, j int) bool { return k[i].Name < k[j].Name } -func (k KeyInfos) Swap(i, j int) { k[i], k[j] = k[j], k[i] } -func (k KeyInfos) Sort() { +func (k Infos) Len() int { return len(k) } +func (k Infos) Less(i, j int) bool { return k[i].Name < k[j].Name } +func (k Infos) Swap(i, j int) { k[i], k[j] = k[j], k[i] } +func (k Infos) Sort() { if k != nil { sort.Sort(k) } @@ -51,11 +51,11 @@ type Signer interface { Sign(name, passphrase string, tx Signable) error } -// KeyManager allows simple CRUD on a keystore, as an aid to signing -type KeyManager interface { - Create(name, passphrase string) error - List() (KeyInfos, error) - Get(name string) (KeyInfo, error) +// Manager allows simple CRUD on a keystore, as an aid to signing +type Manager interface { + Create(name, passphrase string) (Info, error) + List() (Infos, error) + Get(name string) (Info, error) Update(name, oldpass, newpass string) error Delete(name, passphrase string) error } From 9c427e95e224e8e375c586e5cbeafe17fa4a1ce6 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 19:02:24 +0100 Subject: [PATCH 05/45] Add update cli command, cleanup --- cmd/new.go | 14 ++------------ cmd/update.go | 34 +++++++++++++++++++++++++++------- cmd/utils.go | 16 ++++++++++++++++ 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/cmd/new.go b/cmd/new.go index 38d9762f9..03e8fd2ce 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -36,26 +36,16 @@ func init() { func newPassword(cmd *cobra.Command, args []string) { if len(args) != 1 || len(args[0]) == 0 { - fmt.Print("You must provide a name for the key") + fmt.Println("You must provide a name for the key") return } name := args[0] - // TODO: own function??? - pass, err := getPassword("Enter a passphrase:") + pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:") if err != nil { fmt.Println(err.Error()) return } - pass2, err := getPassword("Repeat the passphrase:") - if err != nil { - fmt.Println(err.Error()) - return - } - if pass != pass2 { - fmt.Println("Passphrases don't match") - return - } info, err := manager.Create(name, pass) if err != nil { diff --git a/cmd/update.go b/cmd/update.go index c36bcf25a..9b5387cb9 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -18,7 +18,6 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" ) // updateCmd represents the update command @@ -26,14 +25,35 @@ var updateCmd = &cobra.Command{ Use: "update", Short: "Change the password for a private key", Long: `Change the password for a private key.`, - Run: func(cmd *cobra.Command, args []string) { - // TODO: Work your own magic here - fmt.Println(viper.Get("name")) - fmt.Println("update called") - }, + Run: updatePassword, } func init() { RootCmd.AddCommand(updateCmd) - updateCmd.Flags().StringP("name", "n", "", "Name of key to update") +} + +func updatePassword(cmd *cobra.Command, args []string) { + if len(args) != 1 || len(args[0]) == 0 { + fmt.Println("You must provide a name for the key") + return + } + name := args[0] + + oldpass, err := getPassword("Enter the current passphrase:") + if err != nil { + fmt.Println(err.Error()) + return + } + newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") + if err != nil { + fmt.Println(err.Error()) + return + } + + err = manager.Update(name, oldpass, newpass) + if err != nil { + fmt.Println(err.Error()) + } else { + fmt.Println("Password successfully updated!") + } } diff --git a/cmd/utils.go b/cmd/utils.go index bd067f9ca..9a62fe7e8 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -22,6 +22,22 @@ func getPassword(prompt string) (string, error) { return pass, nil } +func getCheckPassword(prompt, prompt2 string) (string, error) { + // 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 output { case "text": From d5931c9ee39c1482492279cbdc242456bbbc5453 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 19:43:18 +0100 Subject: [PATCH 06/45] Set key algorithm on key creation --- .gitignore | 1 + cmd/README.md | 76 ++++++++++++++++++++++++++++++++++++++ cmd/new.go | 5 ++- cmd/root.go | 5 +-- cryptostore/generator.go | 16 +++++++- cryptostore/holder.go | 19 ++++++---- cryptostore/holder_test.go | 11 +++--- transactions.go | 2 +- 8 files changed, 117 insertions(+), 18 deletions(-) create mode 100644 .gitignore create mode 100644 cmd/README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..cfaef8179 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.mykeys diff --git a/cmd/README.md b/cmd/README.md new file mode 100644 index 000000000..97ea787c9 --- /dev/null +++ b/cmd/README.md @@ -0,0 +1,76 @@ +# 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: + 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. + +## 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. diff --git a/cmd/new.go b/cmd/new.go index 03e8fd2ce..5ac0d3b90 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -18,6 +18,7 @@ import ( "fmt" "github.com/spf13/cobra" + "github.com/spf13/viper" ) // newCmd represents the new command @@ -32,6 +33,7 @@ passed as a command line argument for security.`, func init() { RootCmd.AddCommand(newCmd) + newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1") } func newPassword(cmd *cobra.Command, args []string) { @@ -40,6 +42,7 @@ func newPassword(cmd *cobra.Command, args []string) { return } name := args[0] + algo := viper.GetString("type") pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:") if err != nil { @@ -47,7 +50,7 @@ func newPassword(cmd *cobra.Command, args []string) { return } - info, err := manager.Create(name, pass) + info, err := manager.Create(name, pass, algo) if err != nil { fmt.Println(err.Error()) return diff --git a/cmd/root.go b/cmd/root.go index 9d7ae9a9e..7f5122af2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -58,9 +58,9 @@ func Execute() { func init() { cobra.OnInitialize(initEnv) - RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data (default is TM_ROOT or $HOME/.tlc)") + RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data") RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)") - RootCmd.PersistentFlags().StringP("keydir", "", "keys", "Directory to store private keys (subdir of root)") + RootCmd.PersistentFlags().String("keydir", "keys", "Directory to store private keys (subdir of root)") } // initEnv sets to use ENV variables if set. @@ -107,7 +107,6 @@ func validateFlags(cmd *cobra.Command) error { } // and construct the key manager manager = cryptostore.New( - cryptostore.GenEd25519, // TODO - cli switch??? cryptostore.SecretBox, filestorage.New(keyDir), ) diff --git a/cryptostore/generator.go b/cryptostore/generator.go index c6661fb34..cf877f20b 100644 --- a/cryptostore/generator.go +++ b/cryptostore/generator.go @@ -1,6 +1,9 @@ package cryptostore -import crypto "github.com/tendermint/go-crypto" +import ( + "github.com/pkg/errors" + crypto "github.com/tendermint/go-crypto" +) var ( // GenEd25519 produces Ed25519 private keys @@ -28,3 +31,14 @@ func genEd25519() crypto.PrivKey { func genSecp256() crypto.PrivKey { return crypto.GenPrivKeySecp256k1() } + +func getGenerator(algo string) (Generator, error) { + switch algo { + case crypto.NameEd25519: + return GenEd25519, nil + case crypto.NameSecp256k1: + return GenSecp256k1, nil + default: + return nil, errors.Errorf("Cannot generate keys for algorithm: %s", algo) + } +} diff --git a/cryptostore/holder.go b/cryptostore/holder.go index 29682a87d..549326614 100644 --- a/cryptostore/holder.go +++ b/cryptostore/holder.go @@ -5,13 +5,11 @@ import keys "github.com/tendermint/go-keys" // Manager combines encyption and storage implementation to provide // a full-featured key manager type Manager struct { - gen Generator - es encryptedStorage + es encryptedStorage } -func New(gen Generator, coder Encoder, store keys.Storage) Manager { +func New(coder Encoder, store keys.Storage) Manager { return Manager{ - gen: gen, es: encryptedStorage{ coder: coder, store: store, @@ -31,9 +29,16 @@ func (s Manager) assertKeyManager() keys.Manager { // Create adds a new key to the storage engine, returning error if // another key already stored under this name -func (s Manager) Create(name, passphrase string) (keys.Info, error) { - key := s.gen.Generate() - err := s.es.Put(name, passphrase, key) +// +// algo must be a supported go-crypto algorithm: +// +func (s Manager) Create(name, passphrase, algo string) (keys.Info, error) { + gen, err := getGenerator(algo) + if err != nil { + return keys.Info{}, err + } + key := gen.Generate() + err = s.es.Put(name, passphrase, key) return info(name, key), err } diff --git a/cryptostore/holder_test.go b/cryptostore/holder_test.go index 116b92c4e..860869c86 100644 --- a/cryptostore/holder_test.go +++ b/cryptostore/holder_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-keys/cryptostore" "github.com/tendermint/go-keys/storage/memstorage" ) @@ -15,11 +16,11 @@ func TestKeyManagement(t *testing.T) { // make the storage with reasonable defaults cstore := cryptostore.New( - cryptostore.GenSecp256k1, cryptostore.SecretBox, memstorage.New(), ) + algo := crypto.NameEd25519 n1, n2, n3 := "personal", "business", "other" p1, p2 := "1234", "really-secure!@#$" @@ -31,10 +32,10 @@ func TestKeyManagement(t *testing.T) { // create some keys _, err = cstore.Get(n1) assert.NotNil(err) - i, err := cstore.Create(n1, p1) + i, err := cstore.Create(n1, p1, algo) require.Equal(n1, i.Name) require.Nil(err) - _, err = cstore.Create(n2, p2) + _, err = cstore.Create(n2, p2, algo) require.Nil(err) // we can get these keys @@ -151,16 +152,16 @@ func TestAdvancedKeyManagement(t *testing.T) { // make the storage with reasonable defaults cstore := cryptostore.New( - cryptostore.GenSecp256k1, cryptostore.SecretBox, memstorage.New(), ) + algo := crypto.NameSecp256k1 n1, n2 := "old-name", "new name" p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$" // make sure key works with initial password - _, err := cstore.Create(n1, p1) + _, err := cstore.Create(n1, p1, algo) require.Nil(err, "%+v", err) assertPassword(assert, cstore, n1, p1, p2) diff --git a/transactions.go b/transactions.go index 13f38b41c..3acb476cf 100644 --- a/transactions.go +++ b/transactions.go @@ -53,7 +53,7 @@ type Signer interface { // Manager allows simple CRUD on a keystore, as an aid to signing type Manager interface { - Create(name, passphrase string) (Info, error) + Create(name, passphrase, algo string) (Info, error) List() (Infos, error) Get(name string) (Info, error) Update(name, oldpass, newpass string) error From c59e2d7d13084fb4860fbe4b67db4bef4760e622 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 20:07:44 +0100 Subject: [PATCH 07/45] Expose address in keyinfo, add get command --- cmd/README.md | 7 ++++- cmd/get.go | 47 ++++++++++++++++++++++++++++++++ cmd/root.go | 3 +- cmd/update.go | 2 +- cmd/utils.go | 8 ++++-- cryptostore/enc_storage.go | 6 ++-- storage/filestorage/main.go | 4 +-- storage/filestorage/main_test.go | 6 ++-- storage/memstorage/main.go | 4 +-- storage/memstorage/main_test.go | 6 ++-- transactions.go | 13 +++++++-- 11 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 cmd/get.go diff --git a/cmd/README.md b/cmd/README.md index 97ea787c9..72f202bf1 100644 --- a/cmd/README.md +++ b/cmd/README.md @@ -10,6 +10,7 @@ the commands, and give feedback and changes. ``` # 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 @@ -20,6 +21,7 @@ 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 @@ -30,7 +32,8 @@ Flags: -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.``` +Use "keys [command] --help" for more information about a command. +``` ## Getting the config file @@ -42,6 +45,8 @@ The first step is to load in root, by checking the following in order: 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: diff --git a/cmd/get.go b/cmd/get.go new file mode 100644 index 000000000..d945809f9 --- /dev/null +++ b/cmd/get.go @@ -0,0 +1,47 @@ +// 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 ( + "fmt" + + "github.com/spf13/cobra" +) + +// getCmd represents the get command +var getCmd = &cobra.Command{ + Use: "get ", + Short: "Get details of one key", + Long: `Return public details of one local key.`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 1 || len(args[0]) == 0 { + fmt.Println("You must provide a name for the key") + return + } + name := args[0] + + info, err := manager.Get(name) + if err != nil { + fmt.Println(err.Error()) + return + } + + printInfo(info) + }, +} + +func init() { + RootCmd.AddCommand(getCmd) +} diff --git a/cmd/root.go b/cmd/root.go index 7f5122af2..029c37f9d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -84,7 +84,8 @@ func bindFlags(cmd *cobra.Command, args []string) error { // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) + // stderr, so if we redirect output to json file, this doesn't appear + fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) } return validateFlags(cmd) diff --git a/cmd/update.go b/cmd/update.go index 9b5387cb9..0340944be 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -22,7 +22,7 @@ import ( // updateCmd represents the update command var updateCmd = &cobra.Command{ - Use: "update", + Use: "update ", Short: "Change the password for a private key", Long: `Change the password for a private key.`, Run: updatePassword, diff --git a/cmd/utils.go b/cmd/utils.go index 9a62fe7e8..01f9801ae 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -41,11 +41,15 @@ func getCheckPassword(prompt, prompt2 string) (string, error) { func printInfo(info keys.Info) { switch output { case "text": - key, err := data.ToText(info.PubKey) + addr, err := data.ToText(info.Address) if err != nil { panic(err) // really shouldn't happen... } - fmt.Printf("%s\t%s\n", info.Name, key) + 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 { diff --git a/cryptostore/enc_storage.go b/cryptostore/enc_storage.go index 759ca90e9..9589f4cc1 100644 --- a/cryptostore/enc_storage.go +++ b/cryptostore/enc_storage.go @@ -40,8 +40,10 @@ func (es encryptedStorage) Delete(name string) error { // info hardcodes the encoding of keys func info(name string, key crypto.PrivKey) keys.Info { + pub := key.PubKey() return keys.Info{ - Name: name, - PubKey: crypto.PubKeyS{key.PubKey()}, + Name: name, + Address: pub.Address(), + PubKey: crypto.PubKeyS{pub}, } } diff --git a/storage/filestorage/main.go b/storage/filestorage/main.go index 0396c9532..4b7dbe525 100644 --- a/storage/filestorage/main.go +++ b/storage/filestorage/main.go @@ -74,7 +74,7 @@ func (s FileStore) Get(name string) ([]byte, keys.Info, error) { } key, _, err := read(priv) - return key, info, err + return key, info.Format(), err } // List parses the key directory for public info and returns a list of @@ -99,7 +99,7 @@ func (s FileStore) List() (keys.Infos, error) { if err != nil { return nil, err } - infos = append(infos, info) + infos = append(infos, info.Format()) } } diff --git a/storage/filestorage/main_test.go b/storage/filestorage/main_test.go index b27fecfb7..c9c55eb02 100644 --- a/storage/filestorage/main_test.go +++ b/storage/filestorage/main_test.go @@ -49,11 +49,13 @@ func TestBasicCRUD(t *testing.T) { k, i, err := store.Get(name) require.Nil(err, "%+v", err) assert.Equal(key, k) - assert.Equal(info, i) + assert.Equal(info.Name, i.Name) + assert.Equal(info.PubKey, i.PubKey) + assert.NotEmpty(i.Address) l, err = store.List() require.Nil(err, "%+v", err) assert.Equal(1, len(l)) - assert.Equal(info, l[0]) + assert.Equal(i, l[0]) // querying a non-existent key fails _, _, err = store.Get("badname") diff --git a/storage/memstorage/main.go b/storage/memstorage/main.go index 9f671e484..69c8d9b03 100644 --- a/storage/memstorage/main.go +++ b/storage/memstorage/main.go @@ -44,7 +44,7 @@ func (s MemStore) Get(name string) ([]byte, keys.Info, error) { if !ok { err = errors.Errorf("Key named '%s' doesn't exist", name) } - return d.key, d.info, err + return d.key, d.info.Format(), err } // List returns the public info of all keys in the MemStore in unsorted order @@ -52,7 +52,7 @@ func (s MemStore) List() (keys.Infos, error) { res := make([]keys.Info, len(s)) i := 0 for _, d := range s { - res[i] = d.info + res[i] = d.info.Format() i++ } return res, nil diff --git a/storage/memstorage/main_test.go b/storage/memstorage/main_test.go index 2863bffad..7605c8225 100644 --- a/storage/memstorage/main_test.go +++ b/storage/memstorage/main_test.go @@ -41,11 +41,13 @@ func TestBasicCRUD(t *testing.T) { k, i, err := store.Get(name) assert.Nil(err) assert.Equal(key, k) - assert.Equal(info, i) + assert.Equal(info.Name, i.Name) + assert.Equal(info.PubKey, i.PubKey) + assert.NotEmpty(i.Address) l, err = store.List() assert.Nil(err) assert.Equal(1, len(l)) - assert.Equal(info, l[0]) + assert.Equal(i, l[0]) // querying a non-existent key fails _, _, err = store.Get("badname") diff --git a/transactions.go b/transactions.go index 3acb476cf..91dc0e273 100644 --- a/transactions.go +++ b/transactions.go @@ -4,12 +4,21 @@ import ( "sort" crypto "github.com/tendermint/go-crypto" + data "github.com/tendermint/go-data" ) // Info is the public information about a key type Info struct { - Name string - PubKey crypto.PubKeyS + Name string `json:"name"` + Address data.Bytes `json:"address"` + PubKey crypto.PubKeyS `json:"pubkey"` +} + +func (i *Info) Format() Info { + if !i.PubKey.Empty() { + i.Address = i.PubKey.Address() + } + return *i } // Infos is a wrapper to allows alphabetical sorting of the keys From e1c717a04863a9ddf24251a9eaec14feaf7da37a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 20:26:45 +0100 Subject: [PATCH 08/45] Add support for hex / base64 / btc (b58) encoding of binary data --- cmd/README.md | 36 ++++++++++++++++++++++++++++++++++++ cmd/root.go | 18 +++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/cmd/README.md b/cmd/README.md index 72f202bf1..8bf9ca73b 100644 --- a/cmd/README.md +++ b/cmd/README.md @@ -79,3 +79,39 @@ This CLI is too simple to warant such a structure, but I think eg. tendermint co * 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=" + } + } +] +``` diff --git a/cmd/root.go b/cmd/root.go index 029c37f9d..41803fc65 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -23,6 +23,8 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + data "github.com/tendermint/go-data" + "github.com/tendermint/go-data/base58" keys "github.com/tendermint/go-keys" "github.com/tendermint/go-keys/cryptostore" "github.com/tendermint/go-keys/storage/filestorage" @@ -59,8 +61,9 @@ func Execute() { func init() { cobra.OnInitialize(initEnv) RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data") - RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)") RootCmd.PersistentFlags().String("keydir", "keys", "Directory to store private keys (subdir of root)") + RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)") + RootCmd.PersistentFlags().StringP("encoding", "e", "hex", "Binary encoding (hex|b64|btc)") } // initEnv sets to use ENV variables if set. @@ -101,6 +104,19 @@ func validateFlags(cmd *cobra.Command) error { return errors.Errorf("Unsupported output format: %s", output) } + // validate and set encoding + enc := viper.GetString("encoding") + switch enc { + case "hex": + data.Encoder = data.HexEncoder + case "b64": + data.Encoder = data.B64Encoder + case "btc": + data.Encoder = base58.BTCEncoder + default: + return errors.Errorf("Unsupported encoding: %s", enc) + } + // store the keys directory keyDir = viper.GetString("keydir") if !filepath.IsAbs(keyDir) { From 6ec2330eb8dbecb17495d2dc0565ace570adf457 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 21:09:35 +0100 Subject: [PATCH 09/45] Import keys server from light-client, with changes --- proxy/README.md | 65 +++++++++++++++ proxy/helpers.go | 58 ++++++++++++++ proxy/keys.go | 123 ++++++++++++++++++++++++++++ proxy/keys_test.go | 190 ++++++++++++++++++++++++++++++++++++++++++++ proxy/types/keys.go | 28 +++++++ proxy/valid.go | 12 +++ 6 files changed, 476 insertions(+) create mode 100644 proxy/README.md create mode 100644 proxy/helpers.go create mode 100644 proxy/keys.go create mode 100644 proxy/keys_test.go create mode 100644 proxy/types/keys.go create mode 100644 proxy/valid.go diff --git a/proxy/README.md b/proxy/README.md new file mode 100644 index 000000000..614110b7f --- /dev/null +++ b/proxy/README.md @@ -0,0 +1,65 @@ +# Proxy Server + +This package provides all the functionality for a local proxy http server, and ties together functionality from all the other packages to acheive this aim. Simply configure this server with your application-specific settings via a main script and you are good to go. + +This server should run on the client's machine, and can accept a trusted connection from localhost (via tcp or unix socket). It provides a simple json rest API and handles all the binary wrangling and cryptographic proofs under the hood. Thus, you can host a local webapp (via electron?) and connect to this proxy, perform simple queries and posts and behind the scenes take advantage of the *awesome power* of the tendermint blockchain. + +If you are writing native code, you can use this as well, or you can look for bindings to embed this functionality directly as a library in your codebase. +**(coming soon)** + +## API + +The API has various sections based on functionality. The major portions are key management, signing and posting transactions, and querying and proving data. + +### Key Management + +We expose a number of methods for safely managing your keychain. They are typically bound under `/keys`, but could be placed in another location by the app. + +* `POST /keys/` - provide a name and passphrase and create a brand new key +* `GET /keys/` - get a list of all available key names, along with their public key and address +* `GET /keys/{name}` - get public key and address for this named key + +Later expose: + +* `PUT /keys/{name}` - update the passphrase for the given key. requires you to correctly provide the current passphrase, as well as a new one. +* `DELETE /keys/{name}` - permanently delete this private key. requires you to correctly provide the current passphrase +* export and import functionality + +### Transactions + +You want to post your transaction. Great. Your application must provide logic to transform json into a `Signable` go struct. Then we handle the rest, signing it with a keypair of your choosing, posting it to tendermint core, and returning you confirmation when it was committed. + +* `POST /txs/` - provide name, passphrase and application-specific data to post to tendermint + + +### Proving Data + +We sent some money to our friend, now we want to check his balance. No, not just look at it, but really check it, verify all those cryptographic proofs that some node is not lying and it really, truly is in his account. + +Thankfully, we have the technology and can do all the checks in the proxy, it might just take a second or two for us to get all those block headers. + +However, this still just leaves us with a bunch of binary blobs from the server, so to make this whole process less painless, you should provide some application-specific logic to parse this binary data from the blockchain, so we can present it as json over the interface. + +* `GET /query/{path}/{data}` - will quickly query the data under the given (hex-encoded) key. `path` is `key` to query by merkle key, but your application could provide other prefixes, to differentiate by types (eg. `account`, `votes`, `escrow`). The returned data is parsed into json and displayed. +* `GET /proof/{key}` - will query for a merkle proof of the given key, download block headers, and verify all the signatures of that block. After it is done, it will present you some json and a stamp that it your data is really safe and sound. + +## Configuring + +When you instantiate a server, make sure to pass in application-specific info in order to properly. Like the following info: + +Possibly as command-line flags: + +* Where to store the private keys? (or find existing ones) +* Which type of key to generate? +* What is the URL of the tendermint RPC server? + * TODO: support multiple node URLs and round-robin +* What is the chain_id we wish to connect to? + +Extra code (plugin) you must write: + +* Logic to parse json -> `Signable` transaction +* Logic to parse binary values from merkle tree -> `struct`ured data to render + +TODO: + +* How to get the trusted validator set? diff --git a/proxy/helpers.go b/proxy/helpers.go new file mode 100644 index 000000000..ac314684b --- /dev/null +++ b/proxy/helpers.go @@ -0,0 +1,58 @@ +/* +package proxy provides http handlers to construct a proxy server +for key management, transaction signing, and query validation. + +Please read the README and godoc to see how to +configure the server for your application. +*/ +package proxy + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + data "github.com/tendermint/go-data" + "github.com/tendermint/go-keys/proxy/types" + + "github.com/pkg/errors" +) + +func readRequest(r *http.Request, o interface{}) error { + defer r.Body.Close() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return errors.Wrap(err, "Read Request") + } + err = json.Unmarshal(data, o) + if err != nil { + return errors.Wrap(err, "Parse") + } + return validate(o) +} + +// most errors are bad input, so 406... do better.... +func writeError(w http.ResponseWriter, err error) { + // fmt.Printf("Error: %+v\n", err) + res := types.ErrorResponse{ + Code: 406, + Error: err.Error(), + } + writeCode(w, &res, 406) +} + +func writeCode(w http.ResponseWriter, o interface{}, code int) { + // two space indent to make it easier to read + data, err := data.ToJSON(o) + if err != nil { + writeError(w, err) + } else { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + w.Write(data) + } +} + +func writeSuccess(w http.ResponseWriter, o interface{}) { + writeCode(w, o, 200) +} diff --git a/proxy/keys.go b/proxy/keys.go new file mode 100644 index 000000000..64b6819cd --- /dev/null +++ b/proxy/keys.go @@ -0,0 +1,123 @@ +package proxy + +import ( + "errors" + "net/http" + + "github.com/gorilla/mux" + keys "github.com/tendermint/go-keys" + "github.com/tendermint/go-keys/proxy/types" +) + +type KeyServer struct { + manager keys.Manager +} + +func NewKeyServer(manager keys.Manager) KeyServer { + return KeyServer{ + manager: manager, + } +} + +func (k KeyServer) GenerateKey(w http.ResponseWriter, r *http.Request) { + req := types.CreateKeyRequest{} + err := readRequest(r, &req) + if err != nil { + writeError(w, err) + return + } + + key, err := k.manager.Create(req.Name, req.Passphrase, req.Algo) + if err != nil { + writeError(w, err) + return + } + + writeSuccess(w, &key) +} + +func (k KeyServer) GetKey(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + name := vars["name"] + key, err := k.manager.Get(name) + if err != nil { + writeError(w, err) + return + } + writeSuccess(w, &key) +} + +func (k KeyServer) ListKeys(w http.ResponseWriter, r *http.Request) { + + keys, err := k.manager.List() + if err != nil { + writeError(w, err) + return + } + writeSuccess(w, keys) +} + +func (k KeyServer) UpdateKey(w http.ResponseWriter, r *http.Request) { + req := types.UpdateKeyRequest{} + err := readRequest(r, &req) + if err != nil { + writeError(w, err) + return + } + + vars := mux.Vars(r) + name := vars["name"] + if name != req.Name { + writeError(w, errors.New("path and json key names don't match")) + return + } + + err = k.manager.Update(req.Name, req.OldPass, req.NewPass) + if err != nil { + writeError(w, err) + return + } + + key, err := k.manager.Get(req.Name) + if err != nil { + writeError(w, err) + return + } + writeSuccess(w, &key) +} + +func (k KeyServer) DeleteKey(w http.ResponseWriter, r *http.Request) { + req := types.DeleteKeyRequest{} + err := readRequest(r, &req) + if err != nil { + writeError(w, err) + return + } + + vars := mux.Vars(r) + name := vars["name"] + if name != req.Name { + writeError(w, errors.New("path and json key names don't match")) + return + } + + err = k.manager.Delete(req.Name, req.Passphrase) + if err != nil { + writeError(w, err) + return + } + + // not really an error, but something generic + resp := types.ErrorResponse{ + Success: true, + } + writeSuccess(w, &resp) +} + +func (k KeyServer) Register(r *mux.Router) { + r.HandleFunc("/", k.GenerateKey).Methods("POST") + r.HandleFunc("/", k.ListKeys).Methods("GET") + r.HandleFunc("/{name}", k.GetKey).Methods("GET") + r.HandleFunc("/{name}", k.UpdateKey).Methods("POST", "PUT") + r.HandleFunc("/{name}", k.DeleteKey).Methods("DELETE") +} diff --git a/proxy/keys_test.go b/proxy/keys_test.go new file mode 100644 index 000000000..6eee94e39 --- /dev/null +++ b/proxy/keys_test.go @@ -0,0 +1,190 @@ +package proxy_test + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gorilla/mux" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + keys "github.com/tendermint/go-keys" + "github.com/tendermint/go-keys/cryptostore" + "github.com/tendermint/go-keys/proxy" + "github.com/tendermint/go-keys/proxy/types" + "github.com/tendermint/go-keys/storage/memstorage" +) + +func TestKeyServer(t *testing.T) { + assert, require := assert.New(t), require.New(t) + r := setupServer() + + // let's abstract this out a bit.... + keys, code, err := listKeys(r) + require.Nil(err) + require.Equal(http.StatusOK, code) + assert.Equal(0, len(keys)) + + algo := "ed25519" + n1, n2 := "personal", "business" + p0, p1, p2 := "1234", "over10chars...", "really-secure!@#$" + + // this fails for validation + _, code, err = createKey(r, n1, p0, algo) + require.Nil(err, "%+v", err) + require.NotEqual(http.StatusOK, code) + + // new password better + key, code, err := createKey(r, n1, p1, algo) + require.Nil(err, "%+v", err) + require.Equal(http.StatusOK, code) + require.Equal(key.Name, n1) + + // the other one works + key2, code, err := createKey(r, n2, p2, algo) + require.Nil(err, "%+v", err) + require.Equal(http.StatusOK, code) + require.Equal(key2.Name, n2) + + // let's abstract this out a bit.... + keys, code, err = listKeys(r) + require.Nil(err) + require.Equal(http.StatusOK, code) + if assert.Equal(2, len(keys)) { + // in alphabetical order + assert.Equal(keys[0].Name, n2) + assert.Equal(keys[1].Name, n1) + } + + // get works + k, code, err := getKey(r, n1) + require.Nil(err, "%+v", err) + require.Equal(http.StatusOK, code) + assert.Equal(k.Name, n1) + assert.NotNil(k.Address) + assert.Equal(k.Address, key.Address) + + // delete with proper key + _, code, err = deleteKey(r, n1, p1) + require.Nil(err, "%+v", err) + require.Equal(http.StatusOK, code) + + // after delete, get and list different + _, code, err = getKey(r, n1) + require.Nil(err, "%+v", err) + require.NotEqual(http.StatusOK, code) + keys, code, err = listKeys(r) + require.Nil(err, "%+v", err) + require.Equal(http.StatusOK, code) + if assert.Equal(1, len(keys)) { + assert.Equal(keys[0].Name, n2) + } + +} + +func setupServer() http.Handler { + // make the storage with reasonable defaults + cstore := cryptostore.New( + cryptostore.SecretBox, + memstorage.New(), + ) + + // build your http server + ks := proxy.NewKeyServer(cstore) + r := mux.NewRouter() + sk := r.PathPrefix("/keys").Subrouter() + ks.Register(sk) + return r +} + +// return data, status code, and error +func listKeys(h http.Handler) (keys.Infos, int, error) { + rr := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/keys/", nil) + if err != nil { + return nil, 0, err + } + + h.ServeHTTP(rr, req) + if http.StatusOK != rr.Code { + return nil, rr.Code, nil + } + + data := keys.Infos{} + err = json.Unmarshal(rr.Body.Bytes(), &data) + return data, rr.Code, err +} + +func getKey(h http.Handler, name string) (*keys.Info, int, error) { + rr := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/keys/"+name, nil) + if err != nil { + return nil, 0, err + } + + h.ServeHTTP(rr, req) + if http.StatusOK != rr.Code { + return nil, rr.Code, nil + } + + data := keys.Info{} + err = json.Unmarshal(rr.Body.Bytes(), &data) + return &data, rr.Code, err +} + +func createKey(h http.Handler, name, passphrase, algo string) (*keys.Info, int, error) { + rr := httptest.NewRecorder() + post := types.CreateKeyRequest{ + Name: name, + Passphrase: passphrase, + Algo: algo, + } + var b bytes.Buffer + err := json.NewEncoder(&b).Encode(&post) + if err != nil { + return nil, 0, err + } + + req, err := http.NewRequest("POST", "/keys/", &b) + if err != nil { + return nil, 0, err + } + + h.ServeHTTP(rr, req) + if http.StatusOK != rr.Code { + return nil, rr.Code, nil + } + + data := keys.Info{} + err = json.Unmarshal(rr.Body.Bytes(), &data) + return &data, rr.Code, err +} + +func deleteKey(h http.Handler, name, passphrase string) (*types.ErrorResponse, int, error) { + rr := httptest.NewRecorder() + post := types.DeleteKeyRequest{ + Name: name, + Passphrase: passphrase, + } + var b bytes.Buffer + err := json.NewEncoder(&b).Encode(&post) + if err != nil { + return nil, 0, err + } + + req, err := http.NewRequest("DELETE", "/keys/"+name, &b) + if err != nil { + return nil, 0, err + } + + h.ServeHTTP(rr, req) + if http.StatusOK != rr.Code { + return nil, rr.Code, nil + } + + data := types.ErrorResponse{} + err = json.Unmarshal(rr.Body.Bytes(), &data) + return &data, rr.Code, err +} diff --git a/proxy/types/keys.go b/proxy/types/keys.go new file mode 100644 index 000000000..ffdc542f1 --- /dev/null +++ b/proxy/types/keys.go @@ -0,0 +1,28 @@ +package types + +// CreateKeyRequest is sent to create a new key +type CreateKeyRequest struct { + Name string `json:"name" validate:"required,min=4,printascii"` + Passphrase string `json:"passphrase" validate:"required,min=10"` + Algo string `json:"algo"` +} + +// DeleteKeyRequest to destroy a key permanently (careful!) +type DeleteKeyRequest struct { + Name string `json:"name" validate:"required,min=4,printascii"` + Passphrase string `json:"passphrase" validate:"required,min=10"` +} + +// UpdateKeyRequest is sent to update the passphrase for an existing key +type UpdateKeyRequest struct { + Name string `json:"name" validate:"required,min=4,printascii"` + OldPass string `json:"passphrase" validate:"required,min=10"` + NewPass string `json:"new_passphrase" validate:"required,min=10"` +} + +// ErrorResponse is returned for 4xx and 5xx errors +type ErrorResponse struct { + Success bool `json:"success"` + Error string `json:"error"` // error message if Success is false + Code int `json:"code"` // error code if Success is false +} diff --git a/proxy/valid.go b/proxy/valid.go new file mode 100644 index 000000000..31782cfca --- /dev/null +++ b/proxy/valid.go @@ -0,0 +1,12 @@ +package proxy + +import ( + "github.com/pkg/errors" + "gopkg.in/go-playground/validator.v9" +) + +var v = validator.New() + +func validate(req interface{}) error { + return errors.Wrap(v.Struct(req), "Validate") +} From 6389d208ccd68d5f921b005d60b00e3126882bec Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 21:33:27 +0100 Subject: [PATCH 10/45] Key server API integrated in CLI --- cmd/new.go | 2 +- cmd/serve.go | 78 +++++++++++++++++++++++++++++++++++++++------- proxy/keys.go | 8 +++-- proxy/keys_test.go | 2 +- 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/cmd/new.go b/cmd/new.go index 5ac0d3b90..27c7dce75 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -33,7 +33,7 @@ passed as a command line argument for security.`, func init() { RootCmd.AddCommand(newCmd) - newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1") + newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1)") } func newPassword(cmd *cobra.Command, args []string) { diff --git a/cmd/serve.go b/cmd/serve.go index 06e923c7b..1555c6480 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -16,8 +16,16 @@ package cmd import ( "fmt" + "net" + "net/http" + "os" + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/go-keys/proxy" ) // serveCmd represents the serve command @@ -28,23 +36,71 @@ var serveCmd = &cobra.Command{ private keys much more in depth than the cli can perform. In particular, this will allow you to sign transactions with the private keys in the store.`, - Run: func(cmd *cobra.Command, args []string) { - // TODO: Work your own magic here - fmt.Println("serve called") - }, + RunE: server, } func init() { RootCmd.AddCommand(serveCmd) + serveCmd.Flags().IntP("port", "p", 8118, "TCP Port for listen for http server") + serveCmd.Flags().StringP("socket", "s", "", "UNIX socket for more secure http server") + serveCmd.Flags().StringP("type", "t", "ed25519", "Default key type (ed25519|secp256k1)") +} + +func server(cmd *cobra.Command, args []string) error { + var l net.Listener + var err error + socket := viper.GetString("socket") + if socket != "" { + l, err = createSocket(socket) + if err != nil { + return errors.Wrap(err, "Cannot create socket") + } + } else { + port := viper.GetInt("port") + l, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + return errors.Errorf("Cannot listen on port %d", port) + } + } + + router := mux.NewRouter() + ks := proxy.NewKeyServer(manager, viper.GetString("type")) + ks.Register(router) + + // only set cors for tcp listener + var h http.Handler + if socket == "" { + allowedHeaders := handlers.AllowedHeaders([]string{"Content-Type"}) + h = handlers.CORS(allowedHeaders)(router) + } else { + h = router + } + + err = http.Serve(l, h) + fmt.Printf("Server Killed: %+v\n", err) + return nil +} - // Here you will define your flags and configuration settings. +// createSocket deletes existing socket if there, creates a new one, +// starts a server on the socket, and sets permissions to 0600 +func createSocket(socket string) (net.Listener, error) { + err := os.Remove(socket) + if err != nil && !os.IsNotExist(err) { + // only fail if it does exist and cannot be deleted + return nil, err + } - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // serveCmd.PersistentFlags().String("foo", "", "A help for foo") + l, err := net.Listen("unix", socket) + if err != nil { + return nil, err + } - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // serveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + mode := os.FileMode(0700) | os.ModeSocket + err = os.Chmod(socket, mode) + if err != nil { + l.Close() + return nil, err + } + return l, nil } diff --git a/proxy/keys.go b/proxy/keys.go index 64b6819cd..0dde57074 100644 --- a/proxy/keys.go +++ b/proxy/keys.go @@ -11,16 +11,20 @@ import ( type KeyServer struct { manager keys.Manager + algo string } -func NewKeyServer(manager keys.Manager) KeyServer { +func NewKeyServer(manager keys.Manager, algo string) KeyServer { return KeyServer{ manager: manager, + algo: algo, } } func (k KeyServer) GenerateKey(w http.ResponseWriter, r *http.Request) { - req := types.CreateKeyRequest{} + req := types.CreateKeyRequest{ + Algo: k.algo, // default key type from cli + } err := readRequest(r, &req) if err != nil { writeError(w, err) diff --git a/proxy/keys_test.go b/proxy/keys_test.go index 6eee94e39..3a9c73717 100644 --- a/proxy/keys_test.go +++ b/proxy/keys_test.go @@ -92,7 +92,7 @@ func setupServer() http.Handler { ) // build your http server - ks := proxy.NewKeyServer(cstore) + ks := proxy.NewKeyServer(cstore, "ed25519") r := mux.NewRouter() sk := r.PathPrefix("/keys").Subrouter() ks.Register(sk) From 0c92c8516f530f7f58150073a03e7fc7639b1cfb Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Feb 2017 21:53:05 +0100 Subject: [PATCH 11/45] Rename proxy to server for clarity --- cmd/serve.go | 8 ++-- proxy/README.md | 65 --------------------------------- server/README.md | 13 +++++++ {proxy => server}/helpers.go | 6 +-- {proxy => server}/keys.go | 22 +++++------ {proxy => server}/keys_test.go | 8 ++-- {proxy => server}/types/keys.go | 0 {proxy => server}/valid.go | 2 +- 8 files changed, 36 insertions(+), 88 deletions(-) delete mode 100644 proxy/README.md create mode 100644 server/README.md rename {proxy => server}/helpers.go (90%) rename {proxy => server}/keys.go (77%) rename {proxy => server}/keys_test.go (96%) rename {proxy => server}/types/keys.go (100%) rename {proxy => server}/valid.go (92%) diff --git a/cmd/serve.go b/cmd/serve.go index 1555c6480..2717913fd 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -25,7 +25,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/tendermint/go-keys/proxy" + "github.com/tendermint/go-keys/server" ) // serveCmd represents the serve command @@ -36,7 +36,7 @@ var serveCmd = &cobra.Command{ private keys much more in depth than the cli can perform. In particular, this will allow you to sign transactions with the private keys in the store.`, - RunE: server, + RunE: serveHTTP, } func init() { @@ -46,7 +46,7 @@ func init() { serveCmd.Flags().StringP("type", "t", "ed25519", "Default key type (ed25519|secp256k1)") } -func server(cmd *cobra.Command, args []string) error { +func serveHTTP(cmd *cobra.Command, args []string) error { var l net.Listener var err error socket := viper.GetString("socket") @@ -64,7 +64,7 @@ func server(cmd *cobra.Command, args []string) error { } router := mux.NewRouter() - ks := proxy.NewKeyServer(manager, viper.GetString("type")) + ks := server.New(manager, viper.GetString("type")) ks.Register(router) // only set cors for tcp listener diff --git a/proxy/README.md b/proxy/README.md deleted file mode 100644 index 614110b7f..000000000 --- a/proxy/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Proxy Server - -This package provides all the functionality for a local proxy http server, and ties together functionality from all the other packages to acheive this aim. Simply configure this server with your application-specific settings via a main script and you are good to go. - -This server should run on the client's machine, and can accept a trusted connection from localhost (via tcp or unix socket). It provides a simple json rest API and handles all the binary wrangling and cryptographic proofs under the hood. Thus, you can host a local webapp (via electron?) and connect to this proxy, perform simple queries and posts and behind the scenes take advantage of the *awesome power* of the tendermint blockchain. - -If you are writing native code, you can use this as well, or you can look for bindings to embed this functionality directly as a library in your codebase. -**(coming soon)** - -## API - -The API has various sections based on functionality. The major portions are key management, signing and posting transactions, and querying and proving data. - -### Key Management - -We expose a number of methods for safely managing your keychain. They are typically bound under `/keys`, but could be placed in another location by the app. - -* `POST /keys/` - provide a name and passphrase and create a brand new key -* `GET /keys/` - get a list of all available key names, along with their public key and address -* `GET /keys/{name}` - get public key and address for this named key - -Later expose: - -* `PUT /keys/{name}` - update the passphrase for the given key. requires you to correctly provide the current passphrase, as well as a new one. -* `DELETE /keys/{name}` - permanently delete this private key. requires you to correctly provide the current passphrase -* export and import functionality - -### Transactions - -You want to post your transaction. Great. Your application must provide logic to transform json into a `Signable` go struct. Then we handle the rest, signing it with a keypair of your choosing, posting it to tendermint core, and returning you confirmation when it was committed. - -* `POST /txs/` - provide name, passphrase and application-specific data to post to tendermint - - -### Proving Data - -We sent some money to our friend, now we want to check his balance. No, not just look at it, but really check it, verify all those cryptographic proofs that some node is not lying and it really, truly is in his account. - -Thankfully, we have the technology and can do all the checks in the proxy, it might just take a second or two for us to get all those block headers. - -However, this still just leaves us with a bunch of binary blobs from the server, so to make this whole process less painless, you should provide some application-specific logic to parse this binary data from the blockchain, so we can present it as json over the interface. - -* `GET /query/{path}/{data}` - will quickly query the data under the given (hex-encoded) key. `path` is `key` to query by merkle key, but your application could provide other prefixes, to differentiate by types (eg. `account`, `votes`, `escrow`). The returned data is parsed into json and displayed. -* `GET /proof/{key}` - will query for a merkle proof of the given key, download block headers, and verify all the signatures of that block. After it is done, it will present you some json and a stamp that it your data is really safe and sound. - -## Configuring - -When you instantiate a server, make sure to pass in application-specific info in order to properly. Like the following info: - -Possibly as command-line flags: - -* Where to store the private keys? (or find existing ones) -* Which type of key to generate? -* What is the URL of the tendermint RPC server? - * TODO: support multiple node URLs and round-robin -* What is the chain_id we wish to connect to? - -Extra code (plugin) you must write: - -* Logic to parse json -> `Signable` transaction -* Logic to parse binary values from merkle tree -> `struct`ured data to render - -TODO: - -* How to get the trusted validator set? diff --git a/server/README.md b/server/README.md new file mode 100644 index 000000000..032cf574e --- /dev/null +++ b/server/README.md @@ -0,0 +1,13 @@ +# Proxy Server + +This package provides all the functionality for a local http server, providing access to key management functionality (creating, listing, updating, and deleting keys). This is a nice building block for larger apps, and the HTTP handlers here can be embedded in a larger server that does nice things like signing transactions and posting them to a tendermint chain (which requires domain-knowledge of the transactions types and out of scope of this generic app). + +## Key Management + +We expose a number of methods for safely managing your keychain. If you are embedding this in a larger server, you will typically want to mount all these paths under `/keys`. + +* `POST /` - provide a name and passphrase and create a brand new key +* `GET /` - get a list of all available key names, along with their public key and address +* `GET /{name}` - get public key and address for this named key +* `PUT /{name}` - update the passphrase for the given key. requires you to correctly provide the current passphrase, as well as a new one. +* `DELETE /{name}` - permanently delete this private key. requires you to correctly provide the current passphrase diff --git a/proxy/helpers.go b/server/helpers.go similarity index 90% rename from proxy/helpers.go rename to server/helpers.go index ac314684b..111f158e7 100644 --- a/proxy/helpers.go +++ b/server/helpers.go @@ -1,11 +1,11 @@ /* -package proxy provides http handlers to construct a proxy server +package server provides http handlers to construct a server server for key management, transaction signing, and query validation. Please read the README and godoc to see how to configure the server for your application. */ -package proxy +package server import ( "encoding/json" @@ -13,7 +13,7 @@ import ( "net/http" data "github.com/tendermint/go-data" - "github.com/tendermint/go-keys/proxy/types" + "github.com/tendermint/go-keys/server/types" "github.com/pkg/errors" ) diff --git a/proxy/keys.go b/server/keys.go similarity index 77% rename from proxy/keys.go rename to server/keys.go index 0dde57074..78a382e5e 100644 --- a/proxy/keys.go +++ b/server/keys.go @@ -1,4 +1,4 @@ -package proxy +package server import ( "errors" @@ -6,22 +6,22 @@ import ( "github.com/gorilla/mux" keys "github.com/tendermint/go-keys" - "github.com/tendermint/go-keys/proxy/types" + "github.com/tendermint/go-keys/server/types" ) -type KeyServer struct { +type Keys struct { manager keys.Manager algo string } -func NewKeyServer(manager keys.Manager, algo string) KeyServer { - return KeyServer{ +func New(manager keys.Manager, algo string) Keys { + return Keys{ manager: manager, algo: algo, } } -func (k KeyServer) GenerateKey(w http.ResponseWriter, r *http.Request) { +func (k Keys) GenerateKey(w http.ResponseWriter, r *http.Request) { req := types.CreateKeyRequest{ Algo: k.algo, // default key type from cli } @@ -40,7 +40,7 @@ func (k KeyServer) GenerateKey(w http.ResponseWriter, r *http.Request) { writeSuccess(w, &key) } -func (k KeyServer) GetKey(w http.ResponseWriter, r *http.Request) { +func (k Keys) GetKey(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name := vars["name"] key, err := k.manager.Get(name) @@ -51,7 +51,7 @@ func (k KeyServer) GetKey(w http.ResponseWriter, r *http.Request) { writeSuccess(w, &key) } -func (k KeyServer) ListKeys(w http.ResponseWriter, r *http.Request) { +func (k Keys) ListKeys(w http.ResponseWriter, r *http.Request) { keys, err := k.manager.List() if err != nil { @@ -61,7 +61,7 @@ func (k KeyServer) ListKeys(w http.ResponseWriter, r *http.Request) { writeSuccess(w, keys) } -func (k KeyServer) UpdateKey(w http.ResponseWriter, r *http.Request) { +func (k Keys) UpdateKey(w http.ResponseWriter, r *http.Request) { req := types.UpdateKeyRequest{} err := readRequest(r, &req) if err != nil { @@ -90,7 +90,7 @@ func (k KeyServer) UpdateKey(w http.ResponseWriter, r *http.Request) { writeSuccess(w, &key) } -func (k KeyServer) DeleteKey(w http.ResponseWriter, r *http.Request) { +func (k Keys) DeleteKey(w http.ResponseWriter, r *http.Request) { req := types.DeleteKeyRequest{} err := readRequest(r, &req) if err != nil { @@ -118,7 +118,7 @@ func (k KeyServer) DeleteKey(w http.ResponseWriter, r *http.Request) { writeSuccess(w, &resp) } -func (k KeyServer) Register(r *mux.Router) { +func (k Keys) Register(r *mux.Router) { r.HandleFunc("/", k.GenerateKey).Methods("POST") r.HandleFunc("/", k.ListKeys).Methods("GET") r.HandleFunc("/{name}", k.GetKey).Methods("GET") diff --git a/proxy/keys_test.go b/server/keys_test.go similarity index 96% rename from proxy/keys_test.go rename to server/keys_test.go index 3a9c73717..8333e8e53 100644 --- a/proxy/keys_test.go +++ b/server/keys_test.go @@ -1,4 +1,4 @@ -package proxy_test +package server_test import ( "bytes" @@ -12,8 +12,8 @@ import ( "github.com/stretchr/testify/require" keys "github.com/tendermint/go-keys" "github.com/tendermint/go-keys/cryptostore" - "github.com/tendermint/go-keys/proxy" - "github.com/tendermint/go-keys/proxy/types" + "github.com/tendermint/go-keys/server" + "github.com/tendermint/go-keys/server/types" "github.com/tendermint/go-keys/storage/memstorage" ) @@ -92,7 +92,7 @@ func setupServer() http.Handler { ) // build your http server - ks := proxy.NewKeyServer(cstore, "ed25519") + ks := server.New(cstore, "ed25519") r := mux.NewRouter() sk := r.PathPrefix("/keys").Subrouter() ks.Register(sk) diff --git a/proxy/types/keys.go b/server/types/keys.go similarity index 100% rename from proxy/types/keys.go rename to server/types/keys.go diff --git a/proxy/valid.go b/server/valid.go similarity index 92% rename from proxy/valid.go rename to server/valid.go index 31782cfca..50b51e21b 100644 --- a/proxy/valid.go +++ b/server/valid.go @@ -1,4 +1,4 @@ -package proxy +package server import ( "github.com/pkg/errors" From af7e3120888a199c22e94f6e9a1b1a3a0545c5ac Mon Sep 17 00:00:00 2001 From: "Paul W. Homer" Date: Thu, 2 Mar 2017 17:16:15 -0500 Subject: [PATCH 12/45] Added glide dependency handling --- .gitignore | 1 + Makefile | 19 ++++++++- glide.lock | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++ glide.yaml | 19 +++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 glide.lock create mode 100644 glide.yaml diff --git a/.gitignore b/.gitignore index cfaef8179..2f1e043a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .mykeys +vendor diff --git a/Makefile b/Makefile index b15317b25..63eb70787 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,22 @@ +GOTOOLS = \ + github.com/mitchellh/gox \ + github.com/Masterminds/glide + +.PHONEY: all test install get_vendor_deps ensure_tools + +all: install test + test: - go test ./... + go test `glide novendor` install: go install ./cmd/keys + +get_vendor_deps: ensure_tools + @rm -rf vendor/ + @echo "--> Running glide install" + @glide install + +ensure_tools: + go get $(GOTOOLS) + diff --git a/glide.lock b/glide.lock new file mode 100644 index 000000000..c74262a4e --- /dev/null +++ b/glide.lock @@ -0,0 +1,123 @@ +hash: 4a517b0f71ea6e3aadcf98286cde97c2f567e9e7999a4c7ec9ce6e5b6c21564a +updated: 2017-03-02T16:57:20.740518259-05:00 +imports: +- name: github.com/bgentry/speakeasy + version: 675b82c74c0ed12283ee81ba8a534c8982c07b85 +- name: github.com/btcsuite/btcd + version: d06c0bb181529331be8f8d9350288c420d9e60e4 + subpackages: + - btcec +- name: github.com/fsnotify/fsnotify + version: 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1 +- name: github.com/go-playground/locales + version: 084b0226cf88d891a2bdeccac01d592af13a8f7b + subpackages: + - currency +- name: github.com/go-playground/universal-translator + version: b32fa301c9fe55953584134cb6853a13c87ec0a1 +- name: github.com/go-stack/stack + version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 +- name: github.com/gorilla/context + version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 +- name: github.com/gorilla/handlers + version: 13d73096a474cac93275c679c7b8a2dc17ddba82 +- name: github.com/gorilla/mux + version: 599cba5e7b6137d46ddf58fb1765f5d928e69604 +- name: github.com/hashicorp/hcl + version: 630949a3c5fa3c613328e1b8256052cbc2327c9b + subpackages: + - hcl/ast + - hcl/parser + - hcl/scanner + - hcl/strconv + - hcl/token + - json/parser + - json/scanner + - json/token +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/magiconair/properties + version: b3b15ef068fd0b17ddf408a23669f20811d194d2 +- name: github.com/mattn/go-colorable + version: 5411d3eea5978e6cdc258b30de592b60df6aba96 +- name: github.com/mattn/go-isatty + version: 281032e84ae07510239465db46bf442aa44b953a +- name: github.com/mitchellh/mapstructure + version: db1efb556f84b25a0a13a04aad883943538ad2e0 +- name: github.com/pelletier/go-buffruneio + version: c37440a7cf42ac63b919c752ca73a85067e05992 +- name: github.com/pelletier/go-toml + version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a +- name: github.com/pkg/errors + version: 248dadf4e9068a0b3e79f02ed0a610d935de5302 +- name: github.com/spf13/afero + version: 9be650865eab0c12963d8753212f4f9c66cdcf12 + subpackages: + - mem +- name: github.com/spf13/cast + version: f820543c3592e283e311a60d2a600a664e39f6f7 +- name: github.com/spf13/cobra + version: fcd0c5a1df88f5d6784cb4feead962c3f3d0b66c +- name: github.com/spf13/jwalterweatherman + version: fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66 +- name: github.com/spf13/pflag + version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 +- name: github.com/spf13/viper + version: 7538d73b4eb9511d85a9f1dfef202eeb8ac260f4 +- name: github.com/tendermint/ed25519 + version: 1f52c6f8b8a5c7908aff4497c186af344b428925 + subpackages: + - edwards25519 + - extra25519 +- name: github.com/tendermint/go-common + version: 339e135776142939d82bc8e699db0bf391fd938d +- name: github.com/tendermint/go-crypto + version: 562b4cc9ef0d20217f6e95679f9e83cb7bc98b17 +- name: github.com/tendermint/go-data + version: 32271140e8fd5abdbb22e268d7a02421fa382f0b + subpackages: + - base58 +- name: github.com/tendermint/go-logger + version: cefb3a45c0bf3c493a04e9bcd9b1540528be59f2 +- name: github.com/tendermint/go-wire + version: 3216ec9d47bbdf8d4fc27d22169ea86a6688bc15 +- name: github.com/tendermint/log15 + version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 + subpackages: + - term +- name: golang.org/x/crypto + version: 453249f01cfeb54c3d549ddb75ff152ca243f9d8 + subpackages: + - nacl/secretbox + - openpgp/armor + - openpgp/errors + - poly1305 + - ripemd160 + - salsa20/salsa +- name: golang.org/x/sys + version: e24f485414aeafb646f6fca458b0bf869c0880a1 + subpackages: + - unix +- name: golang.org/x/text + version: d680ca3ed853995402af43b866311167281bdc20 + subpackages: + - transform + - unicode/norm +- name: gopkg.in/go-playground/validator.v9 + version: 4bd19358521c53f09639f21e2a9d6883d6890f24 +- name: gopkg.in/yaml.v2 + version: a3f3340b5840cee44f372bddb5880fcbc419b46a +testImports: +- name: github.com/davecgh/go-spew + version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - require diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 000000000..39402fa8b --- /dev/null +++ b/glide.yaml @@ -0,0 +1,19 @@ +package: github.com/tendermint/go-keys +import: +- package: github.com/bgentry/speakeasy +- package: github.com/gorilla/handlers +- package: github.com/gorilla/mux +- package: github.com/pkg/errors +- package: github.com/spf13/cobra +- package: github.com/spf13/viper +- package: github.com/tendermint/go-crypto + version: develop +- package: github.com/tendermint/go-data + subpackages: + - base58 +- package: gopkg.in/go-playground/validator.v9 +testImport: +- package: github.com/stretchr/testify + subpackages: + - assert + - require From 76ace969256d2e3c2c89ddc8566489c9b8cb78bd Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 20 Mar 2017 09:55:07 +0100 Subject: [PATCH 13/45] Create nested directories as needed to store keys --- storage/filestorage/main.go | 4 ++-- storage/filestorage/main_test.go | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/storage/filestorage/main.go b/storage/filestorage/main.go index 4b7dbe525..737f0772c 100644 --- a/storage/filestorage/main.go +++ b/storage/filestorage/main.go @@ -35,8 +35,8 @@ type FileStore struct { // dir should be an absolute path of a directory owner by this user. It will // be created if it doesn't exist already. func New(dir string) FileStore { - err := os.Mkdir(dir, dirPerm) - if err != nil && !os.IsExist(err) { + err := os.MkdirAll(dir, dirPerm) + if err != nil { panic(err) } return FileStore{dir} diff --git a/storage/filestorage/main_test.go b/storage/filestorage/main_test.go index c9c55eb02..890898dcb 100644 --- a/storage/filestorage/main_test.go +++ b/storage/filestorage/main_test.go @@ -83,15 +83,24 @@ func TestDirectoryHandling(t *testing.T) { newDir := path.Join(os.TempDir(), "file-test-dir") _, err := os.Open(newDir) assert.True(os.IsNotExist(err)) + defer os.RemoveAll(newDir) + + // now, check with two levels deep.... + parentDir := path.Join(os.TempDir(), "missing-dir") + nestedDir := path.Join(parentDir, "lots", "of", "levels", "here") + _, err = os.Open(parentDir) + assert.True(os.IsNotExist(err)) + defer os.RemoveAll(parentDir) // create a new storage, and verify it creates the directory with good permissions - New(newDir) - defer os.RemoveAll(newDir) - d, err := os.Open(newDir) - require.Nil(err) - defer d.Close() + for _, dir := range []string{newDir, nestedDir, newDir} { + New(dir) + d, err := os.Open(dir) + require.Nil(err) + defer d.Close() - stat, err := d.Stat() - require.Nil(err) - assert.Equal(dirPerm, stat.Mode()&os.ModePerm) + stat, err := d.Stat() + require.Nil(err) + assert.Equal(dirPerm, stat.Mode()&os.ModePerm) + } } From 398ac046da7e1b23551e6a0d4405d3a7653eafad Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 23 Mar 2017 20:31:47 +0100 Subject: [PATCH 14/45] Reorganize cobra cmd to enable better reuse --- cmd/common.go | 119 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/get.go | 2 +- cmd/keys/main.go | 11 ++++- cmd/list.go | 2 +- cmd/new.go | 2 +- cmd/root.go | 95 ++++--------------------------------- cmd/serve.go | 2 +- cmd/update.go | 2 +- cmd/utils.go | 5 +- 9 files changed, 146 insertions(+), 94 deletions(-) create mode 100644 cmd/common.go diff --git a/cmd/common.go b/cmd/common.go new file mode 100644 index 000000000..56c9afbab --- /dev/null +++ b/cmd/common.go @@ -0,0 +1,119 @@ +package cmd + +import ( + "fmt" + "os" + "strings" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + data "github.com/tendermint/go-data" + "github.com/tendermint/go-data/base58" +) + +/******* + +TODO + +This file should move into go-common or the like as a basis for all cli tools. +It is here for experimentation of re-use between go-keys and light-client. + +*********/ + +const ( + RootFlag = "root" + OutputFlag = "output" +) + +func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() { + cobra.OnInitialize(func() { initEnv(envPrefix) }) + cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data") + cmd.PersistentFlags().StringP("encoding", "e", "hex", "Binary encoding (hex|b64|btc)") + cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)") + cmd.PersistentPreRunE = multiE(bindFlags, setEncoding, validateOutput, cmd.PersistentPreRunE) + return func() { execute(cmd) } +} + +// initEnv sets to use ENV variables if set. +func initEnv(prefix string) { + // env variables with TM prefix (eg. TM_ROOT) + viper.SetEnvPrefix(prefix) + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.AutomaticEnv() +} + +// execute adds all child commands to the root command sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func execute(cmd *cobra.Command) { + if err := cmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(-1) + } +} + +type wrapE func(cmd *cobra.Command, args []string) error + +func multiE(fs ...wrapE) wrapE { + return func(cmd *cobra.Command, args []string) error { + for _, f := range fs { + if f != nil { + if err := f(cmd, args); err != nil { + return err + } + } + } + return nil + } +} + +func bindFlags(cmd *cobra.Command, args []string) error { + // cmd.Flags() includes flags from this command and all persistent flags from the parent + if err := viper.BindPFlags(cmd.Flags()); err != nil { + return err + } + + // rootDir is command line flag, env variable, or default $HOME/.tlc + rootDir := viper.GetString("root") + viper.SetConfigName("config") // name of config file (without extension) + viper.AddConfigPath(rootDir) // search root directory + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + // stderr, so if we redirect output to json file, this doesn't appear + // fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + } else if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + // we ignore not found error, only parse error + // stderr, so if we redirect output to json file, this doesn't appear + fmt.Fprintf(os.Stderr, "%#v", err) + } + return nil +} + +// setEncoding reads the encoding flag +func setEncoding(cmd *cobra.Command, args []string) error { + // validate and set encoding + enc := viper.GetString("encoding") + switch enc { + case "hex": + data.Encoder = data.HexEncoder + case "b64": + data.Encoder = data.B64Encoder + case "btc": + data.Encoder = base58.BTCEncoder + default: + return errors.Errorf("Unsupported encoding: %s", enc) + } + return nil +} + +func validateOutput(cmd *cobra.Command, args []string) error { + // validate output format + output := viper.GetString(OutputFlag) + switch output { + case "text", "json": + default: + return errors.Errorf("Unsupported output format: %s", output) + } + return nil +} diff --git a/cmd/get.go b/cmd/get.go index d945809f9..10d528807 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -32,7 +32,7 @@ var getCmd = &cobra.Command{ } name := args[0] - info, err := manager.Get(name) + info, err := Manager.Get(name) if err != nil { fmt.Println(err.Error()) return diff --git a/cmd/keys/main.go b/cmd/keys/main.go index 8c880ea03..40bf31808 100644 --- a/cmd/keys/main.go +++ b/cmd/keys/main.go @@ -14,8 +14,15 @@ package main -import "github.com/tendermint/go-keys/cmd" +import ( + "os" + + "github.com/tendermint/go-keys/cmd" +) func main() { - cmd.Execute() + cmd.RootCmd.PersistentPreRunE = cmd.SetupKeys + cmd.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc")) + cmd.RootCmd.Execute() + // exec() } diff --git a/cmd/list.go b/cmd/list.go index 44fefb8d6..bf4d14b68 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -27,7 +27,7 @@ var listCmd = &cobra.Command{ Long: `Return a list of all public keys stored by this key manager along with their associated name and address.`, Run: func(cmd *cobra.Command, args []string) { - infos, err := manager.List() + infos, err := Manager.List() if err != nil { fmt.Println(err.Error()) return diff --git a/cmd/new.go b/cmd/new.go index 27c7dce75..3564ddf90 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -50,7 +50,7 @@ func newPassword(cmd *cobra.Command, args []string) { return } - info, err := manager.Create(name, pass, algo) + info, err := Manager.Create(name, pass, algo) if err != nil { fmt.Println(err.Error()) return diff --git a/cmd/root.go b/cmd/root.go index 41803fc65..ffe5cc36b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -15,26 +15,19 @@ package cmd import ( - "fmt" - "os" "path/filepath" - "strings" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - data "github.com/tendermint/go-data" - "github.com/tendermint/go-data/base58" keys "github.com/tendermint/go-keys" "github.com/tendermint/go-keys/cryptostore" "github.com/tendermint/go-keys/storage/filestorage" ) +const KeySubdir = "keys" + var ( - rootDir string - output string - keyDir string - manager keys.Manager + Manager keys.Manager ) // RootCmd represents the base command when called without any subcommands @@ -46,87 +39,19 @@ var RootCmd = &cobra.Command{ 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.`, - PersistentPreRunE: bindFlags, -} - -// Execute adds all child commands to the root command sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := RootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } -} - -func init() { - cobra.OnInitialize(initEnv) - RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data") - RootCmd.PersistentFlags().String("keydir", "keys", "Directory to store private keys (subdir of root)") - RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)") - RootCmd.PersistentFlags().StringP("encoding", "e", "hex", "Binary encoding (hex|b64|btc)") -} - -// initEnv sets to use ENV variables if set. -func initEnv() { - // env variables with TM prefix (eg. TM_ROOT) - viper.SetEnvPrefix("TM") - viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - viper.AutomaticEnv() } -func bindFlags(cmd *cobra.Command, args []string) error { - // cmd.Flags() includes flags from this command and all persistent flags from the parent - if err := viper.BindPFlags(cmd.Flags()); err != nil { - return err - } - - // rootDir is command line flag, env variable, or default $HOME/.tlc - rootDir = viper.GetString("root") - viper.SetConfigName("keys") // name of config file (without extension) - viper.AddConfigPath(rootDir) // search root directory - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - // stderr, so if we redirect output to json file, this doesn't appear - fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) - } - - return validateFlags(cmd) -} - -// validateFlags asserts all RootCmd flags are valid -func validateFlags(cmd *cobra.Command) error { - // validate output format - output = viper.GetString("output") - switch output { - case "text", "json": - default: - return errors.Errorf("Unsupported output format: %s", output) - } - - // validate and set encoding - enc := viper.GetString("encoding") - switch enc { - case "hex": - data.Encoder = data.HexEncoder - case "b64": - data.Encoder = data.B64Encoder - case "btc": - data.Encoder = base58.BTCEncoder - default: - return errors.Errorf("Unsupported encoding: %s", enc) - } - +// SetupKeys must be registered in main() on the top level command +// here this is RootCmd, but if we embed keys in eg. light-client, +// that must be responsible for the update +func SetupKeys(cmd *cobra.Command, args []string) error { // store the keys directory - keyDir = viper.GetString("keydir") - if !filepath.IsAbs(keyDir) { - keyDir = filepath.Join(rootDir, keyDir) - } + rootDir := viper.GetString("root") + keyDir := filepath.Join(rootDir, KeySubdir) // and construct the key manager - manager = cryptostore.New( + Manager = cryptostore.New( cryptostore.SecretBox, filestorage.New(keyDir), ) - return nil } diff --git a/cmd/serve.go b/cmd/serve.go index 2717913fd..5268f0b92 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -64,7 +64,7 @@ func serveHTTP(cmd *cobra.Command, args []string) error { } router := mux.NewRouter() - ks := server.New(manager, viper.GetString("type")) + ks := server.New(Manager, viper.GetString("type")) ks.Register(router) // only set cors for tcp listener diff --git a/cmd/update.go b/cmd/update.go index 0340944be..63b6f84df 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -50,7 +50,7 @@ func updatePassword(cmd *cobra.Command, args []string) { return } - err = manager.Update(name, oldpass, newpass) + err = Manager.Update(name, oldpass, newpass) if err != nil { fmt.Println(err.Error()) } else { diff --git a/cmd/utils.go b/cmd/utils.go index 01f9801ae..31a3a8df3 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -5,6 +5,7 @@ import ( "github.com/bgentry/speakeasy" "github.com/pkg/errors" + "github.com/spf13/viper" data "github.com/tendermint/go-data" keys "github.com/tendermint/go-keys" ) @@ -39,7 +40,7 @@ func getCheckPassword(prompt, prompt2 string) (string, error) { } func printInfo(info keys.Info) { - switch output { + switch viper.Get(OutputFlag) { case "text": addr, err := data.ToText(info.Address) if err != nil { @@ -60,7 +61,7 @@ func printInfo(info keys.Info) { } func printInfos(infos keys.Infos) { - switch output { + switch viper.Get(OutputFlag) { case "text": fmt.Println("All keys:") for _, i := range infos { From 58e537a42de4de0c7719171123695c5d0ed7b97c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 28 Mar 2017 19:05:33 +0200 Subject: [PATCH 15/45] Refactor setting up the key manager from config --- cmd/common.go | 7 ++++--- cmd/get.go | 2 +- cmd/keys/main.go | 1 - cmd/list.go | 2 +- cmd/new.go | 2 +- cmd/root.go | 28 ++++++++++++++-------------- cmd/serve.go | 2 +- cmd/update.go | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cmd/common.go b/cmd/common.go index 56c9afbab..eb7a158ae 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -22,14 +22,15 @@ It is here for experimentation of re-use between go-keys and light-client. *********/ const ( - RootFlag = "root" - OutputFlag = "output" + RootFlag = "root" + OutputFlag = "output" + EncodingFlag = "encoding" ) func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() { cobra.OnInitialize(func() { initEnv(envPrefix) }) cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data") - cmd.PersistentFlags().StringP("encoding", "e", "hex", "Binary encoding (hex|b64|btc)") + cmd.PersistentFlags().StringP(EncodingFlag, "e", "hex", "Binary encoding (hex|b64|btc)") cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)") cmd.PersistentPreRunE = multiE(bindFlags, setEncoding, validateOutput, cmd.PersistentPreRunE) return func() { execute(cmd) } diff --git a/cmd/get.go b/cmd/get.go index 10d528807..9b8718996 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -32,7 +32,7 @@ var getCmd = &cobra.Command{ } name := args[0] - info, err := Manager.Get(name) + info, err := GetKeyManager().Get(name) if err != nil { fmt.Println(err.Error()) return diff --git a/cmd/keys/main.go b/cmd/keys/main.go index 40bf31808..8b92b3f49 100644 --- a/cmd/keys/main.go +++ b/cmd/keys/main.go @@ -21,7 +21,6 @@ import ( ) func main() { - cmd.RootCmd.PersistentPreRunE = cmd.SetupKeys cmd.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc")) cmd.RootCmd.Execute() // exec() diff --git a/cmd/list.go b/cmd/list.go index bf4d14b68..875520159 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -27,7 +27,7 @@ var listCmd = &cobra.Command{ Long: `Return a list of all public keys stored by this key manager along with their associated name and address.`, Run: func(cmd *cobra.Command, args []string) { - infos, err := Manager.List() + infos, err := GetKeyManager().List() if err != nil { fmt.Println(err.Error()) return diff --git a/cmd/new.go b/cmd/new.go index 3564ddf90..b59874bb0 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -50,7 +50,7 @@ func newPassword(cmd *cobra.Command, args []string) { return } - info, err := Manager.Create(name, pass, algo) + info, err := GetKeyManager().Create(name, pass, algo) if err != nil { fmt.Println(err.Error()) return diff --git a/cmd/root.go b/cmd/root.go index ffe5cc36b..64adf2b0b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,7 +27,7 @@ import ( const KeySubdir = "keys" var ( - Manager keys.Manager + manager keys.Manager ) // RootCmd represents the base command when called without any subcommands @@ -41,17 +41,17 @@ used by light-clients, full nodes, or any other application that needs to sign with a private key.`, } -// SetupKeys must be registered in main() on the top level command -// here this is RootCmd, but if we embed keys in eg. light-client, -// that must be responsible for the update -func SetupKeys(cmd *cobra.Command, args []string) error { - // store the keys directory - rootDir := viper.GetString("root") - keyDir := filepath.Join(rootDir, KeySubdir) - // and construct the key manager - Manager = cryptostore.New( - cryptostore.SecretBox, - filestorage.New(keyDir), - ) - return nil +// GetKeyManager initializes a key manager based on the configuration +func GetKeyManager() keys.Manager { + if manager == nil { + // store the keys directory + rootDir := viper.GetString("root") + keyDir := filepath.Join(rootDir, KeySubdir) + // and construct the key manager + manager = cryptostore.New( + cryptostore.SecretBox, + filestorage.New(keyDir), + ) + } + return manager } diff --git a/cmd/serve.go b/cmd/serve.go index 5268f0b92..c3f7dbe39 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -64,7 +64,7 @@ func serveHTTP(cmd *cobra.Command, args []string) error { } router := mux.NewRouter() - ks := server.New(Manager, viper.GetString("type")) + ks := server.New(GetKeyManager(), viper.GetString("type")) ks.Register(router) // only set cors for tcp listener diff --git a/cmd/update.go b/cmd/update.go index 63b6f84df..3835242c5 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -50,7 +50,7 @@ func updatePassword(cmd *cobra.Command, args []string) { return } - err = Manager.Update(name, oldpass, newpass) + err = GetKeyManager().Update(name, oldpass, newpass) if err != nil { fmt.Println(err.Error()) } else { From ae55713864f0679dfa628b23654d462bb12a109a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 29 Mar 2017 20:41:11 +0200 Subject: [PATCH 16/45] Move tx from light-client and add tests --- tx/docs.go | 10 ++++++ tx/multi.go | 67 +++++++++++++++++++++++++++++++++++++++++ tx/multi_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++++++ tx/one.go | 58 +++++++++++++++++++++++++++++++++++ tx/one_test.go | 73 ++++++++++++++++++++++++++++++++++++++++++++ tx/reader.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++ tx/reader_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 431 insertions(+) create mode 100644 tx/docs.go create mode 100644 tx/multi.go create mode 100644 tx/multi_test.go create mode 100644 tx/one.go create mode 100644 tx/one_test.go create mode 100644 tx/reader.go create mode 100644 tx/reader_test.go diff --git a/tx/docs.go b/tx/docs.go new file mode 100644 index 000000000..6a5ea3ce3 --- /dev/null +++ b/tx/docs.go @@ -0,0 +1,10 @@ +/* +package tx contains generic Signable implementations that can be used +by your application or tests to handle authentication needs. + +It currently supports transaction data as opaque bytes and either single +or multiple private key signatures using straightforward algorithms. +It currently does not support N-of-M key share signing of other more +complex algorithms (although it would be great to add them) +*/ +package tx diff --git a/tx/multi.go b/tx/multi.go new file mode 100644 index 000000000..eac86b010 --- /dev/null +++ b/tx/multi.go @@ -0,0 +1,67 @@ +package tx + +import ( + "github.com/pkg/errors" + crypto "github.com/tendermint/go-crypto" + data "github.com/tendermint/go-data" +) + +// MultiSig lets us wrap arbitrary data with a go-crypto signature +// +// TODO: rethink how we want to integrate this with KeyStore so it makes +// more sense (particularly the verify method) +type MultiSig struct { + Data data.Bytes + Sigs []Signed +} + +type Signed struct { + Sig crypto.SignatureS + Pubkey crypto.PubKeyS +} + +var _ SigInner = &MultiSig{} + +func NewMulti(data []byte) Sig { + return Sig{&MultiSig{Data: data}} +} + +// SignBytes returns the original data passed into `NewSig` +func (s *MultiSig) SignBytes() []byte { + return s.Data +} + +// Sign will add a signature and pubkey. +// +// Depending on the Signable, one may be able to call this multiple times for multisig +// Returns error if called with invalid data or too many times +func (s *MultiSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { + if pubkey == nil || sig == nil { + return errors.New("Signature or Key missing") + } + + // set the value once we are happy + x := Signed{crypto.SignatureS{sig}, crypto.PubKeyS{pubkey}} + s.Sigs = append(s.Sigs, x) + return nil +} + +// Signers will return the public key(s) that signed if the signature +// is valid, or an error if there is any issue with the signature, +// including if there are no signatures +func (s *MultiSig) Signers() ([]crypto.PubKey, error) { + if len(s.Sigs) == 0 { + return nil, errors.New("Never signed") + } + + keys := make([]crypto.PubKey, len(s.Sigs)) + for i := range s.Sigs { + ms := s.Sigs[i] + if !ms.Pubkey.VerifyBytes(s.Data, ms.Sig) { + return nil, errors.Errorf("Signature %d doesn't match (key: %X)", i, ms.Pubkey.Bytes()) + } + keys[i] = ms.Pubkey + } + + return keys, nil +} diff --git a/tx/multi_test.go b/tx/multi_test.go new file mode 100644 index 000000000..815b9e15a --- /dev/null +++ b/tx/multi_test.go @@ -0,0 +1,77 @@ +package tx + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-keys" + "github.com/tendermint/go-keys/cryptostore" + "github.com/tendermint/go-keys/storage/memstorage" +) + +func TestMultiSig(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + algo := crypto.NameEd25519 + cstore := cryptostore.New( + cryptostore.SecretBox, + memstorage.New(), + ) + n, p := "foo", "bar" + n2, p2 := "other", "thing" + + acct, err := cstore.Create(n, p, algo) + require.Nil(err, "%+v", err) + acct2, err := cstore.Create(n2, p2, algo) + require.Nil(err, "%+v", err) + + type signer struct { + key keys.Info + name, pass string + } + cases := []struct { + data string + signers []signer + }{ + {"one", []signer{{acct, n, p}}}, + {"two", []signer{{acct2, n2, p2}}}, + {"both", []signer{{acct, n, p}, {acct2, n2, p2}}}, + } + + for _, tc := range cases { + tx := NewMulti([]byte(tc.data)) + // unsigned version + _, err = tx.Signers() + assert.NotNil(err) + orig, err := tx.TxBytes() + require.Nil(err, "%+v", err) + data := tx.SignBytes() + assert.Equal(tc.data, string(data)) + + // sign it + for _, s := range tc.signers { + err = cstore.Sign(s.name, s.pass, tx) + require.Nil(err, "%+v", err) + } + + // make sure it is proper now + sigs, err := tx.Signers() + require.Nil(err, "%+v", err) + if assert.Equal(len(tc.signers), len(sigs)) { + for i := range sigs { + // This must be refactored... + assert.Equal(tc.signers[i].key.PubKey, sigs[i]) + } + } + // the tx bytes should change after this + after, err := tx.TxBytes() + require.Nil(err, "%+v", err) + assert.NotEqual(orig, after, "%X != %X", orig, after) + + // sign bytes are the same + data = tx.SignBytes() + assert.Equal(tc.data, string(data)) + } +} diff --git a/tx/one.go b/tx/one.go new file mode 100644 index 000000000..0ad61dd50 --- /dev/null +++ b/tx/one.go @@ -0,0 +1,58 @@ +package tx + +import ( + "github.com/pkg/errors" + crypto "github.com/tendermint/go-crypto" + data "github.com/tendermint/go-data" +) + +// OneSig lets us wrap arbitrary data with a go-crypto signature +// +// TODO: rethink how we want to integrate this with KeyStore so it makes +// more sense (particularly the verify method) +type OneSig struct { + Data data.Bytes + Signed +} + +var _ SigInner = &OneSig{} + +func New(data []byte) Sig { + return WrapSig(&OneSig{Data: data}) +} + +// SignBytes returns the original data passed into `NewSig` +func (s *OneSig) SignBytes() []byte { + return s.Data +} + +// Sign will add a signature and pubkey. +// +// Depending on the Signable, one may be able to call this multiple times for multisig +// Returns error if called with invalid data or too many times +func (s *OneSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { + if pubkey == nil || sig == nil { + return errors.New("Signature or Key missing") + } + if !s.Sig.Empty() { + return errors.New("Transaction can only be signed once") + } + + // set the value once we are happy + s.Pubkey = crypto.PubKeyS{pubkey} + s.Sig = crypto.SignatureS{sig} + return nil +} + +// Signers will return the public key(s) that signed if the signature +// is valid, or an error if there is any issue with the signature, +// including if there are no signatures +func (s *OneSig) Signers() ([]crypto.PubKey, error) { + if s.Pubkey.Empty() || s.Sig.Empty() { + return nil, errors.New("Never signed") + } + if !s.Pubkey.VerifyBytes(s.Data, s.Sig) { + return nil, errors.New("Signature doesn't match") + } + return []crypto.PubKey{s.Pubkey}, nil +} diff --git a/tx/one_test.go b/tx/one_test.go new file mode 100644 index 000000000..71b8011ef --- /dev/null +++ b/tx/one_test.go @@ -0,0 +1,73 @@ +package tx + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-keys" + "github.com/tendermint/go-keys/cryptostore" + "github.com/tendermint/go-keys/storage/memstorage" +) + +func TestOneSig(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + algo := crypto.NameEd25519 + cstore := cryptostore.New( + cryptostore.SecretBox, + memstorage.New(), + ) + n, p := "foo", "bar" + n2, p2 := "other", "thing" + + acct, err := cstore.Create(n, p, algo) + require.Nil(err, "%+v", err) + acct2, err := cstore.Create(n2, p2, algo) + require.Nil(err, "%+v", err) + + cases := []struct { + data string + key keys.Info + name, pass string + }{ + {"first", acct, n, p}, + {"kehfkhefy8y", acct, n, p}, + {"second", acct2, n2, p2}, + } + + for _, tc := range cases { + tx := New([]byte(tc.data)) + // unsigned version + _, err = tx.Signers() + assert.NotNil(err) + orig, err := tx.TxBytes() + require.Nil(err, "%+v", err) + data := tx.SignBytes() + assert.Equal(tc.data, string(data)) + + // sign it + err = cstore.Sign(tc.name, tc.pass, tx) + require.Nil(err, "%+v", err) + // but not twice + err = cstore.Sign(tc.name, tc.pass, tx) + require.NotNil(err) + + // make sure it is proper now + sigs, err := tx.Signers() + require.Nil(err, "%+v", err) + if assert.Equal(1, len(sigs)) { + // This must be refactored... + assert.Equal(tc.key.PubKey, sigs[0]) + } + // the tx bytes should change after this + after, err := tx.TxBytes() + require.Nil(err, "%+v", err) + assert.NotEqual(orig, after, "%X != %X", orig, after) + + // sign bytes are the same + data = tx.SignBytes() + assert.Equal(tc.data, string(data)) + } +} diff --git a/tx/reader.go b/tx/reader.go new file mode 100644 index 000000000..7689693cb --- /dev/null +++ b/tx/reader.go @@ -0,0 +1,76 @@ +package tx + +import ( + crypto "github.com/tendermint/go-crypto" + data "github.com/tendermint/go-data" + keys "github.com/tendermint/go-keys" +) + +const ( + typeOneSig = byte(0x01) + typeMultiSig = byte(0x02) + nameOneSig = "sig" + nameMultiSig = "multi" +) + +var _ keys.Signable = Sig{} +var TxMapper data.Mapper + +func init() { + TxMapper = data.NewMapper(Sig{}). + RegisterInterface(&OneSig{}, nameOneSig, typeOneSig). + RegisterInterface(&MultiSig{}, nameMultiSig, typeMultiSig) +} + +/* +DO NOT USE this interface. + +It is public by necessity but should never be used directly +outside of this package. + +Only use Sig, never SigInner +*/ +type SigInner interface { + SignBytes() []byte + Sign(pubkey crypto.PubKey, sig crypto.Signature) error + Signers() ([]crypto.PubKey, error) +} + +// Sig is what is exported, and handles serialization +type Sig struct { + SigInner +} + +// TxBytes +func (s Sig) TxBytes() ([]byte, error) { + return data.ToWire(s) +} + +// WrapSig goes from concrete implementation to "interface" struct +func WrapSig(pk SigInner) Sig { + if wrap, ok := pk.(Sig); ok { + pk = wrap.Unwrap() + } + return Sig{pk} +} + +// Unwrap recovers the concrete interface safely (regardless of levels of embeds) +func (p Sig) Unwrap() SigInner { + pk := p.SigInner + for wrap, ok := pk.(Sig); ok; wrap, ok = pk.(Sig) { + pk = wrap.SigInner + } + return pk +} + +func (p Sig) MarshalJSON() ([]byte, error) { + return TxMapper.ToJSON(p.Unwrap()) +} + +func (p *Sig) UnmarshalJSON(data []byte) (err error) { + parsed, err := TxMapper.FromJSON(data) + if err == nil && parsed != nil { + p.SigInner = parsed.(SigInner) + } + return +} diff --git a/tx/reader_test.go b/tx/reader_test.go new file mode 100644 index 000000000..54b76af96 --- /dev/null +++ b/tx/reader_test.go @@ -0,0 +1,70 @@ +package tx + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" + data "github.com/tendermint/go-data" + "github.com/tendermint/go-keys/cryptostore" + "github.com/tendermint/go-keys/storage/memstorage" +) + +func TestReader(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + algo := crypto.NameEd25519 + cstore := cryptostore.New( + cryptostore.SecretBox, + memstorage.New(), + ) + type sigs struct{ name, pass string } + u := sigs{"alice", "1234"} + u2 := sigs{"bob", "foobar"} + + _, err := cstore.Create(u.name, u.pass, algo) + require.Nil(err, "%+v", err) + _, err = cstore.Create(u2.name, u2.pass, algo) + require.Nil(err, "%+v", err) + + cases := []struct { + tx Sig + sigs []sigs + }{ + {New([]byte("first")), nil}, + {New([]byte("second")), []sigs{u}}, + {New([]byte("other")), []sigs{u2}}, + {NewMulti([]byte("m-first")), nil}, + {NewMulti([]byte("m-second")), []sigs{u}}, + {NewMulti([]byte("m-other")), []sigs{u, u2}}, + } + + for _, tc := range cases { + tx := tc.tx + + // make sure json serialization and loading works w/o sigs + var pre Sig + pjs, err := data.ToJSON(tx) + require.Nil(err, "%+v", err) + err = data.FromJSON(pjs, &pre) + require.Nil(err, "%+v", err) + assert.Equal(tx, pre) + + for _, s := range tc.sigs { + err = cstore.Sign(s.name, s.pass, tx) + require.Nil(err, "%+v", err) + } + + var post Sig + sjs, err := data.ToJSON(tx) + require.Nil(err, "%+v", err) + err = data.FromJSON(sjs, &post) + require.Nil(err, "%+v\n%s", err, string(sjs)) + assert.Equal(tx, post) + + if len(tc.sigs) > 0 { + assert.NotEqual(pjs, sjs, "%s\n ------ %s", string(pjs), string(sjs)) + } + } +} From 66ecd7705fa16cb786cbcfc00513aa88b8251230 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 21 Mar 2017 21:44:24 +0100 Subject: [PATCH 17/45] Use new naming scheme PubKey{PubKeyInner} --- embed_test.go | 1 - encode_test.go | 22 ++++++++--------- priv_key.go | 57 +++++++++++++++++++++++++------------------ pub_key.go | 62 +++++++++++++++++++++++------------------------ pub_key_test.go | 2 +- signature.go | 49 ++++++++++++++++++++++--------------- signature_test.go | 38 ++++++++++++++--------------- 7 files changed, 124 insertions(+), 107 deletions(-) diff --git a/embed_test.go b/embed_test.go index 71852848e..77b821f03 100644 --- a/embed_test.go +++ b/embed_test.go @@ -103,7 +103,6 @@ func TestEncodeDemo(t *testing.T) { for i, tc := range cases { // make sure it is proper to start require.Equal(tc.expected, tc.in.Greet()) - fmt.Println(tc.expected) // now, try to encode as binary b, err := data.ToWire(tc.in) diff --git a/encode_test.go b/encode_test.go index 977cc64cf..667132a6f 100644 --- a/encode_test.go +++ b/encode_test.go @@ -50,17 +50,17 @@ func checkJSON(t *testing.T, in interface{}, reader interface{}, typ string) { func TestKeyEncodings(t *testing.T) { cases := []struct { - privKey PrivKeyS + privKey PrivKey keyType byte keyName string }{ { - privKey: PrivKeyS{GenPrivKeyEd25519()}, + privKey: WrapPrivKey(GenPrivKeyEd25519()), keyType: TypeEd25519, keyName: NameEd25519, }, { - privKey: PrivKeyS{GenPrivKeySecp256k1()}, + privKey: WrapPrivKey(GenPrivKeySecp256k1()), keyType: TypeSecp256k1, keyName: NameSecp256k1, }, @@ -68,19 +68,19 @@ func TestKeyEncodings(t *testing.T) { for _, tc := range cases { // check (de/en)codings of private key - priv2 := PrivKeyS{} + priv2 := PrivKey{} checkWire(t, tc.privKey, &priv2, tc.keyType) assert.EqualValues(t, tc.privKey, priv2) - priv3 := PrivKeyS{} + priv3 := PrivKey{} checkJSON(t, tc.privKey, &priv3, tc.keyName) assert.EqualValues(t, tc.privKey, priv3) // check (de/en)codings of public key - pubKey := PubKeyS{tc.privKey.PubKey()} - pub2 := PubKeyS{} + pubKey := tc.privKey.PubKey() + pub2 := PubKey{} checkWire(t, pubKey, &pub2, tc.keyType) assert.EqualValues(t, pubKey, pub2) - pub3 := PubKeyS{} + pub3 := PubKey{} checkJSON(t, pubKey, &pub3, tc.keyName) assert.EqualValues(t, pubKey, pub3) } @@ -95,17 +95,17 @@ func toFromJSON(t *testing.T, in interface{}, recvr interface{}) { func TestNilEncodings(t *testing.T) { // make sure sigs are okay with nil - a, b := SignatureS{}, SignatureS{} + a, b := Signature{}, Signature{} toFromJSON(t, a, &b) assert.EqualValues(t, a, b) // make sure sigs are okay with nil - c, d := PubKeyS{}, PubKeyS{} + c, d := PubKey{}, PubKey{} toFromJSON(t, c, &d) assert.EqualValues(t, c, d) // make sure sigs are okay with nil - e, f := PrivKeyS{}, PrivKeyS{} + e, f := PrivKey{}, PrivKey{} toFromJSON(t, e, &f) assert.EqualValues(t, e, f) diff --git a/priv_key.go b/priv_key.go index 3a9a5f69b..f00a762d8 100644 --- a/priv_key.go +++ b/priv_key.go @@ -11,8 +11,8 @@ import ( "github.com/tendermint/go-wire" ) -// PrivKey is part of PrivAccount and state.PrivValidator. -type PrivKey interface { +// PrivKeyInner is now the interface itself, use PrivKey in all code +type PrivKeyInner interface { Bytes() []byte Sign(msg []byte) Signature PubKey() PubKey @@ -31,37 +31,45 @@ var privKeyMapper data.Mapper // register both private key types with go-data (and thus go-wire) func init() { - privKeyMapper = data.NewMapper(PrivKeyS{}). + privKeyMapper = data.NewMapper(PrivKey{}). RegisterImplementation(PrivKeyEd25519{}, NameEd25519, TypeEd25519). RegisterImplementation(PrivKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) } -// PrivKeyS add json serialization to PrivKey -type PrivKeyS struct { - PrivKey +// PrivKey handles all encoding and exposes methods +type PrivKey struct { + PrivKeyInner } -func WrapPrivKey(pk PrivKey) PrivKeyS { - for ppk, ok := pk.(PrivKeyS); ok; ppk, ok = pk.(PrivKeyS) { - pk = ppk.PrivKey +func WrapPrivKey(pk PrivKeyInner) PrivKey { + if wrap, ok := pk.(PrivKey); ok { + pk = wrap.Unwrap() } - return PrivKeyS{pk} + return PrivKey{pk} } -func (p PrivKeyS) MarshalJSON() ([]byte, error) { - return privKeyMapper.ToJSON(p.PrivKey) +func (p PrivKey) Unwrap() PrivKeyInner { + pk := p.PrivKeyInner + for wrap, ok := pk.(PrivKey); ok; wrap, ok = pk.(PrivKey) { + pk = wrap.PrivKeyInner + } + return pk +} + +func (p PrivKey) MarshalJSON() ([]byte, error) { + return privKeyMapper.ToJSON(p.PrivKeyInner) } -func (p *PrivKeyS) UnmarshalJSON(data []byte) (err error) { +func (p *PrivKey) UnmarshalJSON(data []byte) (err error) { parsed, err := privKeyMapper.FromJSON(data) if err == nil && parsed != nil { - p.PrivKey = parsed.(PrivKey) + p.PrivKeyInner = parsed.(PrivKeyInner) } return } -func (p PrivKeyS) Empty() bool { - return p.PrivKey == nil +func (p PrivKey) Empty() bool { + return p.PrivKeyInner == nil } func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { @@ -75,22 +83,23 @@ func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { type PrivKeyEd25519 [64]byte func (privKey PrivKeyEd25519) Bytes() []byte { - return wire.BinaryBytes(struct{ PrivKey }{privKey}) + return wire.BinaryBytes(PrivKey{privKey}) } func (privKey PrivKeyEd25519) Sign(msg []byte) Signature { privKeyBytes := [64]byte(privKey) signatureBytes := ed25519.Sign(&privKeyBytes, msg) - return SignatureEd25519(*signatureBytes) + return WrapSignature(SignatureEd25519(*signatureBytes)) } func (privKey PrivKeyEd25519) PubKey() PubKey { privKeyBytes := [64]byte(privKey) - return PubKeyEd25519(*ed25519.MakePublicKey(&privKeyBytes)) + pubBytes := *ed25519.MakePublicKey(&privKeyBytes) + return WrapPubKey(PubKeyEd25519(pubBytes)) } func (privKey PrivKeyEd25519) Equals(other PrivKey) bool { - if otherEd, ok := other.(PrivKeyEd25519); ok { + if otherEd, ok := other.Unwrap().(PrivKeyEd25519); ok { return bytes.Equal(privKey[:], otherEd[:]) } else { return false @@ -153,7 +162,7 @@ func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { type PrivKeySecp256k1 [32]byte func (privKey PrivKeySecp256k1) Bytes() []byte { - return wire.BinaryBytes(struct{ PrivKey }{privKey}) + return wire.BinaryBytes(PrivKey{privKey}) } func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature { @@ -162,18 +171,18 @@ func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature { if err != nil { PanicSanity(err) } - return SignatureSecp256k1(sig__.Serialize()) + return WrapSignature(SignatureSecp256k1(sig__.Serialize())) } func (privKey PrivKeySecp256k1) PubKey() PubKey { _, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) var pub PubKeySecp256k1 copy(pub[:], pub__.SerializeCompressed()) - return pub + return WrapPubKey(pub) } func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool { - if otherSecp, ok := other.(PrivKeySecp256k1); ok { + if otherSecp, ok := other.Unwrap().(PrivKeySecp256k1); ok { return bytes.Equal(privKey[:], otherSecp[:]) } else { return false diff --git a/pub_key.go b/pub_key.go index a047edb2f..fa91b12f4 100644 --- a/pub_key.go +++ b/pub_key.go @@ -13,8 +13,8 @@ import ( "golang.org/x/crypto/ripemd160" ) -// PubKey is part of Account and Validator. -type PubKey interface { +// PubKeyInner is now the interface itself, use PubKey struct in all code +type PubKeyInner interface { Address() []byte Bytes() []byte KeyString() string @@ -26,37 +26,45 @@ var pubKeyMapper data.Mapper // register both public key types with go-data (and thus go-wire) func init() { - pubKeyMapper = data.NewMapper(PubKeyS{}). + pubKeyMapper = data.NewMapper(PubKey{}). RegisterImplementation(PubKeyEd25519{}, NameEd25519, TypeEd25519). RegisterImplementation(PubKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) } -// PubKeyS add json serialization to PubKey -type PubKeyS struct { - PubKey +// PubKey add json serialization to PubKeyInner +type PubKey struct { + PubKeyInner } -func WrapPubKey(pk PubKey) PubKeyS { - for ppk, ok := pk.(PubKeyS); ok; ppk, ok = pk.(PubKeyS) { - pk = ppk.PubKey +func WrapPubKey(pk PubKeyInner) PubKey { + if wrap, ok := pk.(PubKey); ok { + pk = wrap.Unwrap() } - return PubKeyS{pk} + return PubKey{pk} } -func (p PubKeyS) MarshalJSON() ([]byte, error) { - return pubKeyMapper.ToJSON(p.PubKey) +func (p PubKey) Unwrap() PubKeyInner { + pk := p.PubKeyInner + for wrap, ok := pk.(PubKey); ok; wrap, ok = pk.(PubKey) { + pk = wrap.PubKeyInner + } + return pk +} + +func (p PubKey) MarshalJSON() ([]byte, error) { + return pubKeyMapper.ToJSON(p.PubKeyInner) } -func (p *PubKeyS) UnmarshalJSON(data []byte) (err error) { +func (p *PubKey) UnmarshalJSON(data []byte) (err error) { parsed, err := pubKeyMapper.FromJSON(data) if err == nil && parsed != nil { - p.PubKey = parsed.(PubKey) + p.PubKeyInner = parsed.(PubKeyInner) } return } -func (p PubKeyS) Empty() bool { - return p.PubKey == nil +func (p PubKey) Empty() bool { + return p.PubKeyInner == nil } func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { @@ -66,7 +74,7 @@ func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { //------------------------------------- -// Implements PubKey +// Implements PubKeyInner type PubKeyEd25519 [32]byte func (pubKey PubKeyEd25519) Address() []byte { @@ -83,16 +91,12 @@ func (pubKey PubKeyEd25519) Address() []byte { } func (pubKey PubKeyEd25519) Bytes() []byte { - return wire.BinaryBytes(struct{ PubKey }{pubKey}) + return wire.BinaryBytes(PubKey{pubKey}) } func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { - // unwrap if needed - if wrap, ok := sig_.(SignatureS); ok { - sig_ = wrap.Signature - } // make sure we use the same algorithm to sign - sig, ok := sig_.(SignatureEd25519) + sig, ok := sig_.Unwrap().(SignatureEd25519) if !ok { return false } @@ -134,7 +138,7 @@ func (pubKey PubKeyEd25519) KeyString() string { } func (pubKey PubKeyEd25519) Equals(other PubKey) bool { - if otherEd, ok := other.(PubKeyEd25519); ok { + if otherEd, ok := other.Unwrap().(PubKeyEd25519); ok { return bytes.Equal(pubKey[:], otherEd[:]) } else { return false @@ -160,16 +164,12 @@ func (pubKey PubKeySecp256k1) Address() []byte { } func (pubKey PubKeySecp256k1) Bytes() []byte { - return wire.BinaryBytes(struct{ PubKey }{pubKey}) + return wire.BinaryBytes(PubKey{pubKey}) } func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool { - // unwrap if needed - if wrap, ok := sig_.(SignatureS); ok { - sig_ = wrap.Signature - } // and assert same algorithm to sign and verify - sig, ok := sig_.(SignatureSecp256k1) + sig, ok := sig_.Unwrap().(SignatureSecp256k1) if !ok { return false } @@ -207,7 +207,7 @@ func (pubKey PubKeySecp256k1) KeyString() string { } func (pubKey PubKeySecp256k1) Equals(other PubKey) bool { - if otherSecp, ok := other.(PubKeySecp256k1); ok { + if otherSecp, ok := other.Unwrap().(PubKeySecp256k1); ok { return bytes.Equal(pubKey[:], otherSecp[:]) } else { return false diff --git a/pub_key_test.go b/pub_key_test.go index 0616f5546..31642233c 100644 --- a/pub_key_test.go +++ b/pub_key_test.go @@ -31,7 +31,7 @@ func TestPubKeySecp256k1Address(t *testing.T) { var priv PrivKeySecp256k1 copy(priv[:], privB) - pubT := priv.PubKey().(PubKeySecp256k1) + pubT := priv.PubKey().Unwrap().(PubKeySecp256k1) pub := pubT[:] addr := priv.PubKey().Address() diff --git a/signature.go b/signature.go index bb9d82009..e7b6b1745 100644 --- a/signature.go +++ b/signature.go @@ -9,8 +9,9 @@ import ( "github.com/tendermint/go-wire" ) -// Signature is a part of Txs and consensus Votes. -type Signature interface { +// SignatureInner is now the interface itself. +// Use Signature in all code +type SignatureInner interface { Bytes() []byte IsZero() bool String() string @@ -21,37 +22,45 @@ var sigMapper data.Mapper // register both public key types with go-data (and thus go-wire) func init() { - sigMapper = data.NewMapper(SignatureS{}). + sigMapper = data.NewMapper(Signature{}). RegisterImplementation(SignatureEd25519{}, NameEd25519, TypeEd25519). RegisterImplementation(SignatureSecp256k1{}, NameSecp256k1, TypeSecp256k1) } -// SignatureS add json serialization to Signature -type SignatureS struct { - Signature +// Signature add json serialization to Signature +type Signature struct { + SignatureInner } -func WrapSignature(sig Signature) SignatureS { - for ssig, ok := sig.(SignatureS); ok; ssig, ok = sig.(SignatureS) { - sig = ssig.Signature +func WrapSignature(pk SignatureInner) Signature { + if wrap, ok := pk.(Signature); ok { + pk = wrap.Unwrap() } - return SignatureS{sig} + return Signature{pk} } -func (p SignatureS) MarshalJSON() ([]byte, error) { - return sigMapper.ToJSON(p.Signature) +func (p Signature) Unwrap() SignatureInner { + pk := p.SignatureInner + for wrap, ok := pk.(Signature); ok; wrap, ok = pk.(Signature) { + pk = wrap.SignatureInner + } + return pk +} + +func (p Signature) MarshalJSON() ([]byte, error) { + return sigMapper.ToJSON(p.SignatureInner) } -func (p *SignatureS) UnmarshalJSON(data []byte) (err error) { +func (p *Signature) UnmarshalJSON(data []byte) (err error) { parsed, err := sigMapper.FromJSON(data) if err == nil && parsed != nil { - p.Signature = parsed.(Signature) + p.SignatureInner = parsed.(SignatureInner) } return } -func (p SignatureS) Empty() bool { - return p.Signature == nil +func (p Signature) Empty() bool { + return p.SignatureInner == nil } func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { @@ -65,7 +74,7 @@ func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { type SignatureEd25519 [64]byte func (sig SignatureEd25519) Bytes() []byte { - return wire.BinaryBytes(struct{ Signature }{sig}) + return wire.BinaryBytes(Signature{sig}) } func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } @@ -73,7 +82,7 @@ func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } func (sig SignatureEd25519) Equals(other Signature) bool { - if otherEd, ok := other.(SignatureEd25519); ok { + if otherEd, ok := other.Unwrap().(SignatureEd25519); ok { return bytes.Equal(sig[:], otherEd[:]) } else { return false @@ -97,7 +106,7 @@ func (p *SignatureEd25519) UnmarshalJSON(enc []byte) error { type SignatureSecp256k1 []byte func (sig SignatureSecp256k1) Bytes() []byte { - return wire.BinaryBytes(struct{ Signature }{sig}) + return wire.BinaryBytes(Signature{sig}) } func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 } @@ -105,7 +114,7 @@ func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 } func (sig SignatureSecp256k1) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } func (sig SignatureSecp256k1) Equals(other Signature) bool { - if otherEd, ok := other.(SignatureSecp256k1); ok { + if otherEd, ok := other.Unwrap().(SignatureSecp256k1); ok { return bytes.Equal(sig[:], otherEd[:]) } else { return false diff --git a/signature_test.go b/signature_test.go index f04262b77..92fed906d 100644 --- a/signature_test.go +++ b/signature_test.go @@ -22,9 +22,9 @@ func TestSignAndValidateEd25519(t *testing.T) { assert.True(t, pubKey.VerifyBytes(msg, sig)) // Mutate the signature, just one bit. - sigEd := sig.(SignatureEd25519) - sigEd[0] ^= byte(0x01) - sig = Signature(sigEd) + sigEd := sig.Unwrap().(SignatureEd25519) + sigEd[7] ^= byte(0x01) + sig = WrapSignature(sigEd) assert.False(t, pubKey.VerifyBytes(msg, sig)) } @@ -39,28 +39,28 @@ func TestSignAndValidateSecp256k1(t *testing.T) { assert.True(t, pubKey.VerifyBytes(msg, sig)) // Mutate the signature, just one bit. - sigEd := sig.(SignatureSecp256k1) - sigEd[0] ^= byte(0x01) - sig = Signature(sigEd) + sigEd := sig.Unwrap().(SignatureSecp256k1) + sigEd[3] ^= byte(0x01) + sig = WrapSignature(sigEd) assert.False(t, pubKey.VerifyBytes(msg, sig)) } func TestSignatureEncodings(t *testing.T) { cases := []struct { - privKey PrivKeyS + privKey PrivKey sigSize int sigType byte sigName string }{ { - privKey: PrivKeyS{GenPrivKeyEd25519()}, + privKey: WrapPrivKey(GenPrivKeyEd25519()), sigSize: ed25519.SignatureSize, sigType: TypeEd25519, sigName: NameEd25519, }, { - privKey: PrivKeyS{GenPrivKeySecp256k1()}, + privKey: WrapPrivKey(GenPrivKeySecp256k1()), sigSize: 0, // unknown sigType: TypeSecp256k1, sigName: NameSecp256k1, @@ -69,10 +69,10 @@ func TestSignatureEncodings(t *testing.T) { for _, tc := range cases { // note we embed them from the beginning.... - pubKey := PubKeyS{tc.privKey.PubKey()} + pubKey := tc.privKey.PubKey() msg := CRandBytes(128) - sig := SignatureS{tc.privKey.Sign(msg)} + sig := tc.privKey.Sign(msg) // store as wire bin, err := data.ToWire(sig) @@ -83,7 +83,7 @@ func TestSignatureEncodings(t *testing.T) { assert.Equal(t, tc.sigType, bin[0]) // and back - sig2 := SignatureS{} + sig2 := Signature{} err = data.FromWire(bin, &sig2) require.Nil(t, err, "%+v", err) assert.EqualValues(t, sig, sig2) @@ -95,7 +95,7 @@ func TestSignatureEncodings(t *testing.T) { assert.True(t, strings.Contains(string(js), tc.sigName)) // and back - sig3 := SignatureS{} + sig3 := Signature{} err = data.FromJSON(js, &sig3) require.Nil(t, err, "%+v", err) assert.EqualValues(t, sig, sig3) @@ -118,25 +118,25 @@ func TestWrapping(t *testing.T) { sig := priv.Sign(msg) // do some wrapping - pubs := []PubKeyS{ + pubs := []PubKey{ WrapPubKey(nil), WrapPubKey(pub), WrapPubKey(WrapPubKey(WrapPubKey(WrapPubKey(pub)))), - WrapPubKey(PubKeyS{PubKeyS{PubKeyS{pub}}}), + WrapPubKey(PubKey{PubKey{PubKey{pub}}}), } for _, p := range pubs { - _, ok := p.PubKey.(PubKeyS) + _, ok := p.PubKeyInner.(PubKey) assert.False(ok) } - sigs := []SignatureS{ + sigs := []Signature{ WrapSignature(nil), WrapSignature(sig), WrapSignature(WrapSignature(WrapSignature(WrapSignature(sig)))), - WrapSignature(SignatureS{SignatureS{SignatureS{sig}}}), + WrapSignature(Signature{Signature{Signature{sig}}}), } for _, s := range sigs { - _, ok := s.Signature.(SignatureS) + _, ok := s.SignatureInner.(Signature) assert.False(ok) } From 5b94758d4c5f0aa7842f56b31e88a8d2bdb736e2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 22 Mar 2017 15:59:00 +0100 Subject: [PATCH 18/45] Make PubKey struct compatible with go-wire.JSONBytes/ReadJSON --- encode_test.go | 20 ++++++++++++++++++++ priv_key.go | 2 +- pub_key.go | 2 +- signature.go | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/encode_test.go b/encode_test.go index 667132a6f..71b7b3ae6 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1,12 +1,14 @@ package crypto import ( + "fmt" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" data "github.com/tendermint/go-data" + wire "github.com/tendermint/go-wire" ) type byter interface { @@ -48,6 +50,18 @@ func checkJSON(t *testing.T, in interface{}, reader interface{}, typ string) { assert.True(t, strings.Contains(string(js), parts[1])) } +// make sure go-wire json can still figure this out... +func checkWireJSON(t *testing.T, in interface{}, reader interface{}, typ byte) { + // test to and from binary + var err error + js := wire.JSONBytes(in) + btyp := fmt.Sprintf("[%d,", typ) + assert.True(t, strings.HasPrefix(string(js), btyp), string(js)) + + wire.ReadJSON(reader, js, &err) + require.Nil(t, err, "%+v", err) +} + func TestKeyEncodings(t *testing.T) { cases := []struct { privKey PrivKey @@ -74,6 +88,9 @@ func TestKeyEncodings(t *testing.T) { priv3 := PrivKey{} checkJSON(t, tc.privKey, &priv3, tc.keyName) assert.EqualValues(t, tc.privKey, priv3) + priv4 := PrivKey{} + checkWireJSON(t, tc.privKey, &priv4, tc.keyType) + assert.EqualValues(t, tc.privKey, priv4) // check (de/en)codings of public key pubKey := tc.privKey.PubKey() @@ -83,6 +100,9 @@ func TestKeyEncodings(t *testing.T) { pub3 := PubKey{} checkJSON(t, pubKey, &pub3, tc.keyName) assert.EqualValues(t, pubKey, pub3) + pub4 := PubKey{} + checkWireJSON(t, pubKey, &pub4, tc.keyType) + assert.EqualValues(t, pubKey, pub4) } } diff --git a/priv_key.go b/priv_key.go index f00a762d8..5aa2d05ad 100644 --- a/priv_key.go +++ b/priv_key.go @@ -38,7 +38,7 @@ func init() { // PrivKey handles all encoding and exposes methods type PrivKey struct { - PrivKeyInner + PrivKeyInner `json:"unwrap"` } func WrapPrivKey(pk PrivKeyInner) PrivKey { diff --git a/pub_key.go b/pub_key.go index fa91b12f4..8373eb347 100644 --- a/pub_key.go +++ b/pub_key.go @@ -33,7 +33,7 @@ func init() { // PubKey add json serialization to PubKeyInner type PubKey struct { - PubKeyInner + PubKeyInner `json:"unwrap"` } func WrapPubKey(pk PubKeyInner) PubKey { diff --git a/signature.go b/signature.go index e7b6b1745..ba40166cd 100644 --- a/signature.go +++ b/signature.go @@ -29,7 +29,7 @@ func init() { // Signature add json serialization to Signature type Signature struct { - SignatureInner + SignatureInner `json:"unwrap"` } func WrapSignature(pk SignatureInner) Signature { From eb6fcef8d2d26bee5ba49c88fd5af305881fd3e9 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 29 Mar 2017 15:17:06 +0200 Subject: [PATCH 19/45] Better docs and naming thanks to bucky --- embed_test.go | 4 ++++ encode_test.go | 14 +++++--------- priv_key.go | 14 ++++++++++++-- pub_key.go | 14 ++++++++++++-- signature.go | 15 ++++++++++++--- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/embed_test.go b/embed_test.go index 77b821f03..eda72b438 100644 --- a/embed_test.go +++ b/embed_test.go @@ -95,7 +95,11 @@ func TestEncodeDemo(t *testing.T) { }{ {PubName{Foo{"pub-foo"}}, &PubName{}, "Foo: pub-foo"}, {PubName{Bar{7}}, &PubName{}, "Bar #7"}, + // Note these fail - if you can figure a solution here, I'll buy you a beer :) + // (ebuchman is right, you must either break the reflection system, or modify go-wire) + // but such a mod would let us make REALLY sure that no one could construct like this + // {PrivName{Foo{"priv-foo"}}, &PrivName{}, "Foo: priv-foo"}, // {PrivName{Bar{9}}, &PrivName{}, "Bar #9"}, } diff --git a/encode_test.go b/encode_test.go index 71b7b3ae6..9a8e5902c 100644 --- a/encode_test.go +++ b/encode_test.go @@ -82,25 +82,21 @@ func TestKeyEncodings(t *testing.T) { for _, tc := range cases { // check (de/en)codings of private key - priv2 := PrivKey{} + var priv2, priv3, priv4 PrivKey checkWire(t, tc.privKey, &priv2, tc.keyType) assert.EqualValues(t, tc.privKey, priv2) - priv3 := PrivKey{} checkJSON(t, tc.privKey, &priv3, tc.keyName) assert.EqualValues(t, tc.privKey, priv3) - priv4 := PrivKey{} checkWireJSON(t, tc.privKey, &priv4, tc.keyType) assert.EqualValues(t, tc.privKey, priv4) // check (de/en)codings of public key pubKey := tc.privKey.PubKey() - pub2 := PubKey{} + var pub2, pub3, pub4 PubKey checkWire(t, pubKey, &pub2, tc.keyType) assert.EqualValues(t, pubKey, pub2) - pub3 := PubKey{} checkJSON(t, pubKey, &pub3, tc.keyName) assert.EqualValues(t, pubKey, pub3) - pub4 := PubKey{} checkWireJSON(t, pubKey, &pub4, tc.keyType) assert.EqualValues(t, pubKey, pub4) } @@ -115,17 +111,17 @@ func toFromJSON(t *testing.T, in interface{}, recvr interface{}) { func TestNilEncodings(t *testing.T) { // make sure sigs are okay with nil - a, b := Signature{}, Signature{} + var a, b Signature toFromJSON(t, a, &b) assert.EqualValues(t, a, b) // make sure sigs are okay with nil - c, d := PubKey{}, PubKey{} + var c, d PubKey toFromJSON(t, c, &d) assert.EqualValues(t, c, d) // make sure sigs are okay with nil - e, f := PrivKey{}, PrivKey{} + var e, f PrivKey toFromJSON(t, e, &f) assert.EqualValues(t, e, f) diff --git a/priv_key.go b/priv_key.go index 5aa2d05ad..d21bdf458 100644 --- a/priv_key.go +++ b/priv_key.go @@ -11,7 +11,14 @@ import ( "github.com/tendermint/go-wire" ) -// PrivKeyInner is now the interface itself, use PrivKey in all code +/* +DO NOT USE this interface. + +It is public by necessity but should never be used directly +outside of this package. + +Only use the PrivKey, never the PrivKeyInner +*/ type PrivKeyInner interface { Bytes() []byte Sign(msg []byte) Signature @@ -36,11 +43,13 @@ func init() { RegisterImplementation(PrivKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) } -// PrivKey handles all encoding and exposes methods +// PrivKey should be used instead of an interface in all external packages +// unless you demand a concrete implementation, then use that directly. type PrivKey struct { PrivKeyInner `json:"unwrap"` } +// WrapPrivKey goes from concrete implementation to "interface" struct func WrapPrivKey(pk PrivKeyInner) PrivKey { if wrap, ok := pk.(PrivKey); ok { pk = wrap.Unwrap() @@ -48,6 +57,7 @@ func WrapPrivKey(pk PrivKeyInner) PrivKey { return PrivKey{pk} } +// Unwrap recovers the concrete interface safely (regardless of levels of embeds) func (p PrivKey) Unwrap() PrivKeyInner { pk := p.PrivKeyInner for wrap, ok := pk.(PrivKey); ok; wrap, ok = pk.(PrivKey) { diff --git a/pub_key.go b/pub_key.go index 8373eb347..a763688fe 100644 --- a/pub_key.go +++ b/pub_key.go @@ -13,7 +13,14 @@ import ( "golang.org/x/crypto/ripemd160" ) -// PubKeyInner is now the interface itself, use PubKey struct in all code +/* +DO NOT USE this interface. + +It is public by necessity but should never be used directly +outside of this package. + +Only use the PubKey, never the PubKeyInner +*/ type PubKeyInner interface { Address() []byte Bytes() []byte @@ -31,11 +38,13 @@ func init() { RegisterImplementation(PubKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) } -// PubKey add json serialization to PubKeyInner +// PubKey should be used instead of an interface in all external packages +// unless you demand a concrete implementation, then use that directly. type PubKey struct { PubKeyInner `json:"unwrap"` } +// WrapPubKey goes from concrete implementation to "interface" struct func WrapPubKey(pk PubKeyInner) PubKey { if wrap, ok := pk.(PubKey); ok { pk = wrap.Unwrap() @@ -43,6 +52,7 @@ func WrapPubKey(pk PubKeyInner) PubKey { return PubKey{pk} } +// Unwrap recovers the concrete interface safely (regardless of levels of embeds) func (p PubKey) Unwrap() PubKeyInner { pk := p.PubKeyInner for wrap, ok := pk.(PubKey); ok; wrap, ok = pk.(PubKey) { diff --git a/signature.go b/signature.go index ba40166cd..bb3e8f11b 100644 --- a/signature.go +++ b/signature.go @@ -9,8 +9,14 @@ import ( "github.com/tendermint/go-wire" ) -// SignatureInner is now the interface itself. -// Use Signature in all code +/* +DO NOT USE this interface. + +It is public by necessity but should never be used directly +outside of this package. + +Only use the Signature, never the SignatureInner +*/ type SignatureInner interface { Bytes() []byte IsZero() bool @@ -27,11 +33,13 @@ func init() { RegisterImplementation(SignatureSecp256k1{}, NameSecp256k1, TypeSecp256k1) } -// Signature add json serialization to Signature +// Signature should be used instead of an interface in all external packages +// unless you demand a concrete implementation, then use that directly. type Signature struct { SignatureInner `json:"unwrap"` } +// WrapSignature goes from concrete implementation to "interface" struct func WrapSignature(pk SignatureInner) Signature { if wrap, ok := pk.(Signature); ok { pk = wrap.Unwrap() @@ -39,6 +47,7 @@ func WrapSignature(pk SignatureInner) Signature { return Signature{pk} } +// Unwrap recovers the concrete interface safely (regardless of levels of embeds) func (p Signature) Unwrap() SignatureInner { pk := p.SignatureInner for wrap, ok := pk.(Signature); ok; wrap, ok = pk.(Signature) { From a3324cc97bf90b49437eb4cf1da3c7733259b62d Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 9 Apr 2017 00:32:54 -0700 Subject: [PATCH 20/45] Cleanup; Implement .Wrap() --- crypto.go | 9 ++++ embed_test.go | 102 +++++++++++++++------------------------------- encode_test.go | 4 +- priv_key.go | 91 ++++++++++++++++++----------------------- pub_key.go | 85 ++++++++++++++++++-------------------- signature.go | 99 +++++++++++++++++++++----------------------- signature_test.go | 24 +++++------ 7 files changed, 181 insertions(+), 233 deletions(-) create mode 100644 crypto.go diff --git a/crypto.go b/crypto.go new file mode 100644 index 000000000..d9caf1ad9 --- /dev/null +++ b/crypto.go @@ -0,0 +1,9 @@ +package crypto + +// Types of implementations +const ( + TypeEd25519 = byte(0x01) + TypeSecp256k1 = byte(0x02) + NameEd25519 = "ed25519" + NameSecp256k1 = "secp256k1" +) diff --git a/embed_test.go b/embed_test.go index eda72b438..6ebba893c 100644 --- a/embed_test.go +++ b/embed_test.go @@ -9,50 +9,13 @@ import ( data "github.com/tendermint/go-data" ) -type Foo struct { - Name string -} - -func (f Foo) Greet() string { - return "Foo: " + f.Name -} - -type Bar struct { - Age int -} - -func (b Bar) Greet() string { - return fmt.Sprintf("Bar #%d", b.Age) +type PubName struct { + PubNameInner } type PubNameInner interface { - Greet() string -} - -type privNameInner interface { - Greet() string -} - -type Greeter interface { - Greet() string -} - -var ( - pubNameMapper, privNameMapper data.Mapper -) - -// register both public key types with go-data (and thus go-wire) -func init() { - pubNameMapper = data.NewMapper(PubName{}). - RegisterImplementation(Foo{}, "foo", 1). - RegisterImplementation(Bar{}, "bar", 2) - privNameMapper = data.NewMapper(PrivName{}). - RegisterImplementation(Foo{}, "foo", 1). - RegisterImplementation(Bar{}, "bar", 2) -} - -type PubName struct { - PubNameInner + AssertIsPubNameInner() + String() string } func (p PubName) MarshalJSON() ([]byte, error) { @@ -67,62 +30,61 @@ func (p *PubName) UnmarshalJSON(data []byte) error { return err } -type PrivName struct { - privNameInner -} +var pubNameMapper = data.NewMapper(PubName{}). + RegisterImplementation(PubNameFoo{}, "foo", 1). + RegisterImplementation(PubNameBar{}, "bar", 2) -func (p PrivName) MarshalJSON() ([]byte, error) { - return privNameMapper.ToJSON(p.privNameInner) +func (f PubNameFoo) AssertIsPubNameInner() {} +func (f PubNameBar) AssertIsPubNameInner() {} + +//---------------------------------------- + +type PubNameFoo struct { + Name string } -func (p *PrivName) UnmarshalJSON(data []byte) error { - parsed, err := privNameMapper.FromJSON(data) - if err == nil && parsed != nil { - p.privNameInner = parsed.(privNameInner) - } - return err +func (f PubNameFoo) String() string { return "Foo: " + f.Name } + +type PubNameBar struct { + Age int } +func (b PubNameBar) String() string { return fmt.Sprintf("Bar #%d", b.Age) } + +//---------------------------------------- + // TestEncodeDemo tries the various strategies to encode the objects func TestEncodeDemo(t *testing.T) { assert, require := assert.New(t), require.New(t) - // assert := assert.New(t) - // require := require.New(t) cases := []struct { - in, out Greeter + in, out PubNameInner expected string }{ - {PubName{Foo{"pub-foo"}}, &PubName{}, "Foo: pub-foo"}, - {PubName{Bar{7}}, &PubName{}, "Bar #7"}, - - // Note these fail - if you can figure a solution here, I'll buy you a beer :) - // (ebuchman is right, you must either break the reflection system, or modify go-wire) - // but such a mod would let us make REALLY sure that no one could construct like this - - // {PrivName{Foo{"priv-foo"}}, &PrivName{}, "Foo: priv-foo"}, - // {PrivName{Bar{9}}, &PrivName{}, "Bar #9"}, + {PubName{PubNameFoo{"pub-foo"}}, &PubName{}, "Foo: pub-foo"}, + {PubName{PubNameBar{7}}, &PubName{}, "Bar #7"}, } for i, tc := range cases { - // make sure it is proper to start - require.Equal(tc.expected, tc.in.Greet()) - // now, try to encode as binary + // Make sure it is proper to start + require.Equal(tc.expected, tc.in.String()) + + // Try to encode as binary b, err := data.ToWire(tc.in) if assert.Nil(err, "%d: %#v", i, tc.in) { err := data.FromWire(b, tc.out) if assert.Nil(err) { - assert.Equal(tc.expected, tc.out.Greet()) + assert.Equal(tc.expected, tc.out.String()) } } - // try to encode it as json + // Try to encode it as json j, err := data.ToJSON(tc.in) if assert.Nil(err, "%d: %#v", i, tc.in) { err := data.FromJSON(j, tc.out) if assert.Nil(err) { - assert.Equal(tc.expected, tc.out.Greet()) + assert.Equal(tc.expected, tc.out.String()) } } } diff --git a/encode_test.go b/encode_test.go index 9a8e5902c..2d709b943 100644 --- a/encode_test.go +++ b/encode_test.go @@ -69,12 +69,12 @@ func TestKeyEncodings(t *testing.T) { keyName string }{ { - privKey: WrapPrivKey(GenPrivKeyEd25519()), + privKey: GenPrivKeyEd25519().Wrap(), keyType: TypeEd25519, keyName: NameEd25519, }, { - privKey: WrapPrivKey(GenPrivKeySecp256k1()), + privKey: GenPrivKeySecp256k1().Wrap(), keyType: TypeSecp256k1, keyName: NameSecp256k1, }, diff --git a/priv_key.go b/priv_key.go index d21bdf458..74d5b5a3e 100644 --- a/priv_key.go +++ b/priv_key.go @@ -11,50 +11,38 @@ import ( "github.com/tendermint/go-wire" ) -/* -DO NOT USE this interface. +func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { + err = wire.ReadBinaryBytes(privKeyBytes, &privKey) + return +} -It is public by necessity but should never be used directly -outside of this package. +//---------------------------------------- -Only use the PrivKey, never the PrivKeyInner -*/ +type PrivKey struct { + PrivKeyInner `json:"unwrap"` +} + +// DO NOT USE THIS INTERFACE. +// You probably want to use PubKey type PrivKeyInner interface { + AssertIsPrivKeyInner() Bytes() []byte Sign(msg []byte) Signature PubKey() PubKey Equals(PrivKey) bool + Wrap() PrivKey } -// Types of implementations -const ( - TypeEd25519 = byte(0x01) - TypeSecp256k1 = byte(0x02) - NameEd25519 = "ed25519" - NameSecp256k1 = "secp256k1" -) - -var privKeyMapper data.Mapper - -// register both private key types with go-data (and thus go-wire) -func init() { - privKeyMapper = data.NewMapper(PrivKey{}). - RegisterImplementation(PrivKeyEd25519{}, NameEd25519, TypeEd25519). - RegisterImplementation(PrivKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) -} - -// PrivKey should be used instead of an interface in all external packages -// unless you demand a concrete implementation, then use that directly. -type PrivKey struct { - PrivKeyInner `json:"unwrap"` +func (p PrivKey) MarshalJSON() ([]byte, error) { + return privKeyMapper.ToJSON(p.PrivKeyInner) } -// WrapPrivKey goes from concrete implementation to "interface" struct -func WrapPrivKey(pk PrivKeyInner) PrivKey { - if wrap, ok := pk.(PrivKey); ok { - pk = wrap.Unwrap() +func (p *PrivKey) UnmarshalJSON(data []byte) (err error) { + parsed, err := privKeyMapper.FromJSON(data) + if err == nil && parsed != nil { + p.PrivKeyInner = parsed.(PrivKeyInner) } - return PrivKey{pk} + return } // Unwrap recovers the concrete interface safely (regardless of levels of embeds) @@ -66,32 +54,21 @@ func (p PrivKey) Unwrap() PrivKeyInner { return pk } -func (p PrivKey) MarshalJSON() ([]byte, error) { - return privKeyMapper.ToJSON(p.PrivKeyInner) -} - -func (p *PrivKey) UnmarshalJSON(data []byte) (err error) { - parsed, err := privKeyMapper.FromJSON(data) - if err == nil && parsed != nil { - p.PrivKeyInner = parsed.(PrivKeyInner) - } - return -} - func (p PrivKey) Empty() bool { return p.PrivKeyInner == nil } -func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { - err = wire.ReadBinaryBytes(privKeyBytes, &privKey) - return -} +var privKeyMapper = data.NewMapper(PrivKey{}). + RegisterImplementation(PrivKeyEd25519{}, NameEd25519, TypeEd25519). + RegisterImplementation(PrivKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) //------------------------------------- // Implements PrivKey type PrivKeyEd25519 [64]byte +func (privKey PrivKeyEd25519) AssertIsPrivKeyInner() {} + func (privKey PrivKeyEd25519) Bytes() []byte { return wire.BinaryBytes(PrivKey{privKey}) } @@ -99,13 +76,13 @@ func (privKey PrivKeyEd25519) Bytes() []byte { func (privKey PrivKeyEd25519) Sign(msg []byte) Signature { privKeyBytes := [64]byte(privKey) signatureBytes := ed25519.Sign(&privKeyBytes, msg) - return WrapSignature(SignatureEd25519(*signatureBytes)) + return SignatureEd25519(*signatureBytes).Wrap() } func (privKey PrivKeyEd25519) PubKey() PubKey { privKeyBytes := [64]byte(privKey) pubBytes := *ed25519.MakePublicKey(&privKeyBytes) - return WrapPubKey(PubKeyEd25519(pubBytes)) + return PubKeyEd25519(pubBytes).Wrap() } func (privKey PrivKeyEd25519) Equals(other PrivKey) bool { @@ -149,6 +126,10 @@ func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { return PrivKeyEd25519(newKey) } +func (privKey PrivKeyEd25519) Wrap() PrivKey { + return PrivKey{privKey} +} + func GenPrivKeyEd25519() PrivKeyEd25519 { privKeyBytes := new([64]byte) copy(privKeyBytes[:32], CRandBytes(32)) @@ -171,6 +152,8 @@ func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { // Implements PrivKey type PrivKeySecp256k1 [32]byte +func (privKey PrivKeySecp256k1) AssertIsPrivKeyInner() {} + func (privKey PrivKeySecp256k1) Bytes() []byte { return wire.BinaryBytes(PrivKey{privKey}) } @@ -181,14 +164,14 @@ func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature { if err != nil { PanicSanity(err) } - return WrapSignature(SignatureSecp256k1(sig__.Serialize())) + return SignatureSecp256k1(sig__.Serialize()).Wrap() } func (privKey PrivKeySecp256k1) PubKey() PubKey { _, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) var pub PubKeySecp256k1 copy(pub[:], pub__.SerializeCompressed()) - return WrapPubKey(pub) + return pub.Wrap() } func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool { @@ -214,6 +197,10 @@ func (privKey PrivKeySecp256k1) String() string { return Fmt("PrivKeySecp256k1{*****}") } +func (privKey PrivKeySecp256k1) Wrap() PrivKey { + return PrivKey{privKey} +} + /* // Deterministically generates new priv-key bytes from key. func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 { diff --git a/pub_key.go b/pub_key.go index a763688fe..e6567d347 100644 --- a/pub_key.go +++ b/pub_key.go @@ -13,80 +13,65 @@ import ( "golang.org/x/crypto/ripemd160" ) -/* -DO NOT USE this interface. +func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { + err = wire.ReadBinaryBytes(pubKeyBytes, &pubKey) + return +} -It is public by necessity but should never be used directly -outside of this package. +//---------------------------------------- -Only use the PubKey, never the PubKeyInner -*/ +type PubKey struct { + PubKeyInner `json:"unwrap"` +} + +// DO NOT USE THIS INTERFACE. +// You probably want to use PubKey type PubKeyInner interface { + AssertIsPubKeyInner() Address() []byte Bytes() []byte KeyString() string VerifyBytes(msg []byte, sig Signature) bool Equals(PubKey) bool + Wrap() PubKey } -var pubKeyMapper data.Mapper - -// register both public key types with go-data (and thus go-wire) -func init() { - pubKeyMapper = data.NewMapper(PubKey{}). - RegisterImplementation(PubKeyEd25519{}, NameEd25519, TypeEd25519). - RegisterImplementation(PubKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) +func (pk PubKey) MarshalJSON() ([]byte, error) { + return pubKeyMapper.ToJSON(pk.PubKeyInner) } -// PubKey should be used instead of an interface in all external packages -// unless you demand a concrete implementation, then use that directly. -type PubKey struct { - PubKeyInner `json:"unwrap"` -} - -// WrapPubKey goes from concrete implementation to "interface" struct -func WrapPubKey(pk PubKeyInner) PubKey { - if wrap, ok := pk.(PubKey); ok { - pk = wrap.Unwrap() +func (pk *PubKey) UnmarshalJSON(data []byte) (err error) { + parsed, err := pubKeyMapper.FromJSON(data) + if err == nil && parsed != nil { + pk.PubKeyInner = parsed.(PubKeyInner) } - return PubKey{pk} + return } // Unwrap recovers the concrete interface safely (regardless of levels of embeds) -func (p PubKey) Unwrap() PubKeyInner { - pk := p.PubKeyInner - for wrap, ok := pk.(PubKey); ok; wrap, ok = pk.(PubKey) { - pk = wrap.PubKeyInner - } - return pk -} - -func (p PubKey) MarshalJSON() ([]byte, error) { - return pubKeyMapper.ToJSON(p.PubKeyInner) -} - -func (p *PubKey) UnmarshalJSON(data []byte) (err error) { - parsed, err := pubKeyMapper.FromJSON(data) - if err == nil && parsed != nil { - p.PubKeyInner = parsed.(PubKeyInner) +func (pk PubKey) Unwrap() PubKeyInner { + pkI := pk.PubKeyInner + for wrap, ok := pkI.(PubKey); ok; wrap, ok = pkI.(PubKey) { + pkI = wrap.PubKeyInner } - return + return pkI } func (p PubKey) Empty() bool { return p.PubKeyInner == nil } -func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { - err = wire.ReadBinaryBytes(pubKeyBytes, &pubKey) - return -} +var pubKeyMapper = data.NewMapper(PubKey{}). + RegisterImplementation(PubKeyEd25519{}, NameEd25519, TypeEd25519). + RegisterImplementation(PubKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) //------------------------------------- // Implements PubKeyInner type PubKeyEd25519 [32]byte +func (pubKey PubKeyEd25519) AssertIsPubKeyInner() {} + func (pubKey PubKeyEd25519) Address() []byte { w, n, err := new(bytes.Buffer), new(int), new(error) wire.WriteBinary(pubKey[:], w, n, err) @@ -155,6 +140,10 @@ func (pubKey PubKeyEd25519) Equals(other PubKey) bool { } } +func (pubKey PubKeyEd25519) Wrap() PubKey { + return PubKey{pubKey} +} + //------------------------------------- // Implements PubKey. @@ -162,6 +151,8 @@ func (pubKey PubKeyEd25519) Equals(other PubKey) bool { // prefixed with 0x02 or 0x03, depending on the y-cord. type PubKeySecp256k1 [33]byte +func (pubKey PubKeySecp256k1) AssertIsPubKeyInner() {} + // Implements Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) func (pubKey PubKeySecp256k1) Address() []byte { hasherSHA256 := sha256.New() @@ -223,3 +214,7 @@ func (pubKey PubKeySecp256k1) Equals(other PubKey) bool { return false } } + +func (pubKey PubKeySecp256k1) Wrap() PubKey { + return PubKey{pubKey} +} diff --git a/signature.go b/signature.go index bb3e8f11b..66c9a8a27 100644 --- a/signature.go +++ b/signature.go @@ -9,79 +9,64 @@ import ( "github.com/tendermint/go-wire" ) -/* -DO NOT USE this interface. +func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { + err = wire.ReadBinaryBytes(sigBytes, &sig) + return +} -It is public by necessity but should never be used directly -outside of this package. +//---------------------------------------- -Only use the Signature, never the SignatureInner -*/ +type Signature struct { + SignatureInner `json:"unwrap"` +} + +// DO NOT USE THIS INTERFACE. +// You probably want to use Signature. type SignatureInner interface { + AssertIsSignatureInner() Bytes() []byte IsZero() bool String() string Equals(Signature) bool + Wrap() Signature } -var sigMapper data.Mapper - -// register both public key types with go-data (and thus go-wire) -func init() { - sigMapper = data.NewMapper(Signature{}). - RegisterImplementation(SignatureEd25519{}, NameEd25519, TypeEd25519). - RegisterImplementation(SignatureSecp256k1{}, NameSecp256k1, TypeSecp256k1) +func (sig Signature) MarshalJSON() ([]byte, error) { + return sigMapper.ToJSON(sig.SignatureInner) } -// Signature should be used instead of an interface in all external packages -// unless you demand a concrete implementation, then use that directly. -type Signature struct { - SignatureInner `json:"unwrap"` -} - -// WrapSignature goes from concrete implementation to "interface" struct -func WrapSignature(pk SignatureInner) Signature { - if wrap, ok := pk.(Signature); ok { - pk = wrap.Unwrap() +func (sig *Signature) UnmarshalJSON(data []byte) (err error) { + parsed, err := sigMapper.FromJSON(data) + if err == nil && parsed != nil { + sig.SignatureInner = parsed.(SignatureInner) } - return Signature{pk} + return } // Unwrap recovers the concrete interface safely (regardless of levels of embeds) -func (p Signature) Unwrap() SignatureInner { - pk := p.SignatureInner +func (sig Signature) Unwrap() SignatureInner { + pk := sig.SignatureInner for wrap, ok := pk.(Signature); ok; wrap, ok = pk.(Signature) { pk = wrap.SignatureInner } return pk } -func (p Signature) MarshalJSON() ([]byte, error) { - return sigMapper.ToJSON(p.SignatureInner) -} - -func (p *Signature) UnmarshalJSON(data []byte) (err error) { - parsed, err := sigMapper.FromJSON(data) - if err == nil && parsed != nil { - p.SignatureInner = parsed.(SignatureInner) - } - return +func (sig Signature) Empty() bool { + return sig.SignatureInner == nil } -func (p Signature) Empty() bool { - return p.SignatureInner == nil -} - -func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { - err = wire.ReadBinaryBytes(sigBytes, &sig) - return -} +var sigMapper = data.NewMapper(Signature{}). + RegisterImplementation(SignatureEd25519{}, NameEd25519, TypeEd25519). + RegisterImplementation(SignatureSecp256k1{}, NameSecp256k1, TypeSecp256k1) //------------------------------------- // Implements Signature type SignatureEd25519 [64]byte +func (sig SignatureEd25519) AssertIsSignatureInner() {} + func (sig SignatureEd25519) Bytes() []byte { return wire.BinaryBytes(Signature{sig}) } @@ -98,22 +83,28 @@ func (sig SignatureEd25519) Equals(other Signature) bool { } } -func (p SignatureEd25519) MarshalJSON() ([]byte, error) { - return data.Encoder.Marshal(p[:]) +func (sig SignatureEd25519) MarshalJSON() ([]byte, error) { + return data.Encoder.Marshal(sig[:]) } -func (p *SignatureEd25519) UnmarshalJSON(enc []byte) error { +func (sig *SignatureEd25519) UnmarshalJSON(enc []byte) error { var ref []byte err := data.Encoder.Unmarshal(&ref, enc) - copy(p[:], ref) + copy(sig[:], ref) return err } +func (sig SignatureEd25519) Wrap() Signature { + return Signature{sig} +} + //------------------------------------- // Implements Signature type SignatureSecp256k1 []byte +func (sig SignatureSecp256k1) AssertIsSignatureInner() {} + func (sig SignatureSecp256k1) Bytes() []byte { return wire.BinaryBytes(Signature{sig}) } @@ -129,10 +120,14 @@ func (sig SignatureSecp256k1) Equals(other Signature) bool { return false } } -func (p SignatureSecp256k1) MarshalJSON() ([]byte, error) { - return data.Encoder.Marshal(p) +func (sig SignatureSecp256k1) MarshalJSON() ([]byte, error) { + return data.Encoder.Marshal(sig) +} + +func (sig *SignatureSecp256k1) UnmarshalJSON(enc []byte) error { + return data.Encoder.Unmarshal((*[]byte)(sig), enc) } -func (p *SignatureSecp256k1) UnmarshalJSON(enc []byte) error { - return data.Encoder.Unmarshal((*[]byte)(p), enc) +func (sig SignatureSecp256k1) Wrap() Signature { + return Signature{sig} } diff --git a/signature_test.go b/signature_test.go index 92fed906d..93aa17279 100644 --- a/signature_test.go +++ b/signature_test.go @@ -24,7 +24,7 @@ func TestSignAndValidateEd25519(t *testing.T) { // Mutate the signature, just one bit. sigEd := sig.Unwrap().(SignatureEd25519) sigEd[7] ^= byte(0x01) - sig = WrapSignature(sigEd) + sig = sigEd.Wrap() assert.False(t, pubKey.VerifyBytes(msg, sig)) } @@ -41,7 +41,7 @@ func TestSignAndValidateSecp256k1(t *testing.T) { // Mutate the signature, just one bit. sigEd := sig.Unwrap().(SignatureSecp256k1) sigEd[3] ^= byte(0x01) - sig = WrapSignature(sigEd) + sig = sigEd.Wrap() assert.False(t, pubKey.VerifyBytes(msg, sig)) } @@ -54,13 +54,13 @@ func TestSignatureEncodings(t *testing.T) { sigName string }{ { - privKey: WrapPrivKey(GenPrivKeyEd25519()), + privKey: GenPrivKeyEd25519().Wrap(), sigSize: ed25519.SignatureSize, sigType: TypeEd25519, sigName: NameEd25519, }, { - privKey: WrapPrivKey(GenPrivKeySecp256k1()), + privKey: GenPrivKeySecp256k1().Wrap(), sigSize: 0, // unknown sigType: TypeSecp256k1, sigName: NameSecp256k1, @@ -119,10 +119,10 @@ func TestWrapping(t *testing.T) { // do some wrapping pubs := []PubKey{ - WrapPubKey(nil), - WrapPubKey(pub), - WrapPubKey(WrapPubKey(WrapPubKey(WrapPubKey(pub)))), - WrapPubKey(PubKey{PubKey{PubKey{pub}}}), + PubKey{nil}, + pub.Wrap(), + pub.Wrap().Wrap().Wrap(), + PubKey{PubKey{PubKey{pub}}}.Wrap(), } for _, p := range pubs { _, ok := p.PubKeyInner.(PubKey) @@ -130,10 +130,10 @@ func TestWrapping(t *testing.T) { } sigs := []Signature{ - WrapSignature(nil), - WrapSignature(sig), - WrapSignature(WrapSignature(WrapSignature(WrapSignature(sig)))), - WrapSignature(Signature{Signature{Signature{sig}}}), + Signature{nil}, + sig.Wrap(), + sig.Wrap().Wrap().Wrap(), + Signature{Signature{Signature{sig}}}.Wrap(), } for _, s := range sigs { _, ok := s.SignatureInner.(Signature) From 926741c0a1b409d734ab635d52f6ffa5c4c03d2b Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 9 Apr 2017 01:29:33 -0700 Subject: [PATCH 21/45] remove coin param --- hd/address.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/hd/address.go b/hd/address.go index afdeefbc4..139707e46 100644 --- a/hd/address.go +++ b/hd/address.go @@ -22,21 +22,13 @@ import ( "golang.org/x/crypto/ripemd160" ) -const ( - // BIP32 chainpath prefix - CHAINPATH_PREFIX_DEPOSIT = 0 - CHAINPATH_PREFIX_CHANGE = 1 - CHAINPATH_PREFIX_SWEEP = 2 - CHAINPATH_PREFIX_SWEEP_DRY = 102 -) - -func ComputeAddress(coin string, pubKeyHex string, chainHex string, path string, index int32) string { +func ComputeAddress(pubKeyHex string, chainHex string, path string, index int32) string { pubKeyBytes := DerivePublicKeyForPath( HexDecode(pubKeyHex), HexDecode(chainHex), fmt.Sprintf("%v/%v", path, index), ) - return AddrFromPubKeyBytes(coin, pubKeyBytes) + return AddrFromPubKeyBytes(pubKeyBytes) } func ComputePrivateKey(mprivHex string, chainHex string, path string, index int32) string { @@ -48,9 +40,9 @@ func ComputePrivateKey(mprivHex string, chainHex string, path string, index int3 return HexEncode(privKeyBytes) } -func ComputeAddressForPrivKey(coin string, privKey string) string { +func ComputeAddressForPrivKey(privKey string) string { pubKeyBytes := PubKeyBytesFromPrivKeyBytes(HexDecode(privKey), true) - return AddrFromPubKeyBytes(coin, pubKeyBytes) + return AddrFromPubKeyBytes(pubKeyBytes) } func SignMessage(privKey string, message string, compress bool) string { @@ -86,8 +78,8 @@ func ComputeMastersFromSeed(seed string) (string, string, string, string) { return HexEncode(pubKeyBytes), HexEncode(secret), HexEncode(chain), HexEncode(secret) } -func ComputeWIF(coin string, privKey string, compress bool) string { - return WIFFromPrivKeyBytes(coin, HexDecode(privKey), compress) +func ComputeWIF(privKey string, compress bool) string { + return WIFFromPrivKeyBytes(HexDecode(privKey), compress) } func ComputeTxId(rawTxHex string) string { @@ -100,7 +92,7 @@ func printKeyInfo(privKeyBytes []byte, pubKeyBytes []byte, chain []byte) { if pubKeyBytes == nil { pubKeyBytes = PubKeyBytesFromPrivKeyBytes(privKeyBytes, true) } - addr := AddrFromPubKeyBytes("BTC", pubKeyBytes) + addr := AddrFromPubKeyBytes(pubKeyBytes) log.Println("\nprikey:\t%v\npubKeyBytes:\t%v\naddr:\t%v\nchain:\t%v", HexEncode(privKeyBytes), HexEncode(pubKeyBytes), @@ -225,8 +217,9 @@ func I64(key []byte, data []byte) ([]byte, []byte) { return I[:32], I[32:] } -func AddrFromPubKeyBytes(coin string, pubKeyBytes []byte) string { - prefix := byte(0x00) // TODO Make const or configurable +// This returns a Bitcoin-like address. +func AddrFromPubKeyBytes(pubKeyBytes []byte) string { + prefix := byte(0x80) // TODO Make const or configurable h160 := CalcHash160(pubKeyBytes) h160 = append([]byte{prefix}, h160...) checksum := CalcHash256(h160) @@ -234,7 +227,15 @@ func AddrFromPubKeyBytes(coin string, pubKeyBytes []byte) string { return base58.Encode(b) } -func WIFFromPrivKeyBytes(coin string, privKeyBytes []byte, compress bool) string { +func AddrBytesFromPubKeyBytes(pubKeyBytes []byte) (addrBytes []byte, checksum []byte) { + prefix := byte(0x80) // TODO Make const or configurable + h160 := CalcHash160(pubKeyBytes) + _h160 := append([]byte{prefix}, h160...) + checksum = CalcHash256(_h160)[:4] + return h160, checksum +} + +func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string { prefix := byte(0x80) // TODO Make const or configurable bytes := append([]byte{prefix}, privKeyBytes...) if compress { From f17e6bf44c323d0b8883963428bc774aefebfda8 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 10 Apr 2017 03:01:35 -0700 Subject: [PATCH 22/45] Fix bitcoin addr scheme --- hd/address.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hd/address.go b/hd/address.go index 139707e46..28f70a98f 100644 --- a/hd/address.go +++ b/hd/address.go @@ -219,7 +219,7 @@ func I64(key []byte, data []byte) ([]byte, []byte) { // This returns a Bitcoin-like address. func AddrFromPubKeyBytes(pubKeyBytes []byte) string { - prefix := byte(0x80) // TODO Make const or configurable + prefix := byte(0x00) // TODO Make const or configurable h160 := CalcHash160(pubKeyBytes) h160 = append([]byte{prefix}, h160...) checksum := CalcHash256(h160) @@ -228,7 +228,7 @@ func AddrFromPubKeyBytes(pubKeyBytes []byte) string { } func AddrBytesFromPubKeyBytes(pubKeyBytes []byte) (addrBytes []byte, checksum []byte) { - prefix := byte(0x80) // TODO Make const or configurable + prefix := byte(0x00) // TODO Make const or configurable h160 := CalcHash160(pubKeyBytes) _h160 := append([]byte{prefix}, h160...) checksum = CalcHash256(_h160)[:4] From 9e57d521efa0bf734c51e0cad34837d31becc60b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Apr 2017 13:05:52 +0200 Subject: [PATCH 23/45] Add type assertions for sig/pub/privkey implemenetations --- priv_key.go | 4 ++++ pub_key.go | 4 ++++ signature.go | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/priv_key.go b/priv_key.go index 74d5b5a3e..efb85908c 100644 --- a/priv_key.go +++ b/priv_key.go @@ -64,6 +64,8 @@ var privKeyMapper = data.NewMapper(PrivKey{}). //------------------------------------- +var _ PrivKeyInner = PrivKeyEd25519{} + // Implements PrivKey type PrivKeyEd25519 [64]byte @@ -149,6 +151,8 @@ func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { //------------------------------------- +var _ PrivKeyInner = PrivKeySecp256k1{} + // Implements PrivKey type PrivKeySecp256k1 [32]byte diff --git a/pub_key.go b/pub_key.go index e6567d347..4de7d59cd 100644 --- a/pub_key.go +++ b/pub_key.go @@ -67,6 +67,8 @@ var pubKeyMapper = data.NewMapper(PubKey{}). //------------------------------------- +var _ PubKeyInner = PubKeyEd25519{} + // Implements PubKeyInner type PubKeyEd25519 [32]byte @@ -146,6 +148,8 @@ func (pubKey PubKeyEd25519) Wrap() PubKey { //------------------------------------- +var _ PubKeyInner = PubKeySecp256k1{} + // Implements PubKey. // Compressed pubkey (just the x-cord), // prefixed with 0x02 or 0x03, depending on the y-cord. diff --git a/signature.go b/signature.go index 66c9a8a27..5a165dc42 100644 --- a/signature.go +++ b/signature.go @@ -62,6 +62,8 @@ var sigMapper = data.NewMapper(Signature{}). //------------------------------------- +var _ SignatureInner = SignatureEd25519{} + // Implements Signature type SignatureEd25519 [64]byte @@ -100,6 +102,8 @@ func (sig SignatureEd25519) Wrap() Signature { //------------------------------------- +var _ SignatureInner = SignatureSecp256k1{} + // Implements Signature type SignatureSecp256k1 []byte From aecc32d3636d86cf66af9b39fc20bb53578c78c2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Apr 2017 13:10:57 +0200 Subject: [PATCH 24/45] Make dependencies explicit in glide.yaml --- .gitignore | 5 +++++ Makefile | 2 +- glide.yaml | 31 +++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 glide.yaml diff --git a/.gitignore b/.gitignore index 381931381..aac058142 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ *.swp *.swo +vendor + +# these should be locked in the including repo, the dependencies are mainly +# for testing and documentation +glide.lock diff --git a/Makefile b/Makefile index 0cd05448a..b6bd861a6 100644 --- a/Makefile +++ b/Makefile @@ -6,4 +6,4 @@ docs: godoc2md $(REPO) > README.md test: - go test ./... + go test `glide novendor` diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 000000000..4703d68c6 --- /dev/null +++ b/glide.yaml @@ -0,0 +1,31 @@ +package: github.com/tendermint/go-crypto +import: +- package: github.com/btcsuite/btcd + subpackages: + - btcec +- package: github.com/btcsuite/btcutil + subpackages: + - base58 +- package: github.com/tendermint/ed25519 + subpackages: + - extra25519 +- package: github.com/tendermint/go-common +- package: github.com/tendermint/go-data + version: develop +- package: github.com/tendermint/go-wire + version: develop +- package: golang.org/x/crypto + subpackages: + - blowfish + - nacl/secretbox + - openpgp/armor + - ripemd160 +testImport: +- package: github.com/mndrix/btcutil +- package: github.com/stretchr/testify + version: ^1.1.4 + subpackages: + - assert + - require +- package: github.com/tyler-smith/go-bip32 +- package: github.com/tyler-smith/go-bip39 From c410fc5e246e9accf95c6e80cb3c6aca2280755c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Apr 2017 14:24:07 +0200 Subject: [PATCH 25/45] Add extra test for encoding with sig,for p2p test fail --- encode_test.go | 73 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/encode_test.go b/encode_test.go index 2d709b943..c3380d435 100644 --- a/encode_test.go +++ b/encode_test.go @@ -16,13 +16,15 @@ type byter interface { } // go to wire encoding and back -func checkWire(t *testing.T, in byter, reader interface{}, typ byte) { +func checkWire(t *testing.T, in byter, reader interface{}, typ byte, size int) { // test to and from binary bin, err := data.ToWire(in) require.Nil(t, err, "%+v", err) assert.Equal(t, typ, bin[0]) // make sure this is compatible with current (Bytes()) encoding assert.Equal(t, in.Bytes(), bin) + // make sure we have the expected length + assert.Equal(t, size, len(bin)) err = data.FromWire(bin, reader) require.Nil(t, err, "%+v", err) @@ -67,23 +69,29 @@ func TestKeyEncodings(t *testing.T) { privKey PrivKey keyType byte keyName string + // 1 (type byte) + size of byte array + privSize, pubSize int }{ { - privKey: GenPrivKeyEd25519().Wrap(), - keyType: TypeEd25519, - keyName: NameEd25519, + privKey: GenPrivKeyEd25519().Wrap(), + keyType: TypeEd25519, + keyName: NameEd25519, + privSize: 65, + pubSize: 33, }, { - privKey: GenPrivKeySecp256k1().Wrap(), - keyType: TypeSecp256k1, - keyName: NameSecp256k1, + privKey: GenPrivKeySecp256k1().Wrap(), + keyType: TypeSecp256k1, + keyName: NameSecp256k1, + privSize: 33, + pubSize: 34, }, } for _, tc := range cases { // check (de/en)codings of private key var priv2, priv3, priv4 PrivKey - checkWire(t, tc.privKey, &priv2, tc.keyType) + checkWire(t, tc.privKey, &priv2, tc.keyType, tc.privSize) assert.EqualValues(t, tc.privKey, priv2) checkJSON(t, tc.privKey, &priv3, tc.keyName) assert.EqualValues(t, tc.privKey, priv3) @@ -93,7 +101,7 @@ func TestKeyEncodings(t *testing.T) { // check (de/en)codings of public key pubKey := tc.privKey.PubKey() var pub2, pub3, pub4 PubKey - checkWire(t, pubKey, &pub2, tc.keyType) + checkWire(t, pubKey, &pub2, tc.keyType, tc.pubSize) assert.EqualValues(t, pubKey, pub2) checkJSON(t, pubKey, &pub3, tc.keyName) assert.EqualValues(t, pubKey, pub3) @@ -126,3 +134,50 @@ func TestNilEncodings(t *testing.T) { assert.EqualValues(t, e, f) } + +type SigMessage struct { + Key PubKey + Sig Signature +} + +func (s SigMessage) Bytes() []byte { + return wire.BinaryBytes(s) +} + +func TestEmbededWireEncodings(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + privKey PrivKey + keyType byte + keyName string + size int // pub + sig size + }{ + { + privKey: GenPrivKeyEd25519().Wrap(), + keyType: TypeEd25519, + keyName: NameEd25519, + size: 2 + 32 + 64, + }, + // { + // privKey: GenPrivKeySecp256k1().Wrap(), + // keyType: TypeSecp256k1, + // keyName: NameSecp256k1, + // size: 2 + 33 + 72, // ugh, either 72 or 73 depending.... + // }, + } + + payload := randBytes(20) + for i, tc := range cases { + pubKey := tc.privKey.PubKey() + sig := tc.privKey.Sign(payload) + assert.True(pubKey.VerifyBytes(payload, sig), "%d", i) + + msg := SigMessage{ + Key: pubKey, + Sig: sig, + } + var msg2 SigMessage + checkWire(t, msg, &msg2, tc.keyType, tc.size) + } +} From 8bb25ec5ed8fcdc75cd67e77765379b524fd6931 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 18 Apr 2017 18:17:02 -0400 Subject: [PATCH 26/45] update paths --- armor.go | 2 +- embed_test.go | 2 +- encode_test.go | 2 +- glide.yaml | 7 +++---- priv_key.go | 4 ++-- pub_key.go | 4 ++-- random.go | 2 +- signature.go | 4 ++-- signature_test.go | 2 +- symmetric.go | 2 +- 10 files changed, 15 insertions(+), 16 deletions(-) diff --git a/armor.go b/armor.go index 9343284e1..3d2eff5e7 100644 --- a/armor.go +++ b/armor.go @@ -4,7 +4,7 @@ import ( "bytes" "io/ioutil" - . "github.com/tendermint/go-common" + . "github.com/tendermint/tmlibs/common" "golang.org/x/crypto/openpgp/armor" ) diff --git a/embed_test.go b/embed_test.go index 6ebba893c..e2d2fe504 100644 --- a/embed_test.go +++ b/embed_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" ) type PubName struct { diff --git a/encode_test.go b/encode_test.go index c3380d435..6c5d03a1d 100644 --- a/encode_test.go +++ b/encode_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" wire "github.com/tendermint/go-wire" ) diff --git a/glide.yaml b/glide.yaml index 4703d68c6..d5e0e2bbf 100644 --- a/glide.yaml +++ b/glide.yaml @@ -9,11 +9,10 @@ import: - package: github.com/tendermint/ed25519 subpackages: - extra25519 -- package: github.com/tendermint/go-common -- package: github.com/tendermint/go-data - version: develop +- package: github.com/tendermint/tmlibs + version: unstable - package: github.com/tendermint/go-wire - version: develop + version: unstable - package: golang.org/x/crypto subpackages: - blowfish diff --git a/priv_key.go b/priv_key.go index efb85908c..85f8a8ec4 100644 --- a/priv_key.go +++ b/priv_key.go @@ -6,8 +6,8 @@ import ( secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" - . "github.com/tendermint/go-common" - data "github.com/tendermint/go-data" + . "github.com/tendermint/tmlibs/common" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-wire" ) diff --git a/pub_key.go b/pub_key.go index 4de7d59cd..99839f288 100644 --- a/pub_key.go +++ b/pub_key.go @@ -7,8 +7,8 @@ import ( secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" - . "github.com/tendermint/go-common" - data "github.com/tendermint/go-data" + . "github.com/tendermint/tmlibs/common" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-wire" "golang.org/x/crypto/ripemd160" ) diff --git a/random.go b/random.go index edadeaab6..40cbcf8fa 100644 --- a/random.go +++ b/random.go @@ -8,7 +8,7 @@ import ( "io" "sync" - . "github.com/tendermint/go-common" + . "github.com/tendermint/tmlibs/common" ) var gRandInfo *randInfo diff --git a/signature.go b/signature.go index 5a165dc42..36451ed48 100644 --- a/signature.go +++ b/signature.go @@ -4,8 +4,8 @@ import ( "bytes" "fmt" - . "github.com/tendermint/go-common" - data "github.com/tendermint/go-data" + . "github.com/tendermint/tmlibs/common" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-wire" ) diff --git a/signature_test.go b/signature_test.go index 93aa17279..5e9f06723 100644 --- a/signature_test.go +++ b/signature_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/ed25519" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" ) func TestSignAndValidateEd25519(t *testing.T) { diff --git a/symmetric.go b/symmetric.go index 36bddd44c..d4ac9b55b 100644 --- a/symmetric.go +++ b/symmetric.go @@ -3,7 +3,7 @@ package crypto import ( "errors" - . "github.com/tendermint/go-common" + . "github.com/tendermint/tmlibs/common" "golang.org/x/crypto/nacl/secretbox" ) From 17ed6d178d8d1eff960ff84b38be4ceda97bade8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 19 Apr 2017 16:51:29 +0200 Subject: [PATCH 27/45] move go-crypto files pre-keys merge --- LICENSE => crypto/LICENSE | 0 Makefile => crypto/Makefile | 0 README.md => crypto/README.md | 0 armor.go => crypto/armor.go | 0 armor_test.go => crypto/armor_test.go | 0 {bcrypt => crypto/bcrypt}/base64.go | 0 {bcrypt => crypto/bcrypt}/bcrypt.go | 0 crypto.go => crypto/crypto.go | 0 embed_test.go => crypto/embed_test.go | 0 encode_test.go => crypto/encode_test.go | 0 glide.yaml => crypto/glide.yaml | 0 hash.go => crypto/hash.go | 0 {hd => crypto/hd}/address.go | 0 {hd => crypto/hd}/address_test.go | 0 {hd => crypto/hd}/hd_test.go | 0 {hd => crypto/hd}/test.json | 0 priv_key.go => crypto/priv_key.go | 0 pub_key.go => crypto/pub_key.go | 0 pub_key_test.go => crypto/pub_key_test.go | 0 random.go => crypto/random.go | 0 signature.go => crypto/signature.go | 0 signature_test.go => crypto/signature_test.go | 0 symmetric.go => crypto/symmetric.go | 0 symmetric_test.go => crypto/symmetric_test.go | 0 24 files changed, 0 insertions(+), 0 deletions(-) rename LICENSE => crypto/LICENSE (100%) rename Makefile => crypto/Makefile (100%) rename README.md => crypto/README.md (100%) rename armor.go => crypto/armor.go (100%) rename armor_test.go => crypto/armor_test.go (100%) rename {bcrypt => crypto/bcrypt}/base64.go (100%) rename {bcrypt => crypto/bcrypt}/bcrypt.go (100%) rename crypto.go => crypto/crypto.go (100%) rename embed_test.go => crypto/embed_test.go (100%) rename encode_test.go => crypto/encode_test.go (100%) rename glide.yaml => crypto/glide.yaml (100%) rename hash.go => crypto/hash.go (100%) rename {hd => crypto/hd}/address.go (100%) rename {hd => crypto/hd}/address_test.go (100%) rename {hd => crypto/hd}/hd_test.go (100%) rename {hd => crypto/hd}/test.json (100%) rename priv_key.go => crypto/priv_key.go (100%) rename pub_key.go => crypto/pub_key.go (100%) rename pub_key_test.go => crypto/pub_key_test.go (100%) rename random.go => crypto/random.go (100%) rename signature.go => crypto/signature.go (100%) rename signature_test.go => crypto/signature_test.go (100%) rename symmetric.go => crypto/symmetric.go (100%) rename symmetric_test.go => crypto/symmetric_test.go (100%) diff --git a/LICENSE b/crypto/LICENSE similarity index 100% rename from LICENSE rename to crypto/LICENSE diff --git a/Makefile b/crypto/Makefile similarity index 100% rename from Makefile rename to crypto/Makefile diff --git a/README.md b/crypto/README.md similarity index 100% rename from README.md rename to crypto/README.md diff --git a/armor.go b/crypto/armor.go similarity index 100% rename from armor.go rename to crypto/armor.go diff --git a/armor_test.go b/crypto/armor_test.go similarity index 100% rename from armor_test.go rename to crypto/armor_test.go diff --git a/bcrypt/base64.go b/crypto/bcrypt/base64.go similarity index 100% rename from bcrypt/base64.go rename to crypto/bcrypt/base64.go diff --git a/bcrypt/bcrypt.go b/crypto/bcrypt/bcrypt.go similarity index 100% rename from bcrypt/bcrypt.go rename to crypto/bcrypt/bcrypt.go diff --git a/crypto.go b/crypto/crypto.go similarity index 100% rename from crypto.go rename to crypto/crypto.go diff --git a/embed_test.go b/crypto/embed_test.go similarity index 100% rename from embed_test.go rename to crypto/embed_test.go diff --git a/encode_test.go b/crypto/encode_test.go similarity index 100% rename from encode_test.go rename to crypto/encode_test.go diff --git a/glide.yaml b/crypto/glide.yaml similarity index 100% rename from glide.yaml rename to crypto/glide.yaml diff --git a/hash.go b/crypto/hash.go similarity index 100% rename from hash.go rename to crypto/hash.go diff --git a/hd/address.go b/crypto/hd/address.go similarity index 100% rename from hd/address.go rename to crypto/hd/address.go diff --git a/hd/address_test.go b/crypto/hd/address_test.go similarity index 100% rename from hd/address_test.go rename to crypto/hd/address_test.go diff --git a/hd/hd_test.go b/crypto/hd/hd_test.go similarity index 100% rename from hd/hd_test.go rename to crypto/hd/hd_test.go diff --git a/hd/test.json b/crypto/hd/test.json similarity index 100% rename from hd/test.json rename to crypto/hd/test.json diff --git a/priv_key.go b/crypto/priv_key.go similarity index 100% rename from priv_key.go rename to crypto/priv_key.go diff --git a/pub_key.go b/crypto/pub_key.go similarity index 100% rename from pub_key.go rename to crypto/pub_key.go diff --git a/pub_key_test.go b/crypto/pub_key_test.go similarity index 100% rename from pub_key_test.go rename to crypto/pub_key_test.go diff --git a/random.go b/crypto/random.go similarity index 100% rename from random.go rename to crypto/random.go diff --git a/signature.go b/crypto/signature.go similarity index 100% rename from signature.go rename to crypto/signature.go diff --git a/signature_test.go b/crypto/signature_test.go similarity index 100% rename from signature_test.go rename to crypto/signature_test.go diff --git a/symmetric.go b/crypto/symmetric.go similarity index 100% rename from symmetric.go rename to crypto/symmetric.go diff --git a/symmetric_test.go b/crypto/symmetric_test.go similarity index 100% rename from symmetric_test.go rename to crypto/symmetric_test.go From 9016390a6e4d957b909650dbfa63b81510c3a68d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 19 Apr 2017 16:55:15 +0200 Subject: [PATCH 28/45] Moved crypto code to top level again --- crypto/LICENSE => LICENSE | 0 crypto/Makefile => Makefile | 0 crypto/README.md => README.md | 0 crypto/armor.go => armor.go | 0 crypto/armor_test.go => armor_test.go | 0 {crypto/bcrypt => bcrypt}/base64.go | 0 {crypto/bcrypt => bcrypt}/bcrypt.go | 0 crypto/crypto.go => crypto.go | 0 crypto/embed_test.go => embed_test.go | 0 crypto/encode_test.go => encode_test.go | 0 crypto/glide.yaml => glide.yaml | 0 crypto/hash.go => hash.go | 0 {crypto/hd => hd}/address.go | 0 {crypto/hd => hd}/address_test.go | 0 {crypto/hd => hd}/hd_test.go | 0 {crypto/hd => hd}/test.json | 0 keys/cmd/keys/main.go | 2 +- keys/cmd/root.go | 6 +++--- keys/cmd/serve.go | 2 +- keys/cmd/utils.go | 2 +- keys/cryptostore/enc_storage.go | 2 +- keys/cryptostore/encoder_test.go | 2 +- keys/cryptostore/holder.go | 2 +- keys/cryptostore/holder_test.go | 4 ++-- keys/cryptostore/storage_test.go | 2 +- keys/server/helpers.go | 2 +- keys/server/keys.go | 4 ++-- keys/server/keys_test.go | 10 +++++----- keys/storage/filestorage/main.go | 2 +- keys/storage/filestorage/main_test.go | 2 +- keys/storage/memstorage/main.go | 2 +- keys/storage/memstorage/main_test.go | 2 +- keys/tx/multi_test.go | 6 +++--- keys/tx/one_test.go | 6 +++--- keys/tx/reader.go | 2 +- keys/tx/reader_test.go | 4 ++-- crypto/priv_key.go => priv_key.go | 0 crypto/pub_key.go => pub_key.go | 0 crypto/pub_key_test.go => pub_key_test.go | 0 crypto/random.go => random.go | 0 crypto/signature.go => signature.go | 0 crypto/signature_test.go => signature_test.go | 0 crypto/symmetric.go => symmetric.go | 0 crypto/symmetric_test.go => symmetric_test.go | 0 44 files changed, 33 insertions(+), 33 deletions(-) rename crypto/LICENSE => LICENSE (100%) rename crypto/Makefile => Makefile (100%) rename crypto/README.md => README.md (100%) rename crypto/armor.go => armor.go (100%) rename crypto/armor_test.go => armor_test.go (100%) rename {crypto/bcrypt => bcrypt}/base64.go (100%) rename {crypto/bcrypt => bcrypt}/bcrypt.go (100%) rename crypto/crypto.go => crypto.go (100%) rename crypto/embed_test.go => embed_test.go (100%) rename crypto/encode_test.go => encode_test.go (100%) rename crypto/glide.yaml => glide.yaml (100%) rename crypto/hash.go => hash.go (100%) rename {crypto/hd => hd}/address.go (100%) rename {crypto/hd => hd}/address_test.go (100%) rename {crypto/hd => hd}/hd_test.go (100%) rename {crypto/hd => hd}/test.json (100%) rename crypto/priv_key.go => priv_key.go (100%) rename crypto/pub_key.go => pub_key.go (100%) rename crypto/pub_key_test.go => pub_key_test.go (100%) rename crypto/random.go => random.go (100%) rename crypto/signature.go => signature.go (100%) rename crypto/signature_test.go => signature_test.go (100%) rename crypto/symmetric.go => symmetric.go (100%) rename crypto/symmetric_test.go => symmetric_test.go (100%) diff --git a/crypto/LICENSE b/LICENSE similarity index 100% rename from crypto/LICENSE rename to LICENSE diff --git a/crypto/Makefile b/Makefile similarity index 100% rename from crypto/Makefile rename to Makefile diff --git a/crypto/README.md b/README.md similarity index 100% rename from crypto/README.md rename to README.md diff --git a/crypto/armor.go b/armor.go similarity index 100% rename from crypto/armor.go rename to armor.go diff --git a/crypto/armor_test.go b/armor_test.go similarity index 100% rename from crypto/armor_test.go rename to armor_test.go diff --git a/crypto/bcrypt/base64.go b/bcrypt/base64.go similarity index 100% rename from crypto/bcrypt/base64.go rename to bcrypt/base64.go diff --git a/crypto/bcrypt/bcrypt.go b/bcrypt/bcrypt.go similarity index 100% rename from crypto/bcrypt/bcrypt.go rename to bcrypt/bcrypt.go diff --git a/crypto/crypto.go b/crypto.go similarity index 100% rename from crypto/crypto.go rename to crypto.go diff --git a/crypto/embed_test.go b/embed_test.go similarity index 100% rename from crypto/embed_test.go rename to embed_test.go diff --git a/crypto/encode_test.go b/encode_test.go similarity index 100% rename from crypto/encode_test.go rename to encode_test.go diff --git a/crypto/glide.yaml b/glide.yaml similarity index 100% rename from crypto/glide.yaml rename to glide.yaml diff --git a/crypto/hash.go b/hash.go similarity index 100% rename from crypto/hash.go rename to hash.go diff --git a/crypto/hd/address.go b/hd/address.go similarity index 100% rename from crypto/hd/address.go rename to hd/address.go diff --git a/crypto/hd/address_test.go b/hd/address_test.go similarity index 100% rename from crypto/hd/address_test.go rename to hd/address_test.go diff --git a/crypto/hd/hd_test.go b/hd/hd_test.go similarity index 100% rename from crypto/hd/hd_test.go rename to hd/hd_test.go diff --git a/crypto/hd/test.json b/hd/test.json similarity index 100% rename from crypto/hd/test.json rename to hd/test.json diff --git a/keys/cmd/keys/main.go b/keys/cmd/keys/main.go index 8b92b3f49..995df845b 100644 --- a/keys/cmd/keys/main.go +++ b/keys/cmd/keys/main.go @@ -17,7 +17,7 @@ package main import ( "os" - "github.com/tendermint/go-keys/cmd" + "github.com/tendermint/go-crypto/keys/cmd" ) func main() { diff --git a/keys/cmd/root.go b/keys/cmd/root.go index 64adf2b0b..401e7ec38 100644 --- a/keys/cmd/root.go +++ b/keys/cmd/root.go @@ -19,9 +19,9 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - keys "github.com/tendermint/go-keys" - "github.com/tendermint/go-keys/cryptostore" - "github.com/tendermint/go-keys/storage/filestorage" + keys "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/cryptostore" + "github.com/tendermint/go-crypto/keys/storage/filestorage" ) const KeySubdir = "keys" diff --git a/keys/cmd/serve.go b/keys/cmd/serve.go index c3f7dbe39..5ea96db3f 100644 --- a/keys/cmd/serve.go +++ b/keys/cmd/serve.go @@ -25,7 +25,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/tendermint/go-keys/server" + "github.com/tendermint/go-crypto/keys/server" ) // serveCmd represents the serve command diff --git a/keys/cmd/utils.go b/keys/cmd/utils.go index 31a3a8df3..c61383004 100644 --- a/keys/cmd/utils.go +++ b/keys/cmd/utils.go @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/viper" data "github.com/tendermint/go-data" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) const PassLength = 10 diff --git a/keys/cryptostore/enc_storage.go b/keys/cryptostore/enc_storage.go index 9589f4cc1..70e212138 100644 --- a/keys/cryptostore/enc_storage.go +++ b/keys/cryptostore/enc_storage.go @@ -2,7 +2,7 @@ package cryptostore import ( crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) // encryptedStorage needs passphrase to get private keys diff --git a/keys/cryptostore/encoder_test.go b/keys/cryptostore/encoder_test.go index 8b72509ed..0535737d7 100644 --- a/keys/cryptostore/encoder_test.go +++ b/keys/cryptostore/encoder_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/go-keys/cryptostore" + "github.com/tendermint/go-crypto/keys/cryptostore" ) func TestNoopEncoder(t *testing.T) { diff --git a/keys/cryptostore/holder.go b/keys/cryptostore/holder.go index 549326614..42d4662de 100644 --- a/keys/cryptostore/holder.go +++ b/keys/cryptostore/holder.go @@ -1,6 +1,6 @@ package cryptostore -import keys "github.com/tendermint/go-keys" +import keys "github.com/tendermint/go-crypto/keys" // Manager combines encyption and storage implementation to provide // a full-featured key manager diff --git a/keys/cryptostore/holder_test.go b/keys/cryptostore/holder_test.go index 860869c86..4f0383198 100644 --- a/keys/cryptostore/holder_test.go +++ b/keys/cryptostore/holder_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/go-keys/cryptostore" - "github.com/tendermint/go-keys/storage/memstorage" + "github.com/tendermint/go-crypto/keys/cryptostore" + "github.com/tendermint/go-crypto/keys/storage/memstorage" ) // TestKeyManagement makes sure we can manipulate these keys well diff --git a/keys/cryptostore/storage_test.go b/keys/cryptostore/storage_test.go index 78d107c11..b109c44e8 100644 --- a/keys/cryptostore/storage_test.go +++ b/keys/cryptostore/storage_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) func TestSortKeys(t *testing.T) { diff --git a/keys/server/helpers.go b/keys/server/helpers.go index 111f158e7..954922060 100644 --- a/keys/server/helpers.go +++ b/keys/server/helpers.go @@ -13,7 +13,7 @@ import ( "net/http" data "github.com/tendermint/go-data" - "github.com/tendermint/go-keys/server/types" + "github.com/tendermint/go-crypto/keys/server/types" "github.com/pkg/errors" ) diff --git a/keys/server/keys.go b/keys/server/keys.go index 78a382e5e..90d6da2b0 100644 --- a/keys/server/keys.go +++ b/keys/server/keys.go @@ -5,8 +5,8 @@ import ( "net/http" "github.com/gorilla/mux" - keys "github.com/tendermint/go-keys" - "github.com/tendermint/go-keys/server/types" + keys "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/server/types" ) type Keys struct { diff --git a/keys/server/keys_test.go b/keys/server/keys_test.go index 8333e8e53..4908559b0 100644 --- a/keys/server/keys_test.go +++ b/keys/server/keys_test.go @@ -10,11 +10,11 @@ import ( "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - keys "github.com/tendermint/go-keys" - "github.com/tendermint/go-keys/cryptostore" - "github.com/tendermint/go-keys/server" - "github.com/tendermint/go-keys/server/types" - "github.com/tendermint/go-keys/storage/memstorage" + keys "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/cryptostore" + "github.com/tendermint/go-crypto/keys/server" + "github.com/tendermint/go-crypto/keys/server/types" + "github.com/tendermint/go-crypto/keys/storage/memstorage" ) func TestKeyServer(t *testing.T) { diff --git a/keys/storage/filestorage/main.go b/keys/storage/filestorage/main.go index 737f0772c..1add482e2 100644 --- a/keys/storage/filestorage/main.go +++ b/keys/storage/filestorage/main.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) const ( diff --git a/keys/storage/filestorage/main_test.go b/keys/storage/filestorage/main_test.go index 890898dcb..c569eb6f9 100644 --- a/keys/storage/filestorage/main_test.go +++ b/keys/storage/filestorage/main_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) func TestBasicCRUD(t *testing.T) { diff --git a/keys/storage/memstorage/main.go b/keys/storage/memstorage/main.go index 69c8d9b03..195fa7a17 100644 --- a/keys/storage/memstorage/main.go +++ b/keys/storage/memstorage/main.go @@ -7,7 +7,7 @@ package memstorage import ( "github.com/pkg/errors" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) type data struct { diff --git a/keys/storage/memstorage/main_test.go b/keys/storage/memstorage/main_test.go index 7605c8225..8bc7e355f 100644 --- a/keys/storage/memstorage/main_test.go +++ b/keys/storage/memstorage/main_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) func TestBasicCRUD(t *testing.T) { diff --git a/keys/tx/multi_test.go b/keys/tx/multi_test.go index 815b9e15a..97463a5f7 100644 --- a/keys/tx/multi_test.go +++ b/keys/tx/multi_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-keys" - "github.com/tendermint/go-keys/cryptostore" - "github.com/tendermint/go-keys/storage/memstorage" + keys "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/cryptostore" + "github.com/tendermint/go-crypto/keys/storage/memstorage" ) func TestMultiSig(t *testing.T) { diff --git a/keys/tx/one_test.go b/keys/tx/one_test.go index 71b8011ef..05af347b4 100644 --- a/keys/tx/one_test.go +++ b/keys/tx/one_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-keys" - "github.com/tendermint/go-keys/cryptostore" - "github.com/tendermint/go-keys/storage/memstorage" + keys "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/cryptostore" + "github.com/tendermint/go-crypto/keys/storage/memstorage" ) func TestOneSig(t *testing.T) { diff --git a/keys/tx/reader.go b/keys/tx/reader.go index 7689693cb..16a403719 100644 --- a/keys/tx/reader.go +++ b/keys/tx/reader.go @@ -3,7 +3,7 @@ package tx import ( crypto "github.com/tendermint/go-crypto" data "github.com/tendermint/go-data" - keys "github.com/tendermint/go-keys" + keys "github.com/tendermint/go-crypto/keys" ) const ( diff --git a/keys/tx/reader_test.go b/keys/tx/reader_test.go index 54b76af96..4b365604d 100644 --- a/keys/tx/reader_test.go +++ b/keys/tx/reader_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" data "github.com/tendermint/go-data" - "github.com/tendermint/go-keys/cryptostore" - "github.com/tendermint/go-keys/storage/memstorage" + "github.com/tendermint/go-crypto/keys/cryptostore" + "github.com/tendermint/go-crypto/keys/storage/memstorage" ) func TestReader(t *testing.T) { diff --git a/crypto/priv_key.go b/priv_key.go similarity index 100% rename from crypto/priv_key.go rename to priv_key.go diff --git a/crypto/pub_key.go b/pub_key.go similarity index 100% rename from crypto/pub_key.go rename to pub_key.go diff --git a/crypto/pub_key_test.go b/pub_key_test.go similarity index 100% rename from crypto/pub_key_test.go rename to pub_key_test.go diff --git a/crypto/random.go b/random.go similarity index 100% rename from crypto/random.go rename to random.go diff --git a/crypto/signature.go b/signature.go similarity index 100% rename from crypto/signature.go rename to signature.go diff --git a/crypto/signature_test.go b/signature_test.go similarity index 100% rename from crypto/signature_test.go rename to signature_test.go diff --git a/crypto/symmetric.go b/symmetric.go similarity index 100% rename from crypto/symmetric.go rename to symmetric.go diff --git a/crypto/symmetric_test.go b/symmetric_test.go similarity index 100% rename from crypto/symmetric_test.go rename to symmetric_test.go From 0bfae964e140ef1d8b25e5838430c459e04760e1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 19 Apr 2017 17:07:12 +0200 Subject: [PATCH 29/45] Moved keys cmd to top level --- .gitignore | 4 - Makefile | 20 +++- {keys/cmd => cmd}/README.md | 0 {keys/cmd => cmd}/common.go | 2 +- {keys/cmd => cmd}/get.go | 0 {keys/cmd => cmd}/keys/main.go | 2 +- {keys/cmd => cmd}/list.go | 0 {keys/cmd => cmd}/new.go | 0 {keys/cmd => cmd}/root.go | 0 {keys/cmd => cmd}/serve.go | 0 {keys/cmd => cmd}/update.go | 0 {keys/cmd => cmd}/utils.go | 2 +- keys/glide.lock => glide.lock | 75 ++++++++---- glide.yaml | 10 ++ keys/LICENSE | 202 --------------------------------- keys/glide.yaml | 19 ---- 16 files changed, 82 insertions(+), 254 deletions(-) rename {keys/cmd => cmd}/README.md (100%) rename {keys/cmd => cmd}/common.go (98%) rename {keys/cmd => cmd}/get.go (100%) rename {keys/cmd => cmd}/keys/main.go (94%) rename {keys/cmd => cmd}/list.go (100%) rename {keys/cmd => cmd}/new.go (100%) rename {keys/cmd => cmd}/root.go (100%) rename {keys/cmd => cmd}/serve.go (100%) rename {keys/cmd => cmd}/update.go (100%) rename {keys/cmd => cmd}/utils.go (97%) rename keys/glide.lock => glide.lock (60%) delete mode 100644 keys/LICENSE delete mode 100644 keys/glide.yaml diff --git a/.gitignore b/.gitignore index aac058142..f37225baa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ *.swp *.swo vendor - -# these should be locked in the including repo, the dependencies are mainly -# for testing and documentation -glide.lock diff --git a/Makefile b/Makefile index b6bd861a6..59440fe66 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,27 @@ -.PHONY: docs +.PHONEY: all docs test install get_vendor_deps ensure_tools + +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 `glide novendor` + +get_vendor_deps: ensure_tools + @rm -rf vendor/ + @echo "--> Running glide install" + @glide install + +ensure_tools: + go get $(GOTOOLS) + + diff --git a/keys/cmd/README.md b/cmd/README.md similarity index 100% rename from keys/cmd/README.md rename to cmd/README.md diff --git a/keys/cmd/common.go b/cmd/common.go similarity index 98% rename from keys/cmd/common.go rename to cmd/common.go index eb7a158ae..c68cd4304 100644 --- a/keys/cmd/common.go +++ b/cmd/common.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - data "github.com/tendermint/go-data" "github.com/tendermint/go-data/base58" + data "github.com/tendermint/go-wire/data" ) /******* diff --git a/keys/cmd/get.go b/cmd/get.go similarity index 100% rename from keys/cmd/get.go rename to cmd/get.go diff --git a/keys/cmd/keys/main.go b/cmd/keys/main.go similarity index 94% rename from keys/cmd/keys/main.go rename to cmd/keys/main.go index 995df845b..8dd8d6505 100644 --- a/keys/cmd/keys/main.go +++ b/cmd/keys/main.go @@ -17,7 +17,7 @@ package main import ( "os" - "github.com/tendermint/go-crypto/keys/cmd" + "github.com/tendermint/go-crypto/cmd" ) func main() { diff --git a/keys/cmd/list.go b/cmd/list.go similarity index 100% rename from keys/cmd/list.go rename to cmd/list.go diff --git a/keys/cmd/new.go b/cmd/new.go similarity index 100% rename from keys/cmd/new.go rename to cmd/new.go diff --git a/keys/cmd/root.go b/cmd/root.go similarity index 100% rename from keys/cmd/root.go rename to cmd/root.go diff --git a/keys/cmd/serve.go b/cmd/serve.go similarity index 100% rename from keys/cmd/serve.go rename to cmd/serve.go diff --git a/keys/cmd/update.go b/cmd/update.go similarity index 100% rename from keys/cmd/update.go rename to cmd/update.go diff --git a/keys/cmd/utils.go b/cmd/utils.go similarity index 97% rename from keys/cmd/utils.go rename to cmd/utils.go index c61383004..a75262676 100644 --- a/keys/cmd/utils.go +++ b/cmd/utils.go @@ -6,8 +6,8 @@ import ( "github.com/bgentry/speakeasy" "github.com/pkg/errors" "github.com/spf13/viper" - data "github.com/tendermint/go-data" keys "github.com/tendermint/go-crypto/keys" + data "github.com/tendermint/go-wire/data" ) const PassLength = 10 diff --git a/keys/glide.lock b/glide.lock similarity index 60% rename from keys/glide.lock rename to glide.lock index c74262a4e..b804b4801 100644 --- a/keys/glide.lock +++ b/glide.lock @@ -1,14 +1,26 @@ -hash: 4a517b0f71ea6e3aadcf98286cde97c2f567e9e7999a4c7ec9ce6e5b6c21564a -updated: 2017-03-02T16:57:20.740518259-05:00 +hash: 8a63c035134ec024df64d8cc43a732712e48e4cfc5de30d45c1b692b3e9a75b8 +updated: 2017-04-19T17:06:49.640329917+02:00 imports: - name: github.com/bgentry/speakeasy version: 675b82c74c0ed12283ee81ba8a534c8982c07b85 - name: github.com/btcsuite/btcd - version: d06c0bb181529331be8f8d9350288c420d9e60e4 + version: 583684b21bfbde9b5fc4403916fd7c807feb0289 subpackages: - btcec + - chaincfg + - chaincfg/chainhash + - wire +- name: github.com/btcsuite/btcutil + version: a5ecb5d9547afe8d1672073dbdc348203de744a0 + subpackages: + - base58 + - hdkeychain +- name: github.com/btcsuite/golangcrypto + version: 53f62d9b43e87a6c56975cf862af7edf33a8d0df + subpackages: + - ripemd160 - name: github.com/fsnotify/fsnotify - version: 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1 + version: 4da3e2cfbabc9f751898f250b49f2439785783a1 - name: github.com/go-playground/locales version: 084b0226cf88d891a2bdeccac01d592af13a8f7b subpackages: @@ -20,9 +32,9 @@ imports: - name: github.com/gorilla/context version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 - name: github.com/gorilla/handlers - version: 13d73096a474cac93275c679c7b8a2dc17ddba82 + version: 3a5767ca75ece5f7f1440b1d16975247f8d8b221 - name: github.com/gorilla/mux - version: 599cba5e7b6137d46ddf58fb1765f5d928e69604 + version: 392c28fe23e1c45ddba891b0320b3b5df220beea - name: github.com/hashicorp/hcl version: 630949a3c5fa3c613328e1b8256052cbc2327c9b subpackages: @@ -37,25 +49,25 @@ imports: - name: github.com/inconshreveable/mousetrap version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 - name: github.com/magiconair/properties - version: b3b15ef068fd0b17ddf408a23669f20811d194d2 + version: 51463bfca2576e06c62a8504b5c0f06d61312647 - name: github.com/mattn/go-colorable - version: 5411d3eea5978e6cdc258b30de592b60df6aba96 + version: a392f450ea64cee2b268dfaacdc2502b50a22b18 - name: github.com/mattn/go-isatty - version: 281032e84ae07510239465db46bf442aa44b953a + version: 57fdcb988a5c543893cc61bce354a6e24ab70022 - name: github.com/mitchellh/mapstructure - version: db1efb556f84b25a0a13a04aad883943538ad2e0 + version: 53818660ed4955e899c0bcafa97299a388bd7c8e - name: github.com/pelletier/go-buffruneio version: c37440a7cf42ac63b919c752ca73a85067e05992 - name: github.com/pelletier/go-toml version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a - name: github.com/pkg/errors - version: 248dadf4e9068a0b3e79f02ed0a610d935de5302 + version: bfd5150e4e41705ded2129ec33379de1cb90b513 - name: github.com/spf13/afero version: 9be650865eab0c12963d8753212f4f9c66cdcf12 subpackages: - mem - name: github.com/spf13/cast - version: f820543c3592e283e311a60d2a600a664e39f6f7 + version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 - name: github.com/spf13/cobra version: fcd0c5a1df88f5d6784cb4feead962c3f3d0b66c - name: github.com/spf13/jwalterweatherman @@ -63,55 +75,64 @@ imports: - name: github.com/spf13/pflag version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 - name: github.com/spf13/viper - version: 7538d73b4eb9511d85a9f1dfef202eeb8ac260f4 + version: 5d46e70da8c0b6f812e0b170b7a985753b5c63cb - name: github.com/tendermint/ed25519 version: 1f52c6f8b8a5c7908aff4497c186af344b428925 subpackages: - edwards25519 - extra25519 -- name: github.com/tendermint/go-common - version: 339e135776142939d82bc8e699db0bf391fd938d -- name: github.com/tendermint/go-crypto - version: 562b4cc9ef0d20217f6e95679f9e83cb7bc98b17 - name: github.com/tendermint/go-data - version: 32271140e8fd5abdbb22e268d7a02421fa382f0b + version: e7fcc6d081ec8518912fcdc103188275f83a3ee5 subpackages: - base58 -- name: github.com/tendermint/go-logger - version: cefb3a45c0bf3c493a04e9bcd9b1540528be59f2 - name: github.com/tendermint/go-wire - version: 3216ec9d47bbdf8d4fc27d22169ea86a6688bc15 + version: 9127836cbb6dd99e020cb840a0cedcedc4671468 + subpackages: + - data + - data/base58 - name: github.com/tendermint/log15 version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 subpackages: - term +- name: github.com/tendermint/tmlibs + version: 2f8551d3b614dd0c0c6c114c42ab25901cc41a52 + subpackages: + - common + - logger - name: golang.org/x/crypto - version: 453249f01cfeb54c3d549ddb75ff152ca243f9d8 + version: 728b753d0135da6801d45a38e6f43ff55779c5c2 subpackages: + - bcrypt + - blowfish - nacl/secretbox - openpgp/armor - openpgp/errors + - pbkdf2 - poly1305 - ripemd160 - salsa20/salsa - name: golang.org/x/sys - version: e24f485414aeafb646f6fca458b0bf869c0880a1 + version: 99f16d856c9836c42d24e7ab64ea72916925fa97 subpackages: - unix - name: golang.org/x/text - version: d680ca3ed853995402af43b866311167281bdc20 + version: f4b4367115ec2de254587813edaa901bc1c723a8 subpackages: - transform - unicode/norm - name: gopkg.in/go-playground/validator.v9 version: 4bd19358521c53f09639f21e2a9d6883d6890f24 - name: gopkg.in/yaml.v2 - version: a3f3340b5840cee44f372bddb5880fcbc419b46a + version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b testImports: +- name: github.com/cmars/basen + version: fe3947df716ebfda9847eb1b9a48f9592e06478c - name: github.com/davecgh/go-spew version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 subpackages: - spew +- name: github.com/mndrix/btcutil + version: d3a63a5752ecf3fbc06bd97365da752111c263df - name: github.com/pmezard/go-difflib version: d8ed2627bdf02c080bf22230dbb337003b7aba2d subpackages: @@ -121,3 +142,7 @@ testImports: subpackages: - assert - require +- name: github.com/tyler-smith/go-bip32 + version: eb790af526c30f23a7c8b00a48e342f9d0bd6386 +- name: github.com/tyler-smith/go-bip39 + version: 8e7a99b3e716f36d3b080a9a70f9eb45abe4edcc diff --git a/glide.yaml b/glide.yaml index d5e0e2bbf..2566feef9 100644 --- a/glide.yaml +++ b/glide.yaml @@ -13,12 +13,22 @@ import: version: unstable - package: github.com/tendermint/go-wire version: unstable + subpackages: + - data + - data/base58 - package: golang.org/x/crypto subpackages: - blowfish - nacl/secretbox - openpgp/armor - ripemd160 +- package: github.com/bgentry/speakeasy +- package: github.com/gorilla/handlers +- package: github.com/gorilla/mux +- package: github.com/pkg/errors +- package: github.com/spf13/cobra +- package: github.com/spf13/viper +- package: gopkg.in/go-playground/validator.v9 testImport: - package: github.com/mndrix/btcutil - package: github.com/stretchr/testify diff --git a/keys/LICENSE b/keys/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/keys/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/keys/glide.yaml b/keys/glide.yaml deleted file mode 100644 index 39402fa8b..000000000 --- a/keys/glide.yaml +++ /dev/null @@ -1,19 +0,0 @@ -package: github.com/tendermint/go-keys -import: -- package: github.com/bgentry/speakeasy -- package: github.com/gorilla/handlers -- package: github.com/gorilla/mux -- package: github.com/pkg/errors -- package: github.com/spf13/cobra -- package: github.com/spf13/viper -- package: github.com/tendermint/go-crypto - version: develop -- package: github.com/tendermint/go-data - subpackages: - - base58 -- package: gopkg.in/go-playground/validator.v9 -testImport: -- package: github.com/stretchr/testify - subpackages: - - assert - - require From 91bd7efb7bcc2eecda2715286a08b8dddf5f3f64 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 19 Apr 2017 17:14:19 +0200 Subject: [PATCH 30/45] Fixed all imports in keys --- keys/cryptostore/enc_storage.go | 4 ++-- keys/cryptostore/encoder.go | 2 +- keys/cryptostore/encoder_test.go | 2 +- keys/cryptostore/generator.go | 4 ++-- keys/server/helpers.go | 2 +- keys/storage/filestorage/main.go | 2 +- keys/storage/filestorage/main_test.go | 2 +- keys/storage/memstorage/main_test.go | 2 +- keys/transactions.go | 4 ++-- keys/tx/multi.go | 10 +++++----- keys/tx/one.go | 7 +++---- keys/tx/reader.go | 6 +++--- keys/tx/reader_test.go | 2 +- 13 files changed, 24 insertions(+), 25 deletions(-) diff --git a/keys/cryptostore/enc_storage.go b/keys/cryptostore/enc_storage.go index 70e212138..daeb220b5 100644 --- a/keys/cryptostore/enc_storage.go +++ b/keys/cryptostore/enc_storage.go @@ -24,7 +24,7 @@ func (es encryptedStorage) Put(name, pass string, key crypto.PrivKey) error { func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.Info, error) { secret, info, err := es.store.Get(name) if err != nil { - return nil, info, err + return crypto.PrivKey{}, info, err } key, err := es.coder.Decrypt(secret, pass) return key, info, err @@ -44,6 +44,6 @@ func info(name string, key crypto.PrivKey) keys.Info { return keys.Info{ Name: name, Address: pub.Address(), - PubKey: crypto.PubKeyS{pub}, + PubKey: pub, } } diff --git a/keys/cryptostore/encoder.go b/keys/cryptostore/encoder.go index 03bc1e4e8..12792813c 100644 --- a/keys/cryptostore/encoder.go +++ b/keys/cryptostore/encoder.go @@ -37,7 +37,7 @@ func (e secretbox) Decrypt(data []byte, pass string) (crypto.PrivKey, error) { s := secret(pass) private, err := crypto.DecryptSymmetric(data, s) if err != nil { - return nil, errors.Wrap(err, "Invalid Passphrase") + return crypto.PrivKey{}, errors.Wrap(err, "Invalid Passphrase") } key, err := crypto.PrivKeyFromBytes(private) return key, errors.Wrap(err, "Invalid Passphrase") diff --git a/keys/cryptostore/encoder_test.go b/keys/cryptostore/encoder_test.go index 0535737d7..e5ea21111 100644 --- a/keys/cryptostore/encoder_test.go +++ b/keys/cryptostore/encoder_test.go @@ -50,7 +50,7 @@ func TestSecretBox(t *testing.T) { // decoding with a different pass is an error pk, err := enc.Decrypt(b, "decode") require.NotNil(err) - require.Nil(pk) + require.True(pk.Empty()) // but decoding with the same passphrase gets us our key pk, err = enc.Decrypt(b, pass) diff --git a/keys/cryptostore/generator.go b/keys/cryptostore/generator.go index cf877f20b..6bbdb6441 100644 --- a/keys/cryptostore/generator.go +++ b/keys/cryptostore/generator.go @@ -25,11 +25,11 @@ func (f GenFunc) Generate() crypto.PrivKey { } func genEd25519() crypto.PrivKey { - return crypto.GenPrivKeyEd25519() + return crypto.GenPrivKeyEd25519().Wrap() } func genSecp256() crypto.PrivKey { - return crypto.GenPrivKeySecp256k1() + return crypto.GenPrivKeySecp256k1().Wrap() } func getGenerator(algo string) (Generator, error) { diff --git a/keys/server/helpers.go b/keys/server/helpers.go index 954922060..710e4f392 100644 --- a/keys/server/helpers.go +++ b/keys/server/helpers.go @@ -12,7 +12,7 @@ import ( "io/ioutil" "net/http" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-crypto/keys/server/types" "github.com/pkg/errors" diff --git a/keys/storage/filestorage/main.go b/keys/storage/filestorage/main.go index 1add482e2..696b200fc 100644 --- a/keys/storage/filestorage/main.go +++ b/keys/storage/filestorage/main.go @@ -135,7 +135,7 @@ func readInfo(path string) (info keys.Info, err error) { return } pk, err := crypto.PubKeyFromBytes(data) - info.PubKey = crypto.PubKeyS{pk} + info.PubKey = pk return } diff --git a/keys/storage/filestorage/main_test.go b/keys/storage/filestorage/main_test.go index c569eb6f9..28c950c2c 100644 --- a/keys/storage/filestorage/main_test.go +++ b/keys/storage/filestorage/main_test.go @@ -25,7 +25,7 @@ func TestBasicCRUD(t *testing.T) { pubkey := crypto.GenPrivKeyEd25519().PubKey() info := keys.Info{ Name: name, - PubKey: crypto.PubKeyS{pubkey}, + PubKey: pubkey.Wrap(), } // No data: Get and Delete return nothing diff --git a/keys/storage/memstorage/main_test.go b/keys/storage/memstorage/main_test.go index 8bc7e355f..feccb387f 100644 --- a/keys/storage/memstorage/main_test.go +++ b/keys/storage/memstorage/main_test.go @@ -17,7 +17,7 @@ func TestBasicCRUD(t *testing.T) { pubkey := crypto.GenPrivKeyEd25519().PubKey() info := keys.Info{ Name: name, - PubKey: crypto.PubKeyS{pubkey}, + PubKey: pubkey, } // No data: Get and Delete return nothing diff --git a/keys/transactions.go b/keys/transactions.go index 91dc0e273..aafb77873 100644 --- a/keys/transactions.go +++ b/keys/transactions.go @@ -4,14 +4,14 @@ import ( "sort" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" ) // Info is the public information about a key type Info struct { Name string `json:"name"` Address data.Bytes `json:"address"` - PubKey crypto.PubKeyS `json:"pubkey"` + PubKey crypto.PubKey `json:"pubkey"` } func (i *Info) Format() Info { diff --git a/keys/tx/multi.go b/keys/tx/multi.go index eac86b010..f069fb273 100644 --- a/keys/tx/multi.go +++ b/keys/tx/multi.go @@ -3,7 +3,7 @@ package tx import ( "github.com/pkg/errors" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" ) // MultiSig lets us wrap arbitrary data with a go-crypto signature @@ -16,8 +16,8 @@ type MultiSig struct { } type Signed struct { - Sig crypto.SignatureS - Pubkey crypto.PubKeyS + Sig crypto.Signature + Pubkey crypto.PubKey } var _ SigInner = &MultiSig{} @@ -36,12 +36,12 @@ func (s *MultiSig) SignBytes() []byte { // Depending on the Signable, one may be able to call this multiple times for multisig // Returns error if called with invalid data or too many times func (s *MultiSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { - if pubkey == nil || sig == nil { + if pubkey.Empty() || sig.Empty() { return errors.New("Signature or Key missing") } // set the value once we are happy - x := Signed{crypto.SignatureS{sig}, crypto.PubKeyS{pubkey}} + x := Signed{sig, pubkey} s.Sigs = append(s.Sigs, x) return nil } diff --git a/keys/tx/one.go b/keys/tx/one.go index 0ad61dd50..af468cc2c 100644 --- a/keys/tx/one.go +++ b/keys/tx/one.go @@ -3,7 +3,7 @@ package tx import ( "github.com/pkg/errors" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" ) // OneSig lets us wrap arbitrary data with a go-crypto signature @@ -31,7 +31,7 @@ func (s *OneSig) SignBytes() []byte { // Depending on the Signable, one may be able to call this multiple times for multisig // Returns error if called with invalid data or too many times func (s *OneSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { - if pubkey == nil || sig == nil { + if pubkey.Empty() || sig.Empty() { return errors.New("Signature or Key missing") } if !s.Sig.Empty() { @@ -39,8 +39,7 @@ func (s *OneSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { } // set the value once we are happy - s.Pubkey = crypto.PubKeyS{pubkey} - s.Sig = crypto.SignatureS{sig} + s.Signed = Signed{sig, pubkey} return nil } diff --git a/keys/tx/reader.go b/keys/tx/reader.go index 16a403719..265e88b46 100644 --- a/keys/tx/reader.go +++ b/keys/tx/reader.go @@ -2,8 +2,8 @@ package tx import ( crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-data" keys "github.com/tendermint/go-crypto/keys" + data "github.com/tendermint/go-wire/data" ) const ( @@ -18,8 +18,8 @@ var TxMapper data.Mapper func init() { TxMapper = data.NewMapper(Sig{}). - RegisterInterface(&OneSig{}, nameOneSig, typeOneSig). - RegisterInterface(&MultiSig{}, nameMultiSig, typeMultiSig) + RegisterImplementation(&OneSig{}, nameOneSig, typeOneSig). + RegisterImplementation(&MultiSig{}, nameMultiSig, typeMultiSig) } /* diff --git a/keys/tx/reader_test.go b/keys/tx/reader_test.go index 4b365604d..c50481f14 100644 --- a/keys/tx/reader_test.go +++ b/keys/tx/reader_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-data" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-crypto/keys/cryptostore" "github.com/tendermint/go-crypto/keys/storage/memstorage" ) From e3f9b8731bb2e8cb38f2d49dee92713e95f264bb Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 21 Apr 2017 14:31:49 -0400 Subject: [PATCH 31/45] circle.yml --- circle.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..23ac4bd9f --- /dev/null +++ b/circle.yml @@ -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" From 9b95da8fa4187f6799558d89b271dc8ab6485615 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 21 Apr 2017 16:10:06 -0400 Subject: [PATCH 32/45] go-data -> go-wire/data --- cmd/common.go | 2 +- glide.lock | 20 ++++++++------------ glide.yaml | 4 ++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/cmd/common.go b/cmd/common.go index c68cd4304..098bb6c7d 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/tendermint/go-data/base58" data "github.com/tendermint/go-wire/data" + "github.com/tendermint/go-wire/data/base58" ) /******* diff --git a/glide.lock b/glide.lock index b804b4801..dd8ec1952 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 8a63c035134ec024df64d8cc43a732712e48e4cfc5de30d45c1b692b3e9a75b8 -updated: 2017-04-19T17:06:49.640329917+02:00 +hash: 3bcee9fbccf29d21217b24b6a83ec51e1514f37b2ae5d8718cf6c5df80f4fb2c +updated: 2017-04-21T16:09:28.762932633-04:00 imports: - name: github.com/bgentry/speakeasy version: 675b82c74c0ed12283ee81ba8a534c8982c07b85 @@ -51,9 +51,9 @@ imports: - name: github.com/magiconair/properties version: 51463bfca2576e06c62a8504b5c0f06d61312647 - name: github.com/mattn/go-colorable - version: a392f450ea64cee2b268dfaacdc2502b50a22b18 + version: d228849504861217f796da67fae4f6e347643f15 - name: github.com/mattn/go-isatty - version: 57fdcb988a5c543893cc61bce354a6e24ab70022 + version: 30a891c33c7cde7b02a981314b4228ec99380cca - name: github.com/mitchellh/mapstructure version: 53818660ed4955e899c0bcafa97299a388bd7c8e - name: github.com/pelletier/go-buffruneio @@ -81,12 +81,8 @@ imports: subpackages: - edwards25519 - extra25519 -- name: github.com/tendermint/go-data - version: e7fcc6d081ec8518912fcdc103188275f83a3ee5 - subpackages: - - base58 - name: github.com/tendermint/go-wire - version: 9127836cbb6dd99e020cb840a0cedcedc4671468 + version: 334005c236d19c632fb5f073f9de3b0fab6a522b subpackages: - data - data/base58 @@ -95,12 +91,12 @@ imports: subpackages: - term - name: github.com/tendermint/tmlibs - version: 2f8551d3b614dd0c0c6c114c42ab25901cc41a52 + version: 1ea866fd691ded9c1de16408934927d133550efc subpackages: - common - logger - name: golang.org/x/crypto - version: 728b753d0135da6801d45a38e6f43ff55779c5c2 + version: 7c6cc321c680f03b9ef0764448e780704f486b51 subpackages: - bcrypt - blowfish @@ -112,7 +108,7 @@ imports: - ripemd160 - salsa20/salsa - name: golang.org/x/sys - version: 99f16d856c9836c42d24e7ab64ea72916925fa97 + version: d75a52659825e75fff6158388dddc6a5b04f9ba5 subpackages: - unix - name: golang.org/x/text diff --git a/glide.yaml b/glide.yaml index 2566feef9..58e3aecca 100644 --- a/glide.yaml +++ b/glide.yaml @@ -10,9 +10,9 @@ import: subpackages: - extra25519 - package: github.com/tendermint/tmlibs - version: unstable + version: develop - package: github.com/tendermint/go-wire - version: unstable + version: develop subpackages: - data - data/base58 From 197a2b270fd94ee03824b158e738fce62862d0b8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 27 Apr 2017 20:59:48 +0200 Subject: [PATCH 33/45] Consolidate keys.Manager interface --- keys/transactions.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/keys/transactions.go b/keys/transactions.go index aafb77873..13d37feb5 100644 --- a/keys/transactions.go +++ b/keys/transactions.go @@ -9,8 +9,8 @@ import ( // Info is the public information about a key type Info struct { - Name string `json:"name"` - Address data.Bytes `json:"address"` + Name string `json:"name"` + Address data.Bytes `json:"address"` PubKey crypto.PubKey `json:"pubkey"` } @@ -62,6 +62,7 @@ type Signer interface { // Manager allows simple CRUD on a keystore, as an aid to signing type Manager interface { + Signer Create(name, passphrase, algo string) (Info, error) List() (Infos, error) Get(name string) (Info, error) From 524ba917a3a1636f21ab2c0bf76b6526903ab879 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 2 May 2017 17:18:08 +0200 Subject: [PATCH 34/45] Use new PrepareMainCmd from tmlibs/cli --- cmd/keys/main.go | 3 ++- glide.lock | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/keys/main.go b/cmd/keys/main.go index 8dd8d6505..0b0d93dec 100644 --- a/cmd/keys/main.go +++ b/cmd/keys/main.go @@ -18,10 +18,11 @@ import ( "os" "github.com/tendermint/go-crypto/cmd" + "github.com/tendermint/tmlibs/cli" ) func main() { - cmd.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc")) + cli.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc")) cmd.RootCmd.Execute() // exec() } diff --git a/glide.lock b/glide.lock index dd8ec1952..485e75c76 100644 --- a/glide.lock +++ b/glide.lock @@ -91,10 +91,11 @@ imports: subpackages: - term - name: github.com/tendermint/tmlibs - version: 1ea866fd691ded9c1de16408934927d133550efc + version: d4ab9679d71c8fc174284696d15930cb799fa24f subpackages: - common - logger + - cli - name: golang.org/x/crypto version: 7c6cc321c680f03b9ef0764448e780704f486b51 subpackages: From e71bbb2509b586f0b24f120b6ba57f32aefa1579 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 5 May 2017 19:25:44 +0200 Subject: [PATCH 35/45] Update to make full use of new tmlibs/cli helpers --- cmd/common.go | 120 ----------------------------------------------- cmd/get.go | 15 +++--- cmd/keys/main.go | 5 +- cmd/list.go | 16 ++----- cmd/new.go | 20 ++++---- cmd/root.go | 3 +- cmd/update.go | 21 ++++----- cmd/utils.go | 5 +- glide.lock | 39 ++++++++------- 9 files changed, 55 insertions(+), 189 deletions(-) delete mode 100644 cmd/common.go diff --git a/cmd/common.go b/cmd/common.go deleted file mode 100644 index 098bb6c7d..000000000 --- a/cmd/common.go +++ /dev/null @@ -1,120 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "strings" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/viper" - data "github.com/tendermint/go-wire/data" - "github.com/tendermint/go-wire/data/base58" -) - -/******* - -TODO - -This file should move into go-common or the like as a basis for all cli tools. -It is here for experimentation of re-use between go-keys and light-client. - -*********/ - -const ( - RootFlag = "root" - OutputFlag = "output" - EncodingFlag = "encoding" -) - -func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() { - cobra.OnInitialize(func() { initEnv(envPrefix) }) - cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data") - cmd.PersistentFlags().StringP(EncodingFlag, "e", "hex", "Binary encoding (hex|b64|btc)") - cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)") - cmd.PersistentPreRunE = multiE(bindFlags, setEncoding, validateOutput, cmd.PersistentPreRunE) - return func() { execute(cmd) } -} - -// initEnv sets to use ENV variables if set. -func initEnv(prefix string) { - // env variables with TM prefix (eg. TM_ROOT) - viper.SetEnvPrefix(prefix) - viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - viper.AutomaticEnv() -} - -// execute adds all child commands to the root command sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func execute(cmd *cobra.Command) { - if err := cmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } -} - -type wrapE func(cmd *cobra.Command, args []string) error - -func multiE(fs ...wrapE) wrapE { - return func(cmd *cobra.Command, args []string) error { - for _, f := range fs { - if f != nil { - if err := f(cmd, args); err != nil { - return err - } - } - } - return nil - } -} - -func bindFlags(cmd *cobra.Command, args []string) error { - // cmd.Flags() includes flags from this command and all persistent flags from the parent - if err := viper.BindPFlags(cmd.Flags()); err != nil { - return err - } - - // rootDir is command line flag, env variable, or default $HOME/.tlc - rootDir := viper.GetString("root") - viper.SetConfigName("config") // name of config file (without extension) - viper.AddConfigPath(rootDir) // search root directory - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - // stderr, so if we redirect output to json file, this doesn't appear - // fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) - } else if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - // we ignore not found error, only parse error - // stderr, so if we redirect output to json file, this doesn't appear - fmt.Fprintf(os.Stderr, "%#v", err) - } - return nil -} - -// setEncoding reads the encoding flag -func setEncoding(cmd *cobra.Command, args []string) error { - // validate and set encoding - enc := viper.GetString("encoding") - switch enc { - case "hex": - data.Encoder = data.HexEncoder - case "b64": - data.Encoder = data.B64Encoder - case "btc": - data.Encoder = base58.BTCEncoder - default: - return errors.Errorf("Unsupported encoding: %s", enc) - } - return nil -} - -func validateOutput(cmd *cobra.Command, args []string) error { - // validate output format - output := viper.GetString(OutputFlag) - switch output { - case "text", "json": - default: - return errors.Errorf("Unsupported output format: %s", output) - } - return nil -} diff --git a/cmd/get.go b/cmd/get.go index 9b8718996..6e8c620d0 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -15,7 +15,7 @@ package cmd import ( - "fmt" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -25,20 +25,17 @@ var getCmd = &cobra.Command{ Use: "get ", Short: "Get details of one key", Long: `Return public details of one local key.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { - fmt.Println("You must provide a name for the key") - return + return errors.New("You must provide a name for the key") } name := args[0] info, err := GetKeyManager().Get(name) - if err != nil { - fmt.Println(err.Error()) - return + if err == nil { + printInfo(info) } - - printInfo(info) + return err }, } diff --git a/cmd/keys/main.go b/cmd/keys/main.go index 0b0d93dec..82b355346 100644 --- a/cmd/keys/main.go +++ b/cmd/keys/main.go @@ -22,7 +22,6 @@ import ( ) func main() { - cli.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc")) - cmd.RootCmd.Execute() - // exec() + root := cli.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc")) + root.Execute() } diff --git a/cmd/list.go b/cmd/list.go index 875520159..b0419a1e4 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -14,11 +14,7 @@ package cmd -import ( - "fmt" - - "github.com/spf13/cobra" -) +import "github.com/spf13/cobra" // listCmd represents the list command var listCmd = &cobra.Command{ @@ -26,14 +22,12 @@ var listCmd = &cobra.Command{ Short: "List all keys", Long: `Return a list of all public keys stored by this key manager along with their associated name and address.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { infos, err := GetKeyManager().List() - if err != nil { - fmt.Println(err.Error()) - return + if err == nil { + printInfos(infos) } - - printInfos(infos) + return err }, } diff --git a/cmd/new.go b/cmd/new.go index b59874bb0..033410e6a 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -15,7 +15,7 @@ package cmd import ( - "fmt" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -28,7 +28,7 @@ var newCmd = &cobra.Command{ 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.`, - Run: newPassword, + RunE: newPassword, } func init() { @@ -36,25 +36,21 @@ func init() { newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1)") } -func newPassword(cmd *cobra.Command, args []string) { +func newPassword(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { - fmt.Println("You must provide a name for the key") - return + 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 { - fmt.Println(err.Error()) - return + return err } info, err := GetKeyManager().Create(name, pass, algo) - if err != nil { - fmt.Println(err.Error()) - return + if err == nil { + printInfo(info) } - - printInfo(info) + return err } diff --git a/cmd/root.go b/cmd/root.go index 401e7ec38..33153d850 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -22,6 +22,7 @@ import ( keys "github.com/tendermint/go-crypto/keys" "github.com/tendermint/go-crypto/keys/cryptostore" "github.com/tendermint/go-crypto/keys/storage/filestorage" + "github.com/tendermint/tmlibs/cli" ) const KeySubdir = "keys" @@ -45,7 +46,7 @@ needs to sign with a private key.`, func GetKeyManager() keys.Manager { if manager == nil { // store the keys directory - rootDir := viper.GetString("root") + rootDir := viper.GetString(cli.HomeFlag) keyDir := filepath.Join(rootDir, KeySubdir) // and construct the key manager manager = cryptostore.New( diff --git a/cmd/update.go b/cmd/update.go index 3835242c5..c046af126 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -17,6 +17,8 @@ package cmd import ( "fmt" + "github.com/pkg/errors" + "github.com/spf13/cobra" ) @@ -25,35 +27,32 @@ var updateCmd = &cobra.Command{ Use: "update ", Short: "Change the password for a private key", Long: `Change the password for a private key.`, - Run: updatePassword, + RunE: updatePassword, } func init() { RootCmd.AddCommand(updateCmd) } -func updatePassword(cmd *cobra.Command, args []string) { +func updatePassword(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { - fmt.Println("You must provide a name for the key") - return + return errors.New("You must provide a name for the key") } name := args[0] oldpass, err := getPassword("Enter the current passphrase:") if err != nil { - fmt.Println(err.Error()) - return + return err } newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") if err != nil { - fmt.Println(err.Error()) - return + return err } err = GetKeyManager().Update(name, oldpass, newpass) if err != nil { - fmt.Println(err.Error()) - } else { - fmt.Println("Password successfully updated!") + return err } + fmt.Println("Password successfully updated!") + return nil } diff --git a/cmd/utils.go b/cmd/utils.go index a75262676..f2faa892b 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/viper" keys "github.com/tendermint/go-crypto/keys" data "github.com/tendermint/go-wire/data" + "github.com/tendermint/tmlibs/cli" ) const PassLength = 10 @@ -40,7 +41,7 @@ func getCheckPassword(prompt, prompt2 string) (string, error) { } func printInfo(info keys.Info) { - switch viper.Get(OutputFlag) { + switch viper.Get(cli.OutputFlag) { case "text": addr, err := data.ToText(info.Address) if err != nil { @@ -61,7 +62,7 @@ func printInfo(info keys.Info) { } func printInfos(infos keys.Infos) { - switch viper.Get(OutputFlag) { + switch viper.Get(cli.OutputFlag) { case "text": fmt.Println("All keys:") for _, i := range infos { diff --git a/glide.lock b/glide.lock index 485e75c76..7b3087993 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,10 @@ hash: 3bcee9fbccf29d21217b24b6a83ec51e1514f37b2ae5d8718cf6c5df80f4fb2c -updated: 2017-04-21T16:09:28.762932633-04:00 +updated: 2017-05-05T19:07:08.418096598+02:00 imports: - name: github.com/bgentry/speakeasy version: 675b82c74c0ed12283ee81ba8a534c8982c07b85 - name: github.com/btcsuite/btcd - version: 583684b21bfbde9b5fc4403916fd7c807feb0289 + version: 4b348c1d33373d672edd83fc576892d0e46686d2 subpackages: - btcec - chaincfg @@ -28,7 +28,7 @@ imports: - name: github.com/go-playground/universal-translator version: b32fa301c9fe55953584134cb6853a13c87ec0a1 - name: github.com/go-stack/stack - version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 + version: 7a2f19628aabfe68f0766b59e74d6315f8347d22 - name: github.com/gorilla/context version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 - name: github.com/gorilla/handlers @@ -36,7 +36,7 @@ imports: - name: github.com/gorilla/mux version: 392c28fe23e1c45ddba891b0320b3b5df220beea - name: github.com/hashicorp/hcl - version: 630949a3c5fa3c613328e1b8256052cbc2327c9b + version: a4b07c25de5ff55ad3b8936cea69a79a3d95a855 subpackages: - hcl/ast - hcl/parser @@ -51,17 +51,17 @@ imports: - name: github.com/magiconair/properties version: 51463bfca2576e06c62a8504b5c0f06d61312647 - name: github.com/mattn/go-colorable - version: d228849504861217f796da67fae4f6e347643f15 + version: ded68f7a9561c023e790de24279db7ebf473ea80 - name: github.com/mattn/go-isatty - version: 30a891c33c7cde7b02a981314b4228ec99380cca + version: fc9e8d8ef48496124e79ae0df75490096eccf6fe - name: github.com/mitchellh/mapstructure - version: 53818660ed4955e899c0bcafa97299a388bd7c8e + version: cc8532a8e9a55ea36402aa21efdf403a60d34096 - name: github.com/pelletier/go-buffruneio version: c37440a7cf42ac63b919c752ca73a85067e05992 - name: github.com/pelletier/go-toml - version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a + version: 97253b98df84f9eef872866d079e74b8265150f1 - name: github.com/pkg/errors - version: bfd5150e4e41705ded2129ec33379de1cb90b513 + version: c605e284fe17294bda444b34710735b29d1a9d90 - name: github.com/spf13/afero version: 9be650865eab0c12963d8753212f4f9c66cdcf12 subpackages: @@ -69,35 +69,34 @@ imports: - name: github.com/spf13/cast version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 - name: github.com/spf13/cobra - version: fcd0c5a1df88f5d6784cb4feead962c3f3d0b66c + version: 8f0203be891287870100e4af46262cdf4a4261d1 - name: github.com/spf13/jwalterweatherman version: fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66 - name: github.com/spf13/pflag - version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 + version: c990990ab4981d84da820b7b00c85139ca150b5f - name: github.com/spf13/viper - version: 5d46e70da8c0b6f812e0b170b7a985753b5c63cb + version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2 - name: github.com/tendermint/ed25519 version: 1f52c6f8b8a5c7908aff4497c186af344b428925 subpackages: - edwards25519 - extra25519 - name: github.com/tendermint/go-wire - version: 334005c236d19c632fb5f073f9de3b0fab6a522b + version: b53add0b622662731985485f3a19be7f684660b8 subpackages: - data - data/base58 - name: github.com/tendermint/log15 - version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 + version: f91285dece9f4875421b481da3e613d83d44f29b subpackages: - term - name: github.com/tendermint/tmlibs - version: d4ab9679d71c8fc174284696d15930cb799fa24f + version: 706b9fbd671d5d49ecf1b2ea3bb34e51d61ff091 subpackages: - - common - - logger - cli + - common - name: golang.org/x/crypto - version: 7c6cc321c680f03b9ef0764448e780704f486b51 + version: 5a033cc77e57eca05bdb50522851d29e03569cbe subpackages: - bcrypt - blowfish @@ -109,11 +108,11 @@ imports: - ripemd160 - salsa20/salsa - name: golang.org/x/sys - version: d75a52659825e75fff6158388dddc6a5b04f9ba5 + version: 9ccfe848b9db8435a24c424abbc07a921adf1df5 subpackages: - unix - name: golang.org/x/text - version: f4b4367115ec2de254587813edaa901bc1c723a8 + version: 470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4 subpackages: - transform - unicode/norm From d901fba6628e15b83d564753b3bd3cf2c5438686 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 15 May 2017 09:40:00 -0400 Subject: [PATCH 36/45] add changelog and version --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ version.go | 3 +++ 2 files changed, 30 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 version.go diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..72c4bf2cd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog + +## 0.2.0 (May 15, 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 + diff --git a/version.go b/version.go new file mode 100644 index 000000000..e2c9c787b --- /dev/null +++ b/version.go @@ -0,0 +1,3 @@ +package crypto + +const Version = "0.2.0" From a42b10e0feb465eb56fbc6bb5b71d57ef646ec57 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 15 May 2017 09:41:07 -0400 Subject: [PATCH 37/45] update glide --- glide.lock | 62 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/glide.lock b/glide.lock index 7b3087993..1f026ceb9 100644 --- a/glide.lock +++ b/glide.lock @@ -1,34 +1,42 @@ hash: 3bcee9fbccf29d21217b24b6a83ec51e1514f37b2ae5d8718cf6c5df80f4fb2c -updated: 2017-05-05T19:07:08.418096598+02:00 +updated: 2017-05-15T09:40:53.073691731-04:00 imports: - name: github.com/bgentry/speakeasy - version: 675b82c74c0ed12283ee81ba8a534c8982c07b85 + version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd - name: github.com/btcsuite/btcd - version: 4b348c1d33373d672edd83fc576892d0e46686d2 + version: b8df516b4b267acf2de46be593a9d948d1d2c420 subpackages: - btcec - chaincfg - chaincfg/chainhash - wire - name: github.com/btcsuite/btcutil - version: a5ecb5d9547afe8d1672073dbdc348203de744a0 + version: 66871daeb12123ece012a9628d2798d01195c4b3 subpackages: - base58 - hdkeychain -- name: github.com/btcsuite/golangcrypto - version: 53f62d9b43e87a6c56975cf862af7edf33a8d0df - subpackages: - - ripemd160 +- name: github.com/btcsuite/fastsha256 + version: 637e656429416087660c84436a2a035d69d54e2e +- name: github.com/clipperhouse/typewriter + version: c1a48da378ebb7db1db9f35981b5cc24bf2e5b85 - name: github.com/fsnotify/fsnotify version: 4da3e2cfbabc9f751898f250b49f2439785783a1 +- name: github.com/go-kit/kit + version: d67bb4c202e3b91377d1079b110a6c9ce23ab2f8 + subpackages: + - log + - log/level + - log/term +- name: github.com/go-logfmt/logfmt + version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 - name: github.com/go-playground/locales - version: 084b0226cf88d891a2bdeccac01d592af13a8f7b + version: 1e5f1161c6416a5ff48840eb8724a394e48cc534 subpackages: - currency - name: github.com/go-playground/universal-translator - version: b32fa301c9fe55953584134cb6853a13c87ec0a1 + version: 71201497bace774495daed26a3874fd339e0b538 - name: github.com/go-stack/stack - version: 7a2f19628aabfe68f0766b59e74d6315f8347d22 + version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 - name: github.com/gorilla/context version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 - name: github.com/gorilla/handlers @@ -48,20 +56,18 @@ imports: - json/token - name: github.com/inconshreveable/mousetrap version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/kr/logfmt + version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 - name: github.com/magiconair/properties version: 51463bfca2576e06c62a8504b5c0f06d61312647 -- name: github.com/mattn/go-colorable - version: ded68f7a9561c023e790de24279db7ebf473ea80 -- name: github.com/mattn/go-isatty - version: fc9e8d8ef48496124e79ae0df75490096eccf6fe - name: github.com/mitchellh/mapstructure version: cc8532a8e9a55ea36402aa21efdf403a60d34096 - name: github.com/pelletier/go-buffruneio version: c37440a7cf42ac63b919c752ca73a85067e05992 - name: github.com/pelletier/go-toml - version: 97253b98df84f9eef872866d079e74b8265150f1 + version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a - name: github.com/pkg/errors - version: c605e284fe17294bda444b34710735b29d1a9d90 + version: ff09b135c25aae272398c51a07235b90a75aa4f0 - name: github.com/spf13/afero version: 9be650865eab0c12963d8753212f4f9c66cdcf12 subpackages: @@ -69,11 +75,11 @@ imports: - name: github.com/spf13/cast version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 - name: github.com/spf13/cobra - version: 8f0203be891287870100e4af46262cdf4a4261d1 + version: db6b9a8b3f3f400c8ecb4a4d7d02245b8facad66 - name: github.com/spf13/jwalterweatherman version: fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66 - name: github.com/spf13/pflag - version: c990990ab4981d84da820b7b00c85139ca150b5f + version: 80fe0fb4eba54167e2ccae1c6c950e72abf61b73 - name: github.com/spf13/viper version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2 - name: github.com/tendermint/ed25519 @@ -82,21 +88,18 @@ imports: - edwards25519 - extra25519 - name: github.com/tendermint/go-wire - version: b53add0b622662731985485f3a19be7f684660b8 + version: 59ba5af720a8cbaf5594595e1d8aae23bfd18e31 subpackages: - data - data/base58 -- name: github.com/tendermint/log15 - version: f91285dece9f4875421b481da3e613d83d44f29b - subpackages: - - term - name: github.com/tendermint/tmlibs - version: 706b9fbd671d5d49ecf1b2ea3bb34e51d61ff091 + version: 8f5a175ff4c869fedde710615a11f5745ff69bf3 subpackages: - cli - common + - log - name: golang.org/x/crypto - version: 5a033cc77e57eca05bdb50522851d29e03569cbe + version: c7af5bf2638a1164f2eb5467c39c6cffbd13a02e subpackages: - bcrypt - blowfish @@ -116,8 +119,13 @@ imports: subpackages: - transform - unicode/norm +- name: golang.org/x/tools + version: 144c6642b5d832d6c44a53dad6ee61665dd432ce + subpackages: + - go/ast/astutil + - imports - name: gopkg.in/go-playground/validator.v9 - version: 4bd19358521c53f09639f21e2a9d6883d6890f24 + version: 6d8c18553ea1ac493d049edd6f102f52e618f085 - name: gopkg.in/yaml.v2 version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b testImports: From c38a6f55b375c3f213e8acbd66f399d8a7b8ea70 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 15 May 2017 02:22:58 +0200 Subject: [PATCH 38/45] Prepare for codegen --- _gen.go | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 _gen.go diff --git a/_gen.go b/_gen.go new file mode 100644 index 000000000..36e39887f --- /dev/null +++ b/_gen.go @@ -0,0 +1,7 @@ +package main + +import ( + _ "github.com/tendermint/go-wire/data" + _ "github.com/clipperhouse/stringer" + _ "github.com/clipperhouse/set" +) From f16f71199218d8c0e7e9ef0284c361a51b258afd Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 15 May 2017 02:31:05 +0200 Subject: [PATCH 39/45] First code from codegen... wrong names --- pub_key.go | 47 +++----------------------------- pubkeyinner_holder.go | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 43 deletions(-) create mode 100644 pubkeyinner_holder.go diff --git a/pub_key.go b/pub_key.go index 99839f288..a4da59d2f 100644 --- a/pub_key.go +++ b/pub_key.go @@ -7,9 +7,9 @@ import ( secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" - . "github.com/tendermint/tmlibs/common" - data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-wire" + data "github.com/tendermint/go-wire/data" + . "github.com/tendermint/tmlibs/common" "golang.org/x/crypto/ripemd160" ) @@ -20,12 +20,10 @@ func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { //---------------------------------------- -type PubKey struct { - PubKeyInner `json:"unwrap"` -} - // DO NOT USE THIS INTERFACE. // You probably want to use PubKey + +// +gen holder:"PubKey,Impl[PubKeyEd25519,PubKeySecp256k1]" type PubKeyInner interface { AssertIsPubKeyInner() Address() []byte @@ -36,35 +34,6 @@ type PubKeyInner interface { Wrap() PubKey } -func (pk PubKey) MarshalJSON() ([]byte, error) { - return pubKeyMapper.ToJSON(pk.PubKeyInner) -} - -func (pk *PubKey) UnmarshalJSON(data []byte) (err error) { - parsed, err := pubKeyMapper.FromJSON(data) - if err == nil && parsed != nil { - pk.PubKeyInner = parsed.(PubKeyInner) - } - return -} - -// Unwrap recovers the concrete interface safely (regardless of levels of embeds) -func (pk PubKey) Unwrap() PubKeyInner { - pkI := pk.PubKeyInner - for wrap, ok := pkI.(PubKey); ok; wrap, ok = pkI.(PubKey) { - pkI = wrap.PubKeyInner - } - return pkI -} - -func (p PubKey) Empty() bool { - return p.PubKeyInner == nil -} - -var pubKeyMapper = data.NewMapper(PubKey{}). - RegisterImplementation(PubKeyEd25519{}, NameEd25519, TypeEd25519). - RegisterImplementation(PubKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) - //------------------------------------- var _ PubKeyInner = PubKeyEd25519{} @@ -142,10 +111,6 @@ func (pubKey PubKeyEd25519) Equals(other PubKey) bool { } } -func (pubKey PubKeyEd25519) Wrap() PubKey { - return PubKey{pubKey} -} - //------------------------------------- var _ PubKeyInner = PubKeySecp256k1{} @@ -218,7 +183,3 @@ func (pubKey PubKeySecp256k1) Equals(other PubKey) bool { return false } } - -func (pubKey PubKeySecp256k1) Wrap() PubKey { - return PubKey{pubKey} -} diff --git a/pubkeyinner_holder.go b/pubkeyinner_holder.go new file mode 100644 index 000000000..16bfcf8a8 --- /dev/null +++ b/pubkeyinner_holder.go @@ -0,0 +1,62 @@ +// Generated by: main +// TypeWriter: holder +// Directive: +gen on PubKeyInner + +package crypto + +import ( + "github.com/tendermint/go-wire/data" +) + +// Auto-generated adapters for happily unmarshaling interfaces +// Apache License 2.0 +// Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com) + +type PubKey struct { + PubKeyInner +} + +var PubKeyMapper = data.NewMapper(PubKey{}) + +func (h PubKey) MarshalJSON() ([]byte, error) { + return PubKeyMapper.ToJSON(h.PubKeyInner) +} + +func (h *PubKey) UnmarshalJSON(data []byte) (err error) { + parsed, err := PubKeyMapper.FromJSON(data) + if err == nil && parsed != nil { + h.PubKeyInner = parsed.(PubKeyInner) + } + return err +} + +// Unwrap recovers the concrete interface safely (regardless of levels of embeds) +func (h PubKey) Unwrap() PubKeyInner { + hi := h.PubKeyInner + for wrap, ok := hi.(PubKey); ok; wrap, ok = hi.(PubKey) { + hi = wrap.PubKeyInner + } + return hi +} + +func (h PubKey) Empty() bool { + return h.PubKeyInner == nil +} + +/*** below are bindings for each implementation ***/ + +func init() { + PubKeyMapper.RegisterImplementation(PubKeyEd25519{}, "pubkeyed25519", 0x1) +} + +func (hi PubKeyEd25519) Wrap() PubKey { + return PubKey{hi} +} + +func init() { + PubKeyMapper.RegisterImplementation(PubKeySecp256k1{}, "pubkeysecp256k1", 0x2) +} + +func (hi PubKeySecp256k1) Wrap() PubKey { + return PubKey{hi} +} From ee200d998fb42e23462569745c53c800fbdc6891 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 15 May 2017 02:46:28 +0200 Subject: [PATCH 40/45] Fix unwrap for proper json format --- encode_test.go | 4 ++-- pub_key.go | 3 +-- pubkeyinner_holder.go | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/encode_test.go b/encode_test.go index 6c5d03a1d..1b70d88ec 100644 --- a/encode_test.go +++ b/encode_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - data "github.com/tendermint/go-wire/data" wire "github.com/tendermint/go-wire" + data "github.com/tendermint/go-wire/data" ) type byter interface { @@ -58,7 +58,7 @@ func checkWireJSON(t *testing.T, in interface{}, reader interface{}, typ byte) { var err error js := wire.JSONBytes(in) btyp := fmt.Sprintf("[%d,", typ) - assert.True(t, strings.HasPrefix(string(js), btyp), string(js)) + assert.True(t, strings.HasPrefix(string(js), btyp), string(js), btyp) wire.ReadJSON(reader, js, &err) require.Nil(t, err, "%+v", err) diff --git a/pub_key.go b/pub_key.go index a4da59d2f..7483ff93a 100644 --- a/pub_key.go +++ b/pub_key.go @@ -22,8 +22,7 @@ func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { // DO NOT USE THIS INTERFACE. // You probably want to use PubKey - -// +gen holder:"PubKey,Impl[PubKeyEd25519,PubKeySecp256k1]" +// +gen holder:"PubKey,Impl[PubKeyEd25519,PubKeySecp256k1],ed25519,secp256k1" type PubKeyInner interface { AssertIsPubKeyInner() Address() []byte diff --git a/pubkeyinner_holder.go b/pubkeyinner_holder.go index 16bfcf8a8..ee0ca9f00 100644 --- a/pubkeyinner_holder.go +++ b/pubkeyinner_holder.go @@ -13,7 +13,7 @@ import ( // Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com) type PubKey struct { - PubKeyInner + PubKeyInner "json:\"unwrap\"" } var PubKeyMapper = data.NewMapper(PubKey{}) @@ -46,7 +46,7 @@ func (h PubKey) Empty() bool { /*** below are bindings for each implementation ***/ func init() { - PubKeyMapper.RegisterImplementation(PubKeyEd25519{}, "pubkeyed25519", 0x1) + PubKeyMapper.RegisterImplementation(PubKeyEd25519{}, "ed25519", 0x1) } func (hi PubKeyEd25519) Wrap() PubKey { @@ -54,7 +54,7 @@ func (hi PubKeyEd25519) Wrap() PubKey { } func init() { - PubKeyMapper.RegisterImplementation(PubKeySecp256k1{}, "pubkeysecp256k1", 0x2) + PubKeyMapper.RegisterImplementation(PubKeySecp256k1{}, "secp256k1", 0x2) } func (hi PubKeySecp256k1) Wrap() PubKey { From 746a2e286df37747cc00d60bd326f1c21e8aef02 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 15 May 2017 02:51:52 +0200 Subject: [PATCH 41/45] Codegen wrappers for privkey and signature as well --- priv_key.go | 46 ++--------------------------- privkeyinner_holder.go | 62 ++++++++++++++++++++++++++++++++++++++++ signature.go | 47 +++--------------------------- signatureinner_holder.go | 62 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 86 deletions(-) create mode 100644 privkeyinner_holder.go create mode 100644 signatureinner_holder.go diff --git a/priv_key.go b/priv_key.go index 85f8a8ec4..056e50b55 100644 --- a/priv_key.go +++ b/priv_key.go @@ -6,9 +6,9 @@ import ( secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" - . "github.com/tendermint/tmlibs/common" - data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-wire" + data "github.com/tendermint/go-wire/data" + . "github.com/tendermint/tmlibs/common" ) func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { @@ -18,12 +18,9 @@ func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { //---------------------------------------- -type PrivKey struct { - PrivKeyInner `json:"unwrap"` -} - // DO NOT USE THIS INTERFACE. // You probably want to use PubKey +// +gen holder:"PrivKey,Impl[PrivKeyEd25519,PrivKeySecp256k1],ed25519,secp256k1" type PrivKeyInner interface { AssertIsPrivKeyInner() Bytes() []byte @@ -33,35 +30,6 @@ type PrivKeyInner interface { Wrap() PrivKey } -func (p PrivKey) MarshalJSON() ([]byte, error) { - return privKeyMapper.ToJSON(p.PrivKeyInner) -} - -func (p *PrivKey) UnmarshalJSON(data []byte) (err error) { - parsed, err := privKeyMapper.FromJSON(data) - if err == nil && parsed != nil { - p.PrivKeyInner = parsed.(PrivKeyInner) - } - return -} - -// Unwrap recovers the concrete interface safely (regardless of levels of embeds) -func (p PrivKey) Unwrap() PrivKeyInner { - pk := p.PrivKeyInner - for wrap, ok := pk.(PrivKey); ok; wrap, ok = pk.(PrivKey) { - pk = wrap.PrivKeyInner - } - return pk -} - -func (p PrivKey) Empty() bool { - return p.PrivKeyInner == nil -} - -var privKeyMapper = data.NewMapper(PrivKey{}). - RegisterImplementation(PrivKeyEd25519{}, NameEd25519, TypeEd25519). - RegisterImplementation(PrivKeySecp256k1{}, NameSecp256k1, TypeSecp256k1) - //------------------------------------- var _ PrivKeyInner = PrivKeyEd25519{} @@ -128,10 +96,6 @@ func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { return PrivKeyEd25519(newKey) } -func (privKey PrivKeyEd25519) Wrap() PrivKey { - return PrivKey{privKey} -} - func GenPrivKeyEd25519() PrivKeyEd25519 { privKeyBytes := new([64]byte) copy(privKeyBytes[:32], CRandBytes(32)) @@ -201,10 +165,6 @@ func (privKey PrivKeySecp256k1) String() string { return Fmt("PrivKeySecp256k1{*****}") } -func (privKey PrivKeySecp256k1) Wrap() PrivKey { - return PrivKey{privKey} -} - /* // Deterministically generates new priv-key bytes from key. func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 { diff --git a/privkeyinner_holder.go b/privkeyinner_holder.go new file mode 100644 index 000000000..2621ca560 --- /dev/null +++ b/privkeyinner_holder.go @@ -0,0 +1,62 @@ +// Generated by: main +// TypeWriter: holder +// Directive: +gen on PrivKeyInner + +package crypto + +import ( + "github.com/tendermint/go-wire/data" +) + +// Auto-generated adapters for happily unmarshaling interfaces +// Apache License 2.0 +// Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com) + +type PrivKey struct { + PrivKeyInner "json:\"unwrap\"" +} + +var PrivKeyMapper = data.NewMapper(PrivKey{}) + +func (h PrivKey) MarshalJSON() ([]byte, error) { + return PrivKeyMapper.ToJSON(h.PrivKeyInner) +} + +func (h *PrivKey) UnmarshalJSON(data []byte) (err error) { + parsed, err := PrivKeyMapper.FromJSON(data) + if err == nil && parsed != nil { + h.PrivKeyInner = parsed.(PrivKeyInner) + } + return err +} + +// Unwrap recovers the concrete interface safely (regardless of levels of embeds) +func (h PrivKey) Unwrap() PrivKeyInner { + hi := h.PrivKeyInner + for wrap, ok := hi.(PrivKey); ok; wrap, ok = hi.(PrivKey) { + hi = wrap.PrivKeyInner + } + return hi +} + +func (h PrivKey) Empty() bool { + return h.PrivKeyInner == nil +} + +/*** below are bindings for each implementation ***/ + +func init() { + PrivKeyMapper.RegisterImplementation(PrivKeyEd25519{}, "ed25519", 0x1) +} + +func (hi PrivKeyEd25519) Wrap() PrivKey { + return PrivKey{hi} +} + +func init() { + PrivKeyMapper.RegisterImplementation(PrivKeySecp256k1{}, "secp256k1", 0x2) +} + +func (hi PrivKeySecp256k1) Wrap() PrivKey { + return PrivKey{hi} +} diff --git a/signature.go b/signature.go index 36451ed48..12b584d71 100644 --- a/signature.go +++ b/signature.go @@ -4,9 +4,9 @@ import ( "bytes" "fmt" - . "github.com/tendermint/tmlibs/common" - data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-wire" + data "github.com/tendermint/go-wire/data" + . "github.com/tendermint/tmlibs/common" ) func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { @@ -16,12 +16,9 @@ func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { //---------------------------------------- -type Signature struct { - SignatureInner `json:"unwrap"` -} - // DO NOT USE THIS INTERFACE. // You probably want to use Signature. +// +gen holder:"Signature,Impl[SignatureEd25519,SignatureSecp256k1],ed25519,secp256k1" type SignatureInner interface { AssertIsSignatureInner() Bytes() []byte @@ -31,35 +28,6 @@ type SignatureInner interface { Wrap() Signature } -func (sig Signature) MarshalJSON() ([]byte, error) { - return sigMapper.ToJSON(sig.SignatureInner) -} - -func (sig *Signature) UnmarshalJSON(data []byte) (err error) { - parsed, err := sigMapper.FromJSON(data) - if err == nil && parsed != nil { - sig.SignatureInner = parsed.(SignatureInner) - } - return -} - -// Unwrap recovers the concrete interface safely (regardless of levels of embeds) -func (sig Signature) Unwrap() SignatureInner { - pk := sig.SignatureInner - for wrap, ok := pk.(Signature); ok; wrap, ok = pk.(Signature) { - pk = wrap.SignatureInner - } - return pk -} - -func (sig Signature) Empty() bool { - return sig.SignatureInner == nil -} - -var sigMapper = data.NewMapper(Signature{}). - RegisterImplementation(SignatureEd25519{}, NameEd25519, TypeEd25519). - RegisterImplementation(SignatureSecp256k1{}, NameSecp256k1, TypeSecp256k1) - //------------------------------------- var _ SignatureInner = SignatureEd25519{} @@ -96,10 +64,6 @@ func (sig *SignatureEd25519) UnmarshalJSON(enc []byte) error { return err } -func (sig SignatureEd25519) Wrap() Signature { - return Signature{sig} -} - //------------------------------------- var _ SignatureInner = SignatureSecp256k1{} @@ -124,6 +88,7 @@ func (sig SignatureSecp256k1) Equals(other Signature) bool { return false } } + func (sig SignatureSecp256k1) MarshalJSON() ([]byte, error) { return data.Encoder.Marshal(sig) } @@ -131,7 +96,3 @@ func (sig SignatureSecp256k1) MarshalJSON() ([]byte, error) { func (sig *SignatureSecp256k1) UnmarshalJSON(enc []byte) error { return data.Encoder.Unmarshal((*[]byte)(sig), enc) } - -func (sig SignatureSecp256k1) Wrap() Signature { - return Signature{sig} -} diff --git a/signatureinner_holder.go b/signatureinner_holder.go new file mode 100644 index 000000000..cbe1fe093 --- /dev/null +++ b/signatureinner_holder.go @@ -0,0 +1,62 @@ +// Generated by: main +// TypeWriter: holder +// Directive: +gen on SignatureInner + +package crypto + +import ( + "github.com/tendermint/go-wire/data" +) + +// Auto-generated adapters for happily unmarshaling interfaces +// Apache License 2.0 +// Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com) + +type Signature struct { + SignatureInner "json:\"unwrap\"" +} + +var SignatureMapper = data.NewMapper(Signature{}) + +func (h Signature) MarshalJSON() ([]byte, error) { + return SignatureMapper.ToJSON(h.SignatureInner) +} + +func (h *Signature) UnmarshalJSON(data []byte) (err error) { + parsed, err := SignatureMapper.FromJSON(data) + if err == nil && parsed != nil { + h.SignatureInner = parsed.(SignatureInner) + } + return err +} + +// Unwrap recovers the concrete interface safely (regardless of levels of embeds) +func (h Signature) Unwrap() SignatureInner { + hi := h.SignatureInner + for wrap, ok := hi.(Signature); ok; wrap, ok = hi.(Signature) { + hi = wrap.SignatureInner + } + return hi +} + +func (h Signature) Empty() bool { + return h.SignatureInner == nil +} + +/*** below are bindings for each implementation ***/ + +func init() { + SignatureMapper.RegisterImplementation(SignatureEd25519{}, "ed25519", 0x1) +} + +func (hi SignatureEd25519) Wrap() Signature { + return Signature{hi} +} + +func init() { + SignatureMapper.RegisterImplementation(SignatureSecp256k1{}, "secp256k1", 0x2) +} + +func (hi SignatureSecp256k1) Wrap() Signature { + return Signature{hi} +} From 79c580492e6b8658605c8ceea5f6bc52739d0339 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 15 May 2017 15:56:08 +0200 Subject: [PATCH 42/45] add make codegen for gen --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 59440fe66..d453f7ad1 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONEY: all docs test install get_vendor_deps ensure_tools +.PHONEY: all docs test install get_vendor_deps ensure_tools codegen GOTOOLS = \ github.com/Masterminds/glide @@ -24,4 +24,12 @@ get_vendor_deps: ensure_tools ensure_tools: go get $(GOTOOLS) +prepgen: install + cd ../go-wire && make tools + 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: prepgen + gen From bee63ce4ff15b8d48a0ee6fa9d902775f86fa1a1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 15 May 2017 19:13:49 +0200 Subject: [PATCH 43/45] Cleaned up build process, moved codegen to separate package in go-wire --- Makefile | 7 ++++--- _gen.go | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index d453f7ad1..ca9703399 100644 --- a/Makefile +++ b/Makefile @@ -25,11 +25,12 @@ ensure_tools: go get $(GOTOOLS) prepgen: install - cd ../go-wire && make tools 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: prepgen - gen +codegen: + @echo "--> regenerating all interface wrappers" + @gen + @echo "Done!" diff --git a/_gen.go b/_gen.go index 36e39887f..a98feaf4e 100644 --- a/_gen.go +++ b/_gen.go @@ -1,7 +1,6 @@ package main import ( - _ "github.com/tendermint/go-wire/data" + _ "github.com/tendermint/go-wire/gen" _ "github.com/clipperhouse/stringer" - _ "github.com/clipperhouse/set" ) From db5cb8d92c3e993786f406e9fcfebbbd60bd2d87 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 16 May 2017 16:59:40 +0200 Subject: [PATCH 44/45] Change codegen name holder->wrapper --- glide.lock | 2 +- priv_key.go | 2 +- privkeyinner_holder.go => privkeyinner_wrapper.go | 2 +- pub_key.go | 2 +- pubkeyinner_holder.go => pubkeyinner_wrapper.go | 2 +- signature.go | 2 +- signatureinner_holder.go => signatureinner_wrapper.go | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename privkeyinner_holder.go => privkeyinner_wrapper.go (98%) rename pubkeyinner_holder.go => pubkeyinner_wrapper.go (98%) rename signatureinner_holder.go => signatureinner_wrapper.go (98%) diff --git a/glide.lock b/glide.lock index 1f026ceb9..82dd5111e 100644 --- a/glide.lock +++ b/glide.lock @@ -88,7 +88,7 @@ imports: - edwards25519 - extra25519 - name: github.com/tendermint/go-wire - version: 59ba5af720a8cbaf5594595e1d8aae23bfd18e31 + version: 97beaedf0f4dbc035309157c92be3b30cc6e5d74 subpackages: - data - data/base58 diff --git a/priv_key.go b/priv_key.go index 056e50b55..0c6bd2ae7 100644 --- a/priv_key.go +++ b/priv_key.go @@ -20,7 +20,7 @@ func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { // DO NOT USE THIS INTERFACE. // You probably want to use PubKey -// +gen holder:"PrivKey,Impl[PrivKeyEd25519,PrivKeySecp256k1],ed25519,secp256k1" +// +gen wrapper:"PrivKey,Impl[PrivKeyEd25519,PrivKeySecp256k1],ed25519,secp256k1" type PrivKeyInner interface { AssertIsPrivKeyInner() Bytes() []byte diff --git a/privkeyinner_holder.go b/privkeyinner_wrapper.go similarity index 98% rename from privkeyinner_holder.go rename to privkeyinner_wrapper.go index 2621ca560..05ce69672 100644 --- a/privkeyinner_holder.go +++ b/privkeyinner_wrapper.go @@ -1,5 +1,5 @@ // Generated by: main -// TypeWriter: holder +// TypeWriter: wrapper // Directive: +gen on PrivKeyInner package crypto diff --git a/pub_key.go b/pub_key.go index 7483ff93a..4d5c31b21 100644 --- a/pub_key.go +++ b/pub_key.go @@ -22,7 +22,7 @@ func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { // DO NOT USE THIS INTERFACE. // You probably want to use PubKey -// +gen holder:"PubKey,Impl[PubKeyEd25519,PubKeySecp256k1],ed25519,secp256k1" +// +gen wrapper:"PubKey,Impl[PubKeyEd25519,PubKeySecp256k1],ed25519,secp256k1" type PubKeyInner interface { AssertIsPubKeyInner() Address() []byte diff --git a/pubkeyinner_holder.go b/pubkeyinner_wrapper.go similarity index 98% rename from pubkeyinner_holder.go rename to pubkeyinner_wrapper.go index ee0ca9f00..7b36c324d 100644 --- a/pubkeyinner_holder.go +++ b/pubkeyinner_wrapper.go @@ -1,5 +1,5 @@ // Generated by: main -// TypeWriter: holder +// TypeWriter: wrapper // Directive: +gen on PubKeyInner package crypto diff --git a/signature.go b/signature.go index 12b584d71..1c5b8d69e 100644 --- a/signature.go +++ b/signature.go @@ -18,7 +18,7 @@ func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { // DO NOT USE THIS INTERFACE. // You probably want to use Signature. -// +gen holder:"Signature,Impl[SignatureEd25519,SignatureSecp256k1],ed25519,secp256k1" +// +gen wrapper:"Signature,Impl[SignatureEd25519,SignatureSecp256k1],ed25519,secp256k1" type SignatureInner interface { AssertIsSignatureInner() Bytes() []byte diff --git a/signatureinner_holder.go b/signatureinner_wrapper.go similarity index 98% rename from signatureinner_holder.go rename to signatureinner_wrapper.go index cbe1fe093..1fdd790d6 100644 --- a/signatureinner_holder.go +++ b/signatureinner_wrapper.go @@ -1,5 +1,5 @@ // Generated by: main -// TypeWriter: holder +// TypeWriter: wrapper // Directive: +gen on SignatureInner package crypto From c61497b56e43096e47872ec12782d5bf9e20b4f0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 18 May 2017 11:35:32 +0200 Subject: [PATCH 45/45] CHANGELOG: update release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72c4bf2cd..510785477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.2.0 (May 15, 2017) +## 0.2.0 (May 18, 2017) BREAKING CHANGES: