Ledger integration (#931)

Merges the keybase and Ledger code from go-crypto (which is no more) into the SDK
Adds support for Ledger into gaiacli
Cherry-picks updated error handling from #1158
This commit is contained in:
Christopher Goes 2018-06-29 02:54:47 +02:00 committed by GitHub
parent ac3adff1e8
commit 59aadf42aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
143 changed files with 2699 additions and 424 deletions

View File

@ -12,6 +12,7 @@ BREAKING CHANGES
* Removed MsgChangePubKey from auth
* Removed setPubKey from account mapper
* Removed GetMemo from Tx (it is still on StdTx)
* Keybase and Ledger support from go-crypto merged into the SDK in the `crypto` folder
* Gov module REST endpoints changed to be more RESTful
* [cli] rearranged commands under subcommands
* [stake] remove Tick and add EndBlocker
@ -46,6 +47,9 @@ FEATURES
* [types] Switches internal representation of Int/Uint/Rat to use pointers
* [gaiad] unsafe_reset_all now resets addrbook.json
* [democoin] add x/oracle, x/assoc
* [gaiacli] Ledger support added
- You can now use a Ledger with `gaiacli --ledger` for all key-related commands
- Ledger keys can be named and tracked locally in the key DB
* [gaiacli] added an --async flag to the cli to deliver transactions without waiting for a tendermint response
FIXES

132
Gopkg.lock generated
View File

@ -1,12 +1,30 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/bartekn/go-bip39"
packages = ["."]
revision = "a05967ea095d81c8fe4833776774cfaff8e5036c"
[[projects]]
branch = "master"
name = "github.com/beorn7/perks"
packages = ["quantile"]
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
name = "github.com/bgentry/speakeasy"
packages = ["."]
revision = "4aabc24848ce5fd31929f7d1e4ea74d3709c14cd"
version = "v0.1.0"
[[projects]]
branch = "master"
name = "github.com/brejski/hid"
packages = ["."]
revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc"
[[projects]]
branch = "master"
name = "github.com/btcsuite/btcd"
@ -42,7 +60,11 @@
packages = [
"log",
"log/level",
"log/term"
"log/term",
"metrics",
"metrics/discard",
"metrics/internal/lv",
"metrics/prometheus"
]
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
@ -125,12 +147,6 @@
]
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
branch = "master"
name = "github.com/howeyc/crc16"
packages = ["."]
revision = "2b2a61e366a66d3efb279e46176e7291001e0354"
[[projects]]
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
@ -161,6 +177,12 @@
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
@ -185,6 +207,42 @@
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/promhttp"
]
revision = "c5b7fccd204277076155f10851dad72b76a49317"
version = "v0.8.0"
[[projects]]
branch = "master"
name = "github.com/prometheus/client_model"
packages = ["go"]
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
[[projects]]
branch = "master"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model"
]
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
[[projects]]
branch = "master"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs"
]
revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7"
[[projects]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
@ -236,8 +294,8 @@
"assert",
"require"
]
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
@ -258,18 +316,6 @@
]
revision = "0d5a0ceb10cf9ab89fdd744cc8c50a83134f6697"
[[projects]]
name = "github.com/tendermint/abci"
packages = [
"client",
"example/code",
"example/kvstore",
"server",
"types"
]
revision = "198dccf0ddfd1bb176f87657e3286a05a6ed9540"
version = "v0.12.0"
[[projects]]
branch = "master"
name = "github.com/tendermint/ed25519"
@ -283,20 +329,8 @@
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "ed62928576cfcaf887209dc96142cd79cdfff389"
version = "0.9.9"
[[projects]]
name = "github.com/tendermint/go-crypto"
packages = [
".",
"keys",
"keys/bcrypt",
"keys/words",
"keys/words/wordlist"
]
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2"
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
version = "0.10.1"
[[projects]]
name = "github.com/tendermint/iavl"
@ -304,17 +338,23 @@
".",
"sha256truncated"
]
revision = "c9206995e8f948e99927f5084a88a7e94ca256da"
version = "v0.8.0-rc0"
revision = "481b89cbbe6a641f7f6cb5db92b30b20f5a2e001"
[[projects]]
name = "github.com/tendermint/tendermint"
packages = [
"abci/client",
"abci/example/code",
"abci/example/kvstore",
"abci/server",
"abci/types",
"blockchain",
"cmd/tendermint/commands",
"config",
"consensus",
"consensus/types",
"crypto",
"crypto/tmhash",
"evidence",
"libs/events",
"libs/pubsub",
@ -347,7 +387,7 @@
"types",
"version"
]
revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601"
revision = "8412b75b1070ac023405e8228e017ed36531fe1b"
[[projects]]
name = "github.com/tendermint/tmlibs"
@ -364,7 +404,13 @@
"merkle",
"merkle/tmhash"
]
revision = "0c98d10b4ffbd87978d79c160e835b3d3df241ec"
revision = "49596e0a1f48866603813df843c9409fc19805c6"
version = "v0.9.0"
[[projects]]
name = "github.com/zondax/ledger-goclient"
packages = ["."]
revision = "065cbf938a16f20335c40cfe180f9cd4955c6a5a"
[[projects]]
branch = "master"
@ -377,6 +423,7 @@
"nacl/secretbox",
"openpgp/armor",
"openpgp/errors",
"pbkdf2",
"poly1305",
"ripemd160",
"salsa20/salsa"
@ -393,15 +440,16 @@
"http2/hpack",
"idna",
"internal/timeseries",
"netutil",
"trace"
]
revision = "afe8f62b1d6bbd81f31868121a50b06d8188e1f9"
revision = "e514e69ffb8bc3c76a71ae40de0118d794855992"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "a200a19cb90b19de298170992778b1fda7217bd6"
revision = "7138fd3d9dc8335c567ca206f4333fb75eb05d56"
[[projects]]
name = "golang.org/x/text"
@ -462,6 +510,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "8ad5e4b90e57805024944a6ee5eed246f109c18724d453093e82bac1469dbd9e"
inputs-digest = "578ae0e0126ffd04006b6755a02bc25c95e2eb2ecb4ea99869c4ada133f29f6b"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -36,10 +36,6 @@
name = "github.com/mattn/go-isatty"
version = "~0.0.3"
[[constraint]]
name = "github.com/pkg/errors"
version = "~0.8.0"
[[constraint]]
name = "github.com/spf13/cobra"
version = "~0.0.1"
@ -48,33 +44,37 @@
name = "github.com/spf13/viper"
version = "~1.0.0"
[[constraint]]
name = "github.com/pkg/errors"
version = "=0.8.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "~1.2.1"
[[constraint]]
name = "github.com/tendermint/abci"
version = "=0.12.0"
[[constraint]]
name = "github.com/tendermint/go-crypto"
version = "=0.6.2"
version = "=1.2.1"
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "=0.9.9"
version = "=0.10.1"
[[constraint]]
name = "github.com/tendermint/iavl"
version = "=0.8.0-rc0"
revision = "481b89cbbe6a641f7f6cb5db92b30b20f5a2e001"
[[constraint]]
name = "github.com/tendermint/tendermint"
revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601"
revision = "8412b75b1070ac023405e8228e017ed36531fe1b"
[[override]]
[[constraint]]
name = "github.com/tendermint/tmlibs"
revision = "0c98d10b4ffbd87978d79c160e835b3d3df241ec"
version = "=0.9.0"
[[constraint]]
name = "github.com/bartekn/go-bip39"
branch = "master"
[[constraint]]
name = "github.com/zondax/ledger-goclient"
revision = "065cbf938a16f20335c40cfe180f9cd4955c6a5a"
# this got updated and broke, so locked to an old working commit ...
[[override]]

View File

@ -7,7 +7,7 @@ import (
"github.com/pkg/errors"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -85,7 +85,6 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *Bas
txDecoder: defaultTxDecoder(cdc),
}
// Register the undefined & root codespaces, which should not be used by any modules
app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined)
app.codespacer.RegisterOrPanic(sdk.CodespaceRoot)
return app
}
@ -135,7 +134,7 @@ func defaultTxDecoder(cdc *wire.Codec) sdk.TxDecoder {
// are registered by MakeTxCodec
err := cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").Trace(err.Error())
return nil, sdk.ErrTxDecode("").TraceSDK(err.Error())
}
return tx, nil
}

View File

@ -9,8 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
@ -397,7 +397,7 @@ func TestSimulateTx(t *testing.T) {
require.Equal(t, result.Code, sdk.ABCICodeOK, result.Log)
require.Equal(t, int64(80), result.GasUsed)
counter--
encoded, err := json.Marshal(tx)
encoded, err := app.cdc.MarshalJSON(tx)
require.Nil(t, err)
query := abci.RequestQuery{
Path: "/app/simulate",
@ -735,9 +735,14 @@ func GenTx(chainID string, msgs []sdk.Msg, accnums []int64, seq []int64, priv ..
sigs := make([]auth.StdSignature, len(priv))
for i, p := range priv {
sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, ""))
// TODO: replace with proper error handling:
if err != nil {
panic(err)
}
sigs[i] = auth.StdSignature{
PubKey: p.PubKey(),
Signature: p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, "")),
Signature: sig,
AccountNumber: accnums[i],
Sequence: seq[i],
}
@ -994,17 +999,15 @@ func copyVal(val abci.Validator) abci.Validator {
}
func toJSON(o interface{}) []byte {
bz, err := json.Marshal(o)
bz, err := wire.Cdc.MarshalJSON(o)
if err != nil {
panic(err)
}
// fmt.Println(">> toJSON:", string(bz))
return bz
}
func fromJSON(bz []byte, ptr interface{}) {
// fmt.Println(">> fromJSON:", string(bz))
err := json.Unmarshal(bz, ptr)
err := wire.Cdc.UnmarshalJSON(bz, ptr)
if err != nil {
panic(err)
}

View File

@ -1,8 +1,8 @@
package baseapp
import (
"github.com/tendermint/abci/server"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tendermint/abci/server"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
)

View File

@ -126,7 +126,7 @@ func (ctx CoreContext) GetFromAddress() (from sdk.Address, err error) {
return nil, errors.Errorf("no key for: %s", name)
}
return info.PubKey.Address(), nil
return info.GetPubKey().Address(), nil
}
// sign and build the transaction from the msg
@ -187,15 +187,29 @@ func (ctx CoreContext) ensureSignBuild(name string, msgs []sdk.Msg, cdc *wire.Co
return nil, err
}
passphrase, err := ctx.GetPassphraseFromStdin(name)
var txBytes []byte
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
txBytes, err := ctx.SignAndBuild(name, passphrase, msgs, cdc)
info, err := keybase.Get(name)
if err != nil {
return nil, err
}
var passphrase string
// Only need a passphrase for locally-stored keys
if info.GetType() == "local" {
passphrase, err = ctx.GetPassphraseFromStdin(name)
if err != nil {
return nil, fmt.Errorf("Error fetching passphrase: %v", err)
}
}
txBytes, err = ctx.SignAndBuild(name, passphrase, msgs, cdc)
if err != nil {
return nil, fmt.Errorf("Error signing transaction: %v", err)
}
return txBytes, err
}

View File

@ -20,6 +20,7 @@ type CoreContext struct {
Client rpcclient.Client
Decoder auth.AccountDecoder
AccountStore string
UseLedger bool
}
// WithChainID - return a copy of the context with an updated chainID
@ -94,3 +95,9 @@ func (c CoreContext) WithAccountStore(accountStore string) CoreContext {
c.AccountStore = accountStore
return c
}
// WithUseLedger - return a copy of the context with an updated UseLedger
func (c CoreContext) WithUseLedger(useLedger bool) CoreContext {
c.UseLedger = useLedger
return c
}

View File

@ -40,6 +40,7 @@ func NewCoreContextFromViper() CoreContext {
Client: rpc,
Decoder: nil,
AccountStore: "acc",
UseLedger: viper.GetBool(client.FlagUseLedger),
}
}

View File

@ -4,6 +4,7 @@ import "github.com/spf13/cobra"
// nolint
const (
FlagUseLedger = "ledger"
FlagChainID = "chain-id"
FlagNode = "node"
FlagHeight = "height"
@ -25,6 +26,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
for _, c := range cmds {
// TODO: make this default false when we support proofs
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses")
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block")
@ -42,6 +44,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().String(FlagFee, "", "Fee to pay along with transaction")
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction")
}
return cmds

View File

@ -1,8 +1,7 @@
package client
import (
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/words"
"github.com/cosmos/cosmos-sdk/crypto/keys"
dbm "github.com/tendermint/tmlibs/db"
)
@ -11,7 +10,6 @@ import (
func GetKeyBase(db dbm.DB) keys.Keybase {
keybase := keys.New(
db,
words.MustLoadCodec("english"),
)
return keybase
}

View File

@ -12,7 +12,9 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/go-crypto/keys"
ccrypto "github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/tendermint/tmlibs/cli"
)
@ -21,6 +23,8 @@ const (
flagRecover = "recover"
flagNoBackup = "no-backup"
flagDryRun = "dry-run"
flagAccount = "account"
flagIndex = "index"
)
func addKeyCommand() *cobra.Command {
@ -32,10 +36,13 @@ If you select --seed/-s you can recover a key from the seed
phrase, otherwise, a new key will be generated.`,
RunE: runAddCmd,
}
cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)")
cmd.Flags().StringP(flagType, "t", "secp256k1", "Type of private key (secp256k1|ed25519)")
cmd.Flags().Bool(client.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device")
cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating")
cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore")
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
cmd.Flags().Uint32(flagIndex, 0, "Index number for HD derivation")
return cmd
}
@ -70,21 +77,34 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
}
}
pass, err = client.GetCheckPassword(
"Enter a passphrase for your key:",
"Repeat the passphrase:", buf)
if err != nil {
return err
// ask for a password when generating a local key
if !viper.GetBool(client.FlagUseLedger) {
pass, err = client.GetCheckPassword(
"Enter a passphrase for your key:",
"Repeat the passphrase:", buf)
if err != nil {
return err
}
}
}
if viper.GetBool(flagRecover) {
if viper.GetBool(client.FlagUseLedger) {
account := uint32(viper.GetInt(flagAccount))
index := uint32(viper.GetInt(flagIndex))
path := ccrypto.DerivationPath{44, 118, account, 0, index}
algo := keys.SigningAlgo(viper.GetString(flagType))
info, err := kb.CreateLedger(name, path, algo)
if err != nil {
return err
}
printCreate(info, "")
} else if viper.GetBool(flagRecover) {
seed, err := client.GetSeed(
"Enter your recovery seed phrase:", buf)
if err != nil {
return err
}
info, err := kb.Recover(name, pass, seed)
info, err := kb.CreateFundraiserKey(name, seed, pass)
if err != nil {
return err
}
@ -92,8 +112,8 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
viper.Set(flagNoBackup, true)
printCreate(info, "")
} else {
algo := keys.CryptoAlgo(viper.GetString(flagType))
info, seed, err := kb.Create(name, pass, algo)
algo := keys.SigningAlgo(viper.GetString(flagType))
info, seed, err := kb.CreateMnemonic(name, keys.English, pass, algo)
if err != nil {
return err
}
@ -108,7 +128,7 @@ func printCreate(info keys.Info, seed string) {
case "text":
printInfo(info)
// print seed unless requested not to.
if !viper.GetBool(flagNoBackup) {
if !viper.GetBool(client.FlagUseLedger) && !viper.GetBool(flagNoBackup) {
fmt.Println("**Important** write this seed phrase in a safe place.")
fmt.Println("It is the only way to recover your account if you ever forget your password.")
fmt.Println()
@ -139,7 +159,12 @@ func printCreate(info keys.Info, seed string) {
type NewKeyBody struct {
Name string `json:"name"`
Password string `json:"password"`
Seed string `json:"seed"`
}
// new key response REST body
type NewKeyResponse struct {
Address string `json:"address"`
Mnemonic string `json:"mnemonic"`
}
// add new key REST handler
@ -172,16 +197,11 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("You have to specify a password for the locally stored account."))
return
}
if m.Seed == "" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("You have to specify a seed for the locally stored account."))
return
}
// check if already exists
infos, err := kb.List()
for _, i := range infos {
if i.Name == m.Name {
if i.GetName() == m.Name {
w.WriteHeader(http.StatusConflict)
w.Write([]byte(fmt.Sprintf("Account with name %s already exists.", m.Name)))
return
@ -189,22 +209,30 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
}
// create account
info, err := kb.Recover(m.Name, m.Password, m.Seed)
info, mnemonic, err := kb.CreateMnemonic(m.Name, keys.English, m.Password, keys.Secp256k1)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write([]byte(info.PubKey.Address().String()))
bz, err := json.Marshal(NewKeyResponse{
Address: info.GetPubKey().Address().String(),
Mnemonic: mnemonic,
})
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(bz)
}
// function to just a new seed to display in the UI before actually persisting it in the keybase
func getSeed(algo keys.CryptoAlgo) string {
func getSeed(algo keys.SigningAlgo) string {
kb := client.MockKeyBase()
pass := "throwing-this-key-away"
name := "inmemorykey"
_, seed, _ := kb.Create(name, pass, algo)
_, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo)
return seed
}
@ -212,11 +240,11 @@ func getSeed(algo keys.CryptoAlgo) string {
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
algoType := vars["type"]
// algo type defaults to ed25519
// algo type defaults to secp256k1
if algoType == "" {
algoType = "ed25519"
algoType = "secp256k1"
}
algo := keys.CryptoAlgo(algoType)
algo := keys.SigningAlgo(algoType)
seed := getSeed(algo)
w.Write([]byte(seed))

View File

@ -6,8 +6,8 @@ import (
"net/http"
"github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/gorilla/mux"
keys "github.com/tendermint/go-crypto/keys"
"github.com/spf13/cobra"
)

View File

@ -4,8 +4,8 @@ import (
"encoding/json"
"net/http"
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/gorilla/mux"
keys "github.com/tendermint/go-crypto/keys"
"github.com/spf13/cobra"
)
@ -28,7 +28,7 @@ var showKeysCmd = &cobra.Command{
func getKey(name string) (keys.Info, error) {
kb, err := GetKeyBase()
if err != nil {
return keys.Info{}, err
return nil, err
}
return kb.Get(name)

View File

@ -6,8 +6,8 @@ import (
"net/http"
"github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/gorilla/mux"
keys "github.com/tendermint/go-crypto/keys"
"github.com/spf13/cobra"
)

View File

@ -6,7 +6,7 @@ import (
"github.com/spf13/viper"
keys "github.com/tendermint/go-crypto/keys"
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
@ -49,6 +49,7 @@ func SetKeyBase(kb keys.Keybase) {
// used for outputting keys.Info over REST
type KeyOutput struct {
Name string `json:"name"`
Type string `json:"type"`
Address string `json:"address"`
PubKey string `json:"pub_key"`
Seed string `json:"seed,omitempty"`
@ -69,16 +70,17 @@ func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) {
// create a KeyOutput in bech32 format
func Bech32KeyOutput(info keys.Info) (KeyOutput, error) {
bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.PubKey.Address().Bytes()))
bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.GetPubKey().Address().Bytes()))
if err != nil {
return KeyOutput{}, err
}
bechPubKey, err := sdk.Bech32ifyAccPub(info.PubKey)
bechPubKey, err := sdk.Bech32ifyAccPub(info.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return KeyOutput{
Name: info.Name,
Name: info.GetName(),
Type: info.GetType(),
Address: bechAccount,
PubKey: bechPubKey,
}, nil
@ -91,7 +93,7 @@ func printInfo(info keys.Info) {
}
switch viper.Get(cli.OutputFlag) {
case "text":
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
printKeyOutput(ko)
case "json":
out, err := MarshalJSON(ko)
@ -109,7 +111,7 @@ func printInfos(infos []keys.Info) {
}
switch viper.Get(cli.OutputFlag) {
case "text":
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
for _, ko := range kos {
printKeyOutput(ko)
}
@ -123,5 +125,5 @@ func printInfos(infos []keys.Info) {
}
func printKeyOutput(ko KeyOutput) {
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey)
}

View File

@ -2,7 +2,6 @@ package lcd
import (
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"regexp"
@ -12,8 +11,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
cryptoKeys "github.com/tendermint/go-crypto/keys"
cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
abci "github.com/tendermint/tendermint/abci/types"
p2p "github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tmlibs/common"
@ -23,6 +22,7 @@ import (
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
tests "github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/stake"
@ -36,9 +36,9 @@ func TestKeys(t *testing.T) {
defer cleanup()
// get seed
// TODO Do we really need this endpoint?
res, body := Request(t, port, "GET", "/keys/seed", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
newSeed := body
reg, err := regexp.Compile(`([a-z]+ ){12}`)
require.Nil(t, err)
match := reg.MatchString(seed)
@ -48,16 +48,14 @@ func TestKeys(t *testing.T) {
newPassword := "0987654321"
// add key
var jsonStr = []byte(fmt.Sprintf(`{"name":"test_fail", "password":"%s"}`, password))
res, body = Request(t, port, "POST", "/keys", jsonStr)
assert.Equal(t, http.StatusBadRequest, res.StatusCode, "Account creation should require a seed "+body)
jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s"}`, newName, newPassword))
res, body = Request(t, port, "POST", "/keys", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
addr2 := body
var resp keys.NewKeyResponse
err = wire.Cdc.UnmarshalJSON([]byte(body), &resp)
require.Nil(t, err)
addr2 := resp.Address
assert.Len(t, addr2, 40, "Returned address has wrong format", addr2)
// existing keys
@ -171,7 +169,7 @@ func TestBlock(t *testing.T) {
res, body = Request(t, port, "GET", "/blocks/1", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = json.Unmarshal([]byte(body), &resultBlock)
err = wire.Cdc.UnmarshalJSON([]byte(body), &resultBlock)
require.Nil(t, err, "Couldn't parse block")
assert.NotEqual(t, ctypes.ResultBlock{}, resultBlock)
@ -604,9 +602,9 @@ func doSend(t *testing.T, port, seed, name, password string, addr sdk.Address) (
// create receive address
kb := client.MockKeyBase()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, "1234567890", cryptoKeys.SigningAlgo("secp256k1"))
require.Nil(t, err)
receiveAddr = receiveInfo.PubKey.Address()
receiveAddr = receiveInfo.GetPubKey().Address()
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
acc := getAccount(t, port, addr)
@ -615,7 +613,7 @@ func doSend(t *testing.T, port, seed, name, password string, addr sdk.Address) (
chainID := viper.GetString(client.FlagChainID)
// send
coinbz, err := json.Marshal(sdk.NewCoin("steak", 1))
coinbz, err := cdc.MarshalJSON(sdk.NewCoin("steak", 1))
if err != nil {
panic(err)
}
@ -623,9 +621,9 @@ func doSend(t *testing.T, port, seed, name, password string, addr sdk.Address) (
jsonStr := []byte(fmt.Sprintf(`{
"name":"%s",
"password":"%s",
"account_number":%d,
"sequence":%d,
"gas": 10000,
"account_number":"%d",
"sequence":"%d",
"gas": "10000",
"amount":[%s],
"chain_id":"%s"
}`, name, password, accnum, sequence, coinbz, chainID))
@ -641,9 +639,9 @@ func doSend(t *testing.T, port, seed, name, password string, addr sdk.Address) (
func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
// create receive address
kb := client.MockKeyBase()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, "1234567890", cryptoKeys.SigningAlgo("secp256k1"))
require.Nil(t, err)
receiveAddr := receiveInfo.PubKey.Address()
receiveAddr := receiveInfo.GetPubKey().Address()
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
chainID := viper.GetString(client.FlagChainID)
@ -657,14 +655,14 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Add
jsonStr := []byte(fmt.Sprintf(`{
"name":"%s",
"password": "%s",
"account_number":%d,
"sequence": %d,
"gas": 100000,
"account_number":"%d",
"sequence": "%d",
"gas": "100000",
"chain_id": "%s",
"amount":[
{
"denom": "%s",
"amount": 1
"amount": "1"
}
]
}`, name, password, accnum, sequence, chainID, "steak"))
@ -706,15 +704,15 @@ func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr,
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"account_number": %d,
"sequence": %d,
"gas": 10000,
"account_number": "%d",
"sequence": "%d",
"gas": "10000",
"chain_id": "%s",
"delegations": [
{
"delegator_addr": "%s",
"validator_addr": "%s",
"bond": { "denom": "%s", "amount": 60 }
"bond": { "denom": "%s", "amount": "60" }
}
],
"begin_unbondings": [],
@ -749,9 +747,9 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string,
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"account_number": %d,
"sequence": %d,
"gas": 10000,
"account_number": "%d",
"sequence": "%d",
"gas": "10000",
"chain_id": "%s",
"delegations": [],
"begin_unbondings": [
@ -793,9 +791,9 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string,
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"account_number": %d,
"sequence": %d,
"gas": 10000,
"account_number": "%d",
"sequence": "%d",
"gas": "10000",
"chain_id": "%s",
"delegations": [],
"begin_unbondings": [],
@ -922,18 +920,17 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA
"description": "test",
"proposal_type": "Text",
"proposer": "%s",
"initial_deposit": [{ "denom": "steak", "amount": 5 }],
"initial_deposit": [{ "denom": "steak", "amount": "5" }],
"base_req": {
"name": "%s",
"password": "%s",
"chain_id": "%s",
"account_number": %d,
"sequence": %d,
"gas": 100000
"account_number":"%d",
"sequence":"%d",
"gas":"100000"
}
}`, bechProposerAddr, name, password, chainID, accnum, sequence))
res, body := Request(t, port, "POST", "/gov/proposals", jsonStr)
fmt.Println(res)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results ctypes.ResultBroadcastTxCommit
@ -956,18 +953,17 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk
// deposit on proposal
jsonStr := []byte(fmt.Sprintf(`{
"depositer": "%s",
"amount": [{ "denom": "steak", "amount": 5 }],
"amount": [{ "denom": "steak", "amount": "5" }],
"base_req": {
"name": "%s",
"password": "%s",
"chain_id": "%s",
"account_number": %d,
"sequence": %d,
"gas": 100000
"account_number":"%d",
"sequence": "%d",
"gas":"100000"
}
}`, bechProposerAddr, name, password, chainID, accnum, sequence))
res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), jsonStr)
fmt.Println(res)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results ctypes.ResultBroadcastTxCommit
@ -995,9 +991,9 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ad
"name": "%s",
"password": "%s",
"chain_id": "%s",
"account_number": %d,
"sequence": %d,
"gas": 100000
"account_number": "%d",
"sequence": "%d",
"gas":"100000"
}
}`, bechProposerAddr, name, password, chainID, accnum, sequence))
res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), jsonStr)

View File

@ -31,6 +31,7 @@ import (
func ServeCommand(cdc *wire.Codec) *cobra.Command {
flagListenAddr := "laddr"
flagCORS := "cors"
flagMaxOpenConnections := "max-open"
cmd := &cobra.Command{
Use: "rest-server",
@ -40,7 +41,8 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command {
handler := createHandler(cdc)
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
With("module", "rest-server")
listener, err := tmserver.StartHTTPServer(listenAddr, handler, logger)
maxOpen := viper.GetInt(flagMaxOpenConnections)
listener, err := tmserver.StartHTTPServer(listenAddr, handler, logger, tmserver.Config{MaxOpenConnections: maxOpen})
if err != nil {
return err
}
@ -58,6 +60,7 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command {
cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)")
cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
cmd.Flags().IntP(flagMaxOpenConnections, "o", 1000, "Maximum open connections")
return cmd
}

View File

@ -15,10 +15,10 @@ import (
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
crkeys "github.com/tendermint/go-crypto/keys"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
abci "github.com/tendermint/tendermint/abci/types"
tmcfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
nm "github.com/tendermint/tendermint/node"
pvm "github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
@ -83,9 +83,9 @@ func GetKB(t *testing.T) crkeys.Keybase {
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (addr sdk.Address, seed string) {
var info crkeys.Info
var err error
info, seed, err = kb.Create(name, password, crkeys.AlgoEd25519)
info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1)
require.NoError(t, err)
addr = info.PubKey.Address()
addr = info.GetPubKey().Address()
return
}
@ -193,6 +193,7 @@ func startTM(tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc,
proxy.NewLocalClientCreator(app),
genDocProvider,
dbProvider,
nm.DefaultMetricsProvider,
logger.With("module", "node"))
if err != nil {
return nil, err
@ -213,7 +214,7 @@ func startTM(tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc,
// start the LCD. note this blocks!
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
handler := createHandler(cdc)
return tmrpc.StartHTTPServer(listenAddr, handler, logger)
return tmrpc.StartHTTPServer(listenAddr, handler, logger, tmrpc.Config{})
}
// make a test lcd test request

View File

@ -82,7 +82,7 @@ func NodeSyncingRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return
}
syncing := status.SyncInfo.Syncing
syncing := status.SyncInfo.CatchingUp
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))

View File

@ -2,7 +2,6 @@ package tx
import (
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
@ -12,7 +11,7 @@ import (
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client"
@ -72,7 +71,7 @@ func queryTx(cdc *wire.Codec, ctx context.CoreContext, hashHexStr string, trustN
return nil, err
}
return json.MarshalIndent(info, "", " ")
return wire.MarshalJSONIndent(cdc, info)
}
func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (txInfo, error) {

View File

@ -5,7 +5,7 @@ import (
"net/http"
keybase "github.com/cosmos/cosmos-sdk/client/keys"
keys "github.com/tendermint/go-crypto/keys"
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
)
// REST request body

View File

@ -4,7 +4,7 @@ import (
"encoding/json"
"os"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -5,7 +5,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
)
func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {

View File

@ -5,7 +5,7 @@ import (
"errors"
"github.com/spf13/pflag"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/server"

View File

@ -6,7 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/stretchr/testify/assert"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
)
func TestToAccount(t *testing.T) {

View File

@ -17,7 +17,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/stake"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
)
func TestGaiaCLISend(t *testing.T) {

View File

@ -5,7 +5,7 @@ import (
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -8,8 +8,8 @@ import (
"path"
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"

View File

@ -14,7 +14,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/spf13/cobra"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
)
func init() {

19
crypto/amino.go Normal file
View File

@ -0,0 +1,19 @@
package crypto
import (
"github.com/tendermint/go-amino"
tcrypto "github.com/tendermint/tendermint/crypto"
)
var cdc = amino.NewCodec()
func init() {
RegisterAmino(cdc)
tcrypto.RegisterAmino(cdc)
}
// RegisterAmino registers all go-crypto related types in the given (amino) codec.
func RegisterAmino(cdc *amino.Codec) {
cdc.RegisterConcrete(PrivKeyLedgerSecp256k1{},
"tendermint/PrivKeyLedgerSecp256k1", nil)
}

122
crypto/encode_test.go Normal file
View File

@ -0,0 +1,122 @@
package crypto
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"os"
"testing"
tcrypto "github.com/tendermint/tendermint/crypto"
)
type byter interface {
Bytes() []byte
}
func checkAminoBinary(t *testing.T, src byter, dst interface{}, size int) {
// Marshal to binary bytes.
bz, err := cdc.MarshalBinaryBare(src)
require.Nil(t, err, "%+v", err)
// Make sure this is compatible with current (Bytes()) encoding.
assert.Equal(t, src.Bytes(), bz, "Amino binary vs Bytes() mismatch")
// Make sure we have the expected length.
if size != -1 {
assert.Equal(t, size, len(bz), "Amino binary size mismatch")
}
// Unmarshal.
err = cdc.UnmarshalBinaryBare(bz, dst)
require.Nil(t, err, "%+v", err)
}
func checkAminoJSON(t *testing.T, src interface{}, dst interface{}, isNil bool) {
// Marshal to JSON bytes.
js, err := cdc.MarshalJSON(src)
require.Nil(t, err, "%+v", err)
if isNil {
assert.Equal(t, string(js), `null`)
} else {
assert.Contains(t, string(js), `"type":`)
assert.Contains(t, string(js), `"value":`)
}
// Unmarshal.
err = cdc.UnmarshalJSON(js, dst)
require.Nil(t, err, "%+v", err)
}
//nolint
func ExamplePrintRegisteredTypes() {
cdc.PrintTypes(os.Stdout)
// Output: | Type | Name | Prefix | Length | Notes |
//| ---- | ---- | ------ | ----- | ------ |
//| PrivKeyLedgerSecp256k1 | tendermint/PrivKeyLedgerSecp256k1 | 0x10CAB393 | variable | |
//| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | |
//| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | |
//| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | |
//| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | |
//| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | |
//| SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | |
}
func TestKeyEncodings(t *testing.T) {
cases := []struct {
privKey tcrypto.PrivKey
privSize, pubSize int // binary sizes
}{
{
privKey: tcrypto.GenPrivKeyEd25519(),
privSize: 69,
pubSize: 37,
},
{
privKey: tcrypto.GenPrivKeySecp256k1(),
privSize: 37,
pubSize: 38,
},
}
for _, tc := range cases {
// Check (de/en)codings of PrivKeys.
var priv2, priv3 tcrypto.PrivKey
checkAminoBinary(t, tc.privKey, &priv2, tc.privSize)
assert.EqualValues(t, tc.privKey, priv2)
checkAminoJSON(t, tc.privKey, &priv3, false) // TODO also check Prefix bytes.
assert.EqualValues(t, tc.privKey, priv3)
// Check (de/en)codings of Signatures.
var sig1, sig2, sig3 tcrypto.Signature
sig1, err := tc.privKey.Sign([]byte("something"))
assert.NoError(t, err)
checkAminoBinary(t, sig1, &sig2, -1) // Signature size changes for Secp anyways.
assert.EqualValues(t, sig1, sig2)
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes.
assert.EqualValues(t, sig1, sig3)
// Check (de/en)codings of PubKeys.
pubKey := tc.privKey.PubKey()
var pub2, pub3 tcrypto.PubKey
checkAminoBinary(t, pubKey, &pub2, tc.pubSize)
assert.EqualValues(t, pubKey, pub2)
checkAminoJSON(t, pubKey, &pub3, false) // TODO also check Prefix bytes.
assert.EqualValues(t, pubKey, pub3)
}
}
func TestNilEncodings(t *testing.T) {
// Check nil Signature.
var a, b tcrypto.Signature
checkAminoJSON(t, &a, &b, true)
assert.EqualValues(t, a, b)
// Check nil PubKey.
var c, d tcrypto.PubKey
checkAminoJSON(t, &c, &d, true)
assert.EqualValues(t, c, d)
// Check nil PrivKey.
var e, f tcrypto.PrivKey
checkAminoJSON(t, &e, &f, true)
assert.EqualValues(t, e, f)
}

View File

@ -0,0 +1,35 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bcrypt
import "encoding/base64"
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
var bcEncoding = base64.NewEncoding(alphabet)
func base64Encode(src []byte) []byte {
n := bcEncoding.EncodedLen(len(src))
dst := make([]byte, n)
bcEncoding.Encode(dst, src)
for dst[n-1] == '=' {
n--
}
return dst[:n]
}
func base64Decode(src []byte) ([]byte, error) {
numOfEquals := 4 - (len(src) % 4)
for i := 0; i < numOfEquals; i++ {
src = append(src, '=')
}
dst := make([]byte, bcEncoding.DecodedLen(len(src)))
n, err := bcEncoding.Decode(dst, src)
if err != nil {
return nil, err
}
return dst[:n], nil
}

View File

@ -0,0 +1,297 @@
package bcrypt
// MODIFIED BY TENDERMINT TO EXPOSE NONCE
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
// The code is a port of Provos and Mazières's C implementation.
import (
"crypto/subtle"
"errors"
"fmt"
"strconv"
"golang.org/x/crypto/blowfish"
)
const (
// the minimum allowable cost as passed in to GenerateFromPassword
MinCost int = 4
// the maximum allowable cost as passed in to GenerateFromPassword
MaxCost int = 31
// the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
DefaultCost int = 10
)
// The error returned from CompareHashAndPassword when a password and hash do
// not match.
var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
// The error returned from CompareHashAndPassword when a hash is too short to
// be a bcrypt hash.
var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
// The error returned from CompareHashAndPassword when a hash was created with
// a bcrypt algorithm newer than this implementation.
type HashVersionTooNewError byte
func (hv HashVersionTooNewError) Error() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
}
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
type InvalidHashPrefixError byte
// Format error
func (ih InvalidHashPrefixError) Error() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
}
// Invalid bcrypt cost
type InvalidCostError int
func (ic InvalidCostError) Error() string {
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) // nolint: unconvert
}
const (
majorVersion = '2'
minorVersion = 'a'
maxSaltSize = 16
maxCryptedHashSize = 23
encodedSaltSize = 22
encodedHashSize = 31
minHashSize = 59
)
// magicCipherData is an IV for the 64 Blowfish encryption calls in
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
var magicCipherData = []byte{
0x4f, 0x72, 0x70, 0x68,
0x65, 0x61, 0x6e, 0x42,
0x65, 0x68, 0x6f, 0x6c,
0x64, 0x65, 0x72, 0x53,
0x63, 0x72, 0x79, 0x44,
0x6f, 0x75, 0x62, 0x74,
}
type hashed struct {
hash []byte
salt []byte
cost int // allowed range is MinCost to MaxCost
major byte
minor byte
}
// GenerateFromPassword returns the bcrypt hash of the password at the given
// cost. If the cost given is less than MinCost, the cost will be set to
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
// to compare the returned hashed password with its cleartext version.
func GenerateFromPassword(salt []byte, password []byte, cost int) ([]byte, error) {
if len(salt) != maxSaltSize {
return nil, fmt.Errorf("salt len must be %v", maxSaltSize)
}
p, err := newFromPassword(salt, password, cost)
if err != nil {
return nil, err
}
return p.Hash(), nil
}
// CompareHashAndPassword compares a bcrypt hashed password with its possible
// plaintext equivalent. Returns nil on success, or an error on failure.
func CompareHashAndPassword(hashedPassword, password []byte) error {
p, err := newFromHash(hashedPassword)
if err != nil {
return err
}
otherHash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return err
}
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
return nil
}
return ErrMismatchedHashAndPassword
}
// Cost returns the hashing cost used to create the given hashed
// password. When, in the future, the hashing cost of a password system needs
// to be increased in order to adjust for greater computational power, this
// function allows one to establish which passwords need to be updated.
func Cost(hashedPassword []byte) (int, error) {
p, err := newFromHash(hashedPassword)
if err != nil {
return 0, err
}
return p.cost, nil
}
func newFromPassword(salt []byte, password []byte, cost int) (*hashed, error) {
if cost < MinCost {
cost = DefaultCost
}
p := new(hashed)
p.major = majorVersion
p.minor = minorVersion
err := checkCost(cost)
if err != nil {
return nil, err
}
p.cost = cost
p.salt = base64Encode(salt)
hash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return nil, err
}
p.hash = hash
return p, err
}
func newFromHash(hashedSecret []byte) (*hashed, error) {
if len(hashedSecret) < minHashSize {
return nil, ErrHashTooShort
}
p := new(hashed)
n, err := p.decodeVersion(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
n, err = p.decodeCost(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
// The "+2" is here because we'll have to append at most 2 '=' to the salt
// when base64 decoding it in expensiveBlowfishSetup().
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
copy(p.salt, hashedSecret[:encodedSaltSize])
hashedSecret = hashedSecret[encodedSaltSize:]
p.hash = make([]byte, len(hashedSecret))
copy(p.hash, hashedSecret)
return p, nil
}
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
cipherData := make([]byte, len(magicCipherData))
copy(cipherData, magicCipherData)
c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
if err != nil {
return nil, err
}
for i := 0; i < 24; i += 8 {
for j := 0; j < 64; j++ {
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
}
}
// Bug compatibility with C bcrypt implementations. We only encode 23 of
// the 24 bytes encrypted.
hsh := base64Encode(cipherData[:maxCryptedHashSize])
return hsh, nil
}
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
csalt, err := base64Decode(salt)
if err != nil {
return nil, err
}
// Bug compatibility with C bcrypt implementations. They use the trailing
// NULL in the key string during expansion.
ckey := append(key, 0)
c, err := blowfish.NewSaltedCipher(ckey, csalt)
if err != nil {
return nil, err
}
var i, rounds uint64
rounds = 1 << cost
for i = 0; i < rounds; i++ {
blowfish.ExpandKey(ckey, c)
blowfish.ExpandKey(csalt, c)
}
return c, nil
}
func (p *hashed) Hash() []byte {
arr := make([]byte, 60)
arr[0] = '$'
arr[1] = p.major
n := 2
if p.minor != 0 {
arr[2] = p.minor
n = 3
}
arr[n] = '$'
n++
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
n += 2
arr[n] = '$'
n++
copy(arr[n:], p.salt)
n += encodedSaltSize
copy(arr[n:], p.hash)
n += encodedHashSize
return arr[:n]
}
func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
if sbytes[0] != '$' {
return -1, InvalidHashPrefixError(sbytes[0])
}
if sbytes[1] > majorVersion {
return -1, HashVersionTooNewError(sbytes[1])
}
p.major = sbytes[1]
n := 3
if sbytes[2] != '$' {
p.minor = sbytes[2]
n++
}
return n, nil
}
// sbytes should begin where decodeVersion left off.
func (p *hashed) decodeCost(sbytes []byte) (int, error) {
cost, err := strconv.Atoi(string(sbytes[0:2]))
if err != nil {
return -1, err
}
err = checkCost(cost)
if err != nil {
return -1, err
}
p.cost = cost
return 3, nil
}
func (p *hashed) String() string {
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
}
func checkCost(cost int) error {
if cost < MinCost || cost > MaxCost {
return InvalidCostError(cost)
}
return nil
}

View File

@ -0,0 +1,60 @@
package bip39
import (
"strings"
"github.com/bartekn/go-bip39"
)
// ValidSentenceLen defines the mnemonic sentence lengths supported by this BIP 39 library.
type ValidSentenceLen uint8
const (
// FundRaiser is the sentence length used during the cosmos fundraiser (12 words).
FundRaiser ValidSentenceLen = 12
// FreshKey is the sentence length used for newly created keys (24 words).
FreshKey ValidSentenceLen = 24
)
// NewMnemonic will return a string consisting of the mnemonic words for
// the given sentence length.
func NewMnemonic(len ValidSentenceLen) (words []string, err error) {
// len = (entropySize + checksum) / 11
var entropySize int
switch len {
case FundRaiser:
entropySize = 128
case FreshKey:
entropySize = 256
}
var entropy []byte
entropy, err = bip39.NewEntropy(entropySize)
if err != nil {
return
}
var mnemonic string
mnemonic, err = bip39.NewMnemonic(entropy)
if err != nil {
return
}
words = strings.Split(mnemonic, " ")
return
}
// MnemonicToSeed creates a BIP 39 seed from the passed mnemonic (with an empty BIP 39 password).
// This method does not validate the mnemonics checksum.
func MnemonicToSeed(mne string) (seed []byte) {
// we do not checksum here...
seed = bip39.NewSeed(mne, "")
return
}
// MnemonicToSeedWithErrChecking returns the same seed as MnemonicToSeed.
// It creates a BIP 39 seed from the passed mnemonic (with an empty BIP 39 password).
//
// Different from MnemonicToSeed it validates the checksum.
// For details on the checksum see the BIP 39 spec.
func MnemonicToSeedWithErrChecking(mne string) (seed []byte, err error) {
seed, err = bip39.NewSeedWithErrorChecking(mne, "")
return
}

View File

@ -0,0 +1,15 @@
package bip39
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestWordCodec_NewMnemonic(t *testing.T) {
_, err := NewMnemonic(FundRaiser)
assert.NoError(t, err, "unexpected error generating fundraiser mnemonic")
_, err = NewMnemonic(FreshKey)
assert.NoError(t, err, "unexpected error generating new 24-word mnemonic")
}

View File

@ -0,0 +1,80 @@
package hd
import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"testing"
"github.com/bartekn/go-bip39"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/crypto"
)
type addrData struct {
Mnemonic string
Master string
Seed string
Priv string
Pub string
Addr string
}
func initFundraiserTestVectors(t *testing.T) []addrData {
// NOTE: atom fundraiser address
// var hdPath string = "m/44'/118'/0'/0/0"
var hdToAddrTable []addrData
b, err := ioutil.ReadFile("test.json")
if err != nil {
t.Fatalf("could not read fundraiser test vector file (test.json): %s", err)
}
err = json.Unmarshal(b, &hdToAddrTable)
if err != nil {
t.Fatalf("could not decode test vectors (test.json): %s", err)
}
return hdToAddrTable
}
func TestFundraiserCompatibility(t *testing.T) {
hdToAddrTable := initFundraiserTestVectors(t)
for i, d := range hdToAddrTable {
privB, _ := hex.DecodeString(d.Priv)
pubB, _ := hex.DecodeString(d.Pub)
addrB, _ := hex.DecodeString(d.Addr)
seedB, _ := hex.DecodeString(d.Seed)
masterB, _ := hex.DecodeString(d.Master)
seed := bip39.NewSeed(d.Mnemonic, "")
t.Log("================================")
t.Logf("ROUND: %d MNEMONIC: %s", i, d.Mnemonic)
master, ch := ComputeMastersFromSeed(seed)
priv, err := DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
assert.NoError(t, err)
pub := crypto.PrivKeySecp256k1(priv).PubKey()
t.Log("\tNODEJS GOLANG\n")
t.Logf("SEED \t%X %X\n", seedB, seed)
t.Logf("MSTR \t%X %X\n", masterB, master)
t.Logf("PRIV \t%X %X\n", privB, priv)
t.Logf("PUB \t%X %X\n", pubB, pub)
assert.Equal(t, seedB, seed)
assert.Equal(t, master[:], masterB, fmt.Sprintf("Expected masters to match for %d", i))
assert.Equal(t, priv[:], privB, "Expected priv keys to match")
var pubBFixed [33]byte
copy(pubBFixed[:], pubB)
assert.Equal(t, pub, crypto.PubKeySecp256k1(pubBFixed), fmt.Sprintf("Expected pub keys to match for %d", i))
addr := pub.Address()
t.Logf("ADDR \t%X %X\n", addrB, addr)
assert.Equal(t, addr, crypto.Address(addrB), fmt.Sprintf("Expected addresses to match %d", i))
}
}

168
crypto/keys/hd/hdpath.go Normal file
View File

@ -0,0 +1,168 @@
// Package hd provides basic functionality Hierarchical Deterministic Wallets.
//
// The user must understand the overall concept of the BIP 32 and the BIP 44 specs:
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
//
// In combination with the bip39 package in go-crypto this package provides the functionality for deriving keys using a
// BIP 44 HD path, or, more general, by passing a BIP 32 path.
//
// In particular, this package (together with bip39) provides all necessary functionality to derive keys from
// mnemonics generated during the cosmos fundraiser.
package hd
import (
"crypto/hmac"
"crypto/sha512"
"encoding/binary"
"errors"
"fmt"
"math/big"
"strconv"
"strings"
"github.com/btcsuite/btcd/btcec"
"github.com/tendermint/tendermint/crypto"
)
// BIP44Prefix is the parts of the BIP32 HD path that are fixed by what we used during the fundraiser.
const (
BIP44Prefix = "44'/118'/"
FullFundraiserPath = BIP44Prefix + "0'/0/0"
)
// BIP44Params wraps BIP 44 params (5 level BIP 32 path).
// To receive a canonical string representation ala
// m / purpose' / coin_type' / account' / change / address_index
// call String() on a BIP44Params instance.
type BIP44Params struct {
purpose uint32
coinType uint32
account uint32
change bool
addressIdx uint32
}
// NewParams creates a BIP 44 parameter object from the params:
// m / purpose' / coin_type' / account' / change / address_index
func NewParams(purpose, coinType, account uint32, change bool, addressIdx uint32) *BIP44Params {
return &BIP44Params{
purpose: purpose,
coinType: coinType,
account: account,
change: change,
addressIdx: addressIdx,
}
}
// NewFundraiserParams creates a BIP 44 parameter object from the params:
// m / 44' / 118' / account' / 0 / address_index
// The fixed parameters (purpose', coin_type', and change) are determined by what was used in the fundraiser.
func NewFundraiserParams(account uint32, addressIdx uint32) *BIP44Params {
return NewParams(44, 118, account, false, addressIdx)
}
func (p BIP44Params) String() string {
var changeStr string
if p.change {
changeStr = "1"
} else {
changeStr = "0"
}
// m / purpose' / coin_type' / account' / change / address_index
return fmt.Sprintf("%d'/%d'/%d'/%s/%d",
p.purpose,
p.coinType,
p.account,
changeStr,
p.addressIdx)
}
// ComputeMastersFromSeed returns the master public key, master secret, and chain code in hex.
func ComputeMastersFromSeed(seed []byte) (secret [32]byte, chainCode [32]byte) {
masterSecret := []byte("Bitcoin seed")
secret, chainCode = i64(masterSecret, seed)
return
}
// DerivePrivateKeyForPath derives the private key by following the BIP 32/44 path from privKeyBytes,
// using the given chainCode.
func DerivePrivateKeyForPath(privKeyBytes [32]byte, chainCode [32]byte, path string) ([32]byte, error) {
data := privKeyBytes
parts := strings.Split(path, "/")
for _, part := range parts {
// do we have an apostrophe?
harden := part[len(part)-1:] == "'"
// harden == private derivation, else public derivation:
if harden {
part = part[:len(part)-1]
}
idx, err := strconv.Atoi(part)
if err != nil {
return [32]byte{}, fmt.Errorf("invalid BIP 32 path: %s", err)
}
if idx < 0 {
return [32]byte{}, errors.New("invalid BIP 32 path: index negative ot too large")
}
data, chainCode = derivePrivateKey(data, chainCode, uint32(idx), harden)
}
var derivedKey [32]byte
n := copy(derivedKey[:], data[:])
if n != 32 || len(data) != 32 {
return [32]byte{}, fmt.Errorf("expected a (secp256k1) key of length 32, got length: %v", len(data))
}
return derivedKey, nil
}
// derivePrivateKey derives the private key with index and chainCode.
// If harden is true, the derivation is 'hardened'.
// It returns the new private key and new chain code.
// For more information on hardened keys see:
// - https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
func derivePrivateKey(privKeyBytes [32]byte, chainCode [32]byte, index uint32, harden bool) ([32]byte, [32]byte) {
var data []byte
if harden {
index = index | 0x80000000
data = append([]byte{byte(0)}, privKeyBytes[:]...)
} else {
// this can't return an error:
pubkey := crypto.PrivKeySecp256k1(privKeyBytes).PubKey()
public := pubkey.(crypto.PubKeySecp256k1)
data = public[:]
}
data = append(data, uint32ToBytes(index)...)
data2, chainCode2 := i64(chainCode[:], data)
x := addScalars(privKeyBytes[:], data2[:])
return x, chainCode2
}
// modular big endian addition
func addScalars(a []byte, b []byte) [32]byte {
aInt := new(big.Int).SetBytes(a)
bInt := new(big.Int).SetBytes(b)
sInt := new(big.Int).Add(aInt, bInt)
x := sInt.Mod(sInt, btcec.S256().N).Bytes()
x2 := [32]byte{}
copy(x2[32-len(x):], x)
return x2
}
func uint32ToBytes(i uint32) []byte {
b := [4]byte{}
binary.BigEndian.PutUint32(b[:], i)
return b[:]
}
// i64 returns the two halfs of the SHA512 HMAC of key and data.
func i64(key []byte, data []byte) (IL [32]byte, IR [32]byte) {
mac := hmac.New(sha512.New, key)
// sha512 does not err
_, _ = mac.Write(data)
I := mac.Sum(nil)
copy(IL[:], I[:32])
copy(IR[:], I[32:])
return
}

View File

@ -0,0 +1,75 @@
package hd
import (
"encoding/hex"
"fmt"
"github.com/cosmos/cosmos-sdk/crypto/keys/bip39"
)
//nolint
func ExampleStringifyPathParams() {
path := NewParams(44, 0, 0, false, 0)
fmt.Println(path.String())
// Output: 44'/0'/0'/0/0
}
//nolint
func ExampleSomeBIP32TestVecs() {
seed := bip39.MnemonicToSeed("barrel original fuel morning among eternal " +
"filter ball stove pluck matrix mechanic")
master, ch := ComputeMastersFromSeed(seed)
fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)")
fmt.Println()
// cosmos
priv, _ := DerivePrivateKeyForPath(master, ch, FullFundraiserPath)
fmt.Println(hex.EncodeToString(priv[:]))
// bitcoin
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0")
fmt.Println(hex.EncodeToString(priv[:]))
// ether
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0")
fmt.Println(hex.EncodeToString(priv[:]))
fmt.Println()
fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html")
fmt.Println()
seed = bip39.MnemonicToSeed(
"advice process birth april short trust crater change bacon monkey medal garment " +
"gorilla ranch hour rival razor call lunar mention taste vacant woman sister")
master, ch = ComputeMastersFromSeed(seed)
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
fmt.Println(hex.EncodeToString(priv[:]))
seed = bip39.MnemonicToSeed("idea naive region square margin day captain habit " +
"gun second farm pact pulse someone armed")
master, ch = ComputeMastersFromSeed(seed)
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
fmt.Println(hex.EncodeToString(priv[:]))
fmt.Println()
fmt.Println("BIP 32 example")
fmt.Println()
// bip32 path: m/0/7
seed = bip39.MnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
master, ch = ComputeMastersFromSeed(seed)
priv, _ = DerivePrivateKeyForPath(master, ch, "0/7")
fmt.Println(hex.EncodeToString(priv[:]))
// Output: keys from fundraiser test-vector (cosmos, bitcoin, ether)
//
// bfcb217c058d8bbafd5e186eae936106ca3e943889b0b4a093ae13822fd3170c
// e77c3de76965ad89997451de97b95bb65ede23a6bf185a55d80363d92ee37c3d
// 7fc4d8a8146dea344ba04c593517d3f377fa6cded36cd55aee0a0bb968e651bc
//
// keys generated via https://coinomi.com/recovery-phrase-tool.html
//
// a61f10c5fecf40c084c94fa54273b6f5d7989386be4a37669e6d6f7b0169c163
// 32c4599843de3ef161a629a461d12c60b009b676c35050be5f7ded3a3b23501f
//
// BIP 32 example
//
// c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78
}

1
crypto/keys/hd/test.json Normal file

File diff suppressed because one or more lines are too long

364
crypto/keys/keybase.go Normal file
View File

@ -0,0 +1,364 @@
package keys
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/pkg/errors"
tcrypto "github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/bip39"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
)
var _ Keybase = dbKeybase{}
// Language is a language to create the BIP 39 mnemonic in.
// Currently, only english is supported though.
// Find a list of all supported languages in the BIP 39 spec (word lists).
type Language int
const (
// English is the default language to create a mnemonic.
// It is the only supported language by this package.
English Language = iota + 1
// Japanese is currently not supported.
Japanese
// Korean is currently not supported.
Korean
// Spanish is currently not supported.
Spanish
// ChineseSimplified is currently not supported.
ChineseSimplified
// ChineseTraditional is currently not supported.
ChineseTraditional
// French is currently not supported.
French
// Italian is currently not supported.
Italian
)
var (
// ErrUnsupportedSigningAlgo is raised when the caller tries to use a different signing scheme than secp256k1.
ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo: only secp256k1 is supported")
// ErrUnsupportedLanguage is raised when the caller tries to use a different language than english for creating
// a mnemonic sentence.
ErrUnsupportedLanguage = errors.New("unsupported language: only english is supported")
)
// dbKeybase combines encryption and storage implementation to provide
// a full-featured key manager
type dbKeybase struct {
db dbm.DB
}
// New creates a new keybase instance using the passed DB for reading and writing keys.
func New(db dbm.DB) Keybase {
return dbKeybase{
db: db,
}
}
// CreateMnemonic generates a new key and persists it to storage, encrypted
// using the provided password.
// It returns the generated mnemonic and the key Info.
// It returns an error if it fails to
// generate a key for the given algo type, or if another key is
// already stored under the same name.
func (kb dbKeybase) CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, mnemonic string, err error) {
if language != English {
return nil, "", ErrUnsupportedLanguage
}
if algo != Secp256k1 {
err = ErrUnsupportedSigningAlgo
return
}
// default number of words (24):
mnemonicS, err := bip39.NewMnemonic(bip39.FreshKey)
if err != nil {
return
}
mnemonic = strings.Join(mnemonicS, " ")
seed := bip39.MnemonicToSeed(mnemonic)
info, err = kb.persistDerivedKey(seed, passwd, name, hd.FullFundraiserPath)
return
}
// CreateFundraiserKey converts a mnemonic to a private key and persists it,
// encrypted with the given password.
// TODO(ismail)
func (kb dbKeybase) CreateFundraiserKey(name, mnemonic, passwd string) (info Info, err error) {
words := strings.Split(mnemonic, " ")
if len(words) != 12 {
err = fmt.Errorf("recovering only works with 12 word (fundraiser) mnemonics, got: %v words", len(words))
return
}
seed, err := bip39.MnemonicToSeedWithErrChecking(mnemonic)
if err != nil {
return
}
info, err = kb.persistDerivedKey(seed, passwd, name, hd.FullFundraiserPath)
return
}
func (kb dbKeybase) Derive(name, mnemonic, passwd string, params hd.BIP44Params) (info Info, err error) {
seed, err := bip39.MnemonicToSeedWithErrChecking(mnemonic)
if err != nil {
return
}
info, err = kb.persistDerivedKey(seed, passwd, name, params.String())
return
}
// CreateLedger creates a new locally-stored reference to a Ledger keypair
// It returns the created key info and an error if the Ledger could not be queried
func (kb dbKeybase) CreateLedger(name string, path crypto.DerivationPath, algo SigningAlgo) (Info, error) {
if algo != Secp256k1 {
return nil, ErrUnsupportedSigningAlgo
}
priv, err := crypto.NewPrivKeyLedgerSecp256k1(path)
if err != nil {
return nil, err
}
pub := priv.PubKey()
return kb.writeLedgerKey(pub, path, name), nil
}
// CreateOffline creates a new reference to an offline keypair
// It returns the created key info
func (kb dbKeybase) CreateOffline(name string, pub tcrypto.PubKey) (Info, error) {
return kb.writeOfflineKey(pub, name), nil
}
func (kb *dbKeybase) persistDerivedKey(seed []byte, passwd, name, fullHdPath string) (info Info, err error) {
// create master key and derive first key:
masterPriv, ch := hd.ComputeMastersFromSeed(seed)
derivedPriv, err := hd.DerivePrivateKeyForPath(masterPriv, ch, fullHdPath)
if err != nil {
return
}
// if we have a password, use it to encrypt the private key and store it
// else store the public key only
if passwd != "" {
info = kb.writeLocalKey(tcrypto.PrivKeySecp256k1(derivedPriv), name, passwd)
} else {
pubk := tcrypto.PrivKeySecp256k1(derivedPriv).PubKey()
info = kb.writeOfflineKey(pubk, name)
}
return
}
// List returns the keys from storage in alphabetical order.
func (kb dbKeybase) List() ([]Info, error) {
var res []Info
iter := kb.db.Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
info, err := readInfo(iter.Value())
if err != nil {
return nil, err
}
res = append(res, info)
}
return res, nil
}
// Get returns the public information about one key.
func (kb dbKeybase) Get(name string) (Info, error) {
bs := kb.db.Get(infoKey(name))
return readInfo(bs)
}
// Sign signs the msg with the named key.
// It returns an error if the key doesn't exist or the decryption fails.
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig tcrypto.Signature, pub tcrypto.PubKey, err error) {
info, err := kb.Get(name)
if err != nil {
return
}
var priv tcrypto.PrivKey
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
if linfo.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return
}
priv, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, nil, err
}
case ledgerInfo:
linfo := info.(ledgerInfo)
priv, err = crypto.NewPrivKeyLedgerSecp256k1(linfo.Path)
if err != nil {
return
}
case offlineInfo:
linfo := info.(offlineInfo)
fmt.Printf("Bytes to sign:\n%s", msg)
buf := bufio.NewReader(os.Stdin)
fmt.Printf("\nEnter Amino-encoded signature:\n")
// Will block until user inputs the signature
signed, err := buf.ReadString('\n')
if err != nil {
return nil, nil, err
}
cdc.MustUnmarshalBinary([]byte(signed), sig)
return sig, linfo.GetPubKey(), nil
}
sig, err = priv.Sign(msg)
if err != nil {
return nil, nil, err
}
pub = priv.PubKey()
return sig, pub, nil
}
func (kb dbKeybase) Export(name string) (armor string, err error) {
bz := kb.db.Get(infoKey(name))
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
return armorInfoBytes(bz), nil
}
// ExportPubKey returns public keys in ASCII armored format.
// Retrieve a Info object by its name and return the public key in
// a portable format.
func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
bz := kb.db.Get(infoKey(name))
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
info, err := readInfo(bz)
if err != nil {
return
}
return armorPubKeyBytes(info.GetPubKey().Bytes()), nil
}
func (kb dbKeybase) Import(name string, armor string) (err error) {
bz := kb.db.Get(infoKey(name))
if len(bz) > 0 {
return errors.New("Cannot overwrite data for name " + name)
}
infoBytes, err := unarmorInfoBytes(armor)
if err != nil {
return
}
kb.db.Set(infoKey(name), infoBytes)
return nil
}
// ImportPubKey imports ASCII-armored public keys.
// Store a new Info object holding a public key only, i.e. it will
// not be possible to sign with it as it lacks the secret key.
func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
bz := kb.db.Get(infoKey(name))
if len(bz) > 0 {
return errors.New("Cannot overwrite data for name " + name)
}
pubBytes, err := unarmorPubKeyBytes(armor)
if err != nil {
return
}
pubKey, err := tcrypto.PubKeyFromBytes(pubBytes)
if err != nil {
return
}
kb.writeOfflineKey(pubKey, name)
return
}
// Delete removes key forever, but we must present the
// proper passphrase before deleting it (for security).
// A passphrase of 'yes' is used to delete stored
// references to offline and Ledger / HW wallet keys
func (kb dbKeybase) Delete(name, passphrase string) error {
// verify we have the proper password before deleting
info, err := kb.Get(name)
if err != nil {
return err
}
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
_, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return err
}
kb.db.DeleteSync(infoKey(name))
return nil
case ledgerInfo:
case offlineInfo:
if passphrase != "yes" {
return fmt.Errorf("enter 'yes' exactly to delete the key - this cannot be undone")
}
kb.db.DeleteSync(infoKey(name))
return nil
}
return nil
}
// Update changes the passphrase with which an already stored key is
// encrypted.
//
// oldpass must be the current passphrase used for encryption,
// newpass will be the only valid passphrase from this time forward.
func (kb dbKeybase) Update(name, oldpass, newpass string) error {
info, err := kb.Get(name)
if err != nil {
return err
}
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
key, err := unarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
if err != nil {
return err
}
kb.writeLocalKey(key, name, newpass)
return nil
default:
return fmt.Errorf("locally stored key required")
}
}
func (kb dbKeybase) writeLocalKey(priv tcrypto.PrivKey, name, passphrase string) Info {
// encrypt private key using passphrase
privArmor := encryptArmorPrivKey(priv, passphrase)
// make Info
pub := priv.PubKey()
info := newLocalInfo(name, pub, privArmor)
kb.writeInfo(info, name)
return info
}
func (kb dbKeybase) writeLedgerKey(pub tcrypto.PubKey, path crypto.DerivationPath, name string) Info {
info := newLedgerInfo(name, pub, path)
kb.writeInfo(info, name)
return info
}
func (kb dbKeybase) writeOfflineKey(pub tcrypto.PubKey, name string) Info {
info := newOfflineInfo(name, pub)
kb.writeInfo(info, name)
return info
}
func (kb dbKeybase) writeInfo(info Info, name string) {
// write the info by key
kb.db.SetSync(infoKey(name), writeInfo(info))
}
func infoKey(name string) []byte {
return []byte(fmt.Sprintf("%s.info", name))
}

382
crypto/keys/keybase_test.go Normal file
View File

@ -0,0 +1,382 @@
package keys
import (
"fmt"
"testing"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tmlibs/db"
)
// TestKeyManagement makes sure we can manipulate these keys well
func TestKeyManagement(t *testing.T) {
// make the storage with reasonable defaults
cstore := New(
dbm.NewMemDB(),
)
algo := Secp256k1
n1, n2, n3 := "personal", "business", "other"
p1, p2 := "1234", "really-secure!@#$"
// Check empty state
l, err := cstore.List()
require.Nil(t, err)
assert.Empty(t, l)
_, _, err = cstore.CreateMnemonic(n1, English, p1, Ed25519)
assert.Error(t, err, "ed25519 keys are currently not supported by keybase")
// create some keys
_, err = cstore.Get(n1)
assert.Error(t, err)
i, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.NoError(t, err)
require.Equal(t, n1, i.GetName())
_, _, err = cstore.CreateMnemonic(n2, English, p2, algo)
require.NoError(t, err)
// we can get these keys
i2, err := cstore.Get(n2)
assert.NoError(t, err)
_, err = cstore.Get(n3)
assert.NotNil(t, err)
// list shows them in order
keyS, err := cstore.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// note these are in alphabetical order
assert.Equal(t, n2, keyS[0].GetName())
assert.Equal(t, n1, keyS[1].GetName())
assert.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey())
// deleting a key removes it
err = cstore.Delete("bad name", "foo")
require.NotNil(t, err)
err = cstore.Delete(n1, p1)
require.NoError(t, err)
keyS, err = cstore.List()
require.NoError(t, err)
assert.Equal(t, 1, len(keyS))
_, err = cstore.Get(n1)
assert.Error(t, err)
// create an offline key
o1 := "offline"
priv1 := crypto.GenPrivKeyEd25519()
pub1 := priv1.PubKey()
i, err = cstore.CreateOffline(o1, pub1)
require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName())
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// delete the offline key
err = cstore.Delete(o1, "no")
require.NotNil(t, err)
err = cstore.Delete(o1, "yes")
require.NoError(t, err)
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 1, len(keyS))
}
// TestSignVerify does some detailed checks on how we sign and validate
// signatures
func TestSignVerify(t *testing.T) {
cstore := New(
dbm.NewMemDB(),
)
algo := Secp256k1
n1, n2, n3 := "some dude", "a dudette", "dude-ish"
p1, p2, p3 := "1234", "foobar", "foobar"
// create two users and get their info
i1, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err)
i2, _, err := cstore.CreateMnemonic(n2, English, p2, algo)
require.Nil(t, err)
// Import a public key
armor, err := cstore.ExportPubKey(n2)
require.Nil(t, err)
cstore.ImportPubKey(n3, armor)
i3, err := cstore.Get(n3)
require.NoError(t, err)
require.Equal(t, i3.GetName(), n3)
// let's try to sign some messages
d1 := []byte("my first message")
d2 := []byte("some other important info!")
d3 := []byte("feels like I forgot something...")
// try signing both data with both ..
s11, pub1, err := cstore.Sign(n1, p1, d1)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s12, pub1, err := cstore.Sign(n1, p1, d2)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s21, pub2, err := cstore.Sign(n2, p2, d1)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
s22, pub2, err := cstore.Sign(n2, p2, d2)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
// let's try to validate and make sure it only works when everything is proper
cases := []struct {
key crypto.PubKey
data []byte
sig crypto.Signature
valid bool
}{
// proper matches
{i1.GetPubKey(), d1, s11, true},
// change data, pubkey, or signature leads to fail
{i1.GetPubKey(), d2, s11, false},
{i2.GetPubKey(), d1, s11, false},
{i1.GetPubKey(), d1, s21, false},
// make sure other successes
{i1.GetPubKey(), d2, s12, true},
{i2.GetPubKey(), d1, s21, true},
{i2.GetPubKey(), d2, s22, true},
}
for i, tc := range cases {
valid := tc.key.VerifyBytes(tc.data, tc.sig)
assert.Equal(t, tc.valid, valid, "%d", i)
}
// Now try to sign data with a secret-less key
_, _, err = cstore.Sign(n3, p3, d3)
assert.NotNil(t, err)
}
func assertPassword(t *testing.T, cstore Keybase, name, pass, badpass string) {
err := cstore.Update(name, badpass, pass)
assert.NotNil(t, err)
err = cstore.Update(name, pass, pass)
assert.Nil(t, err, "%+v", err)
}
// TestExportImport tests exporting and importing
func TestExportImport(t *testing.T) {
// make the storage with reasonable defaults
db := dbm.NewMemDB()
cstore := New(
db,
)
info, _, err := cstore.CreateMnemonic("john", English, "secretcpw", Secp256k1)
assert.NoError(t, err)
assert.Equal(t, info.GetName(), "john")
john, err := cstore.Get("john")
assert.NoError(t, err)
assert.Equal(t, info.GetName(), "john")
johnAddr := info.GetPubKey().Address()
armor, err := cstore.Export("john")
assert.NoError(t, err)
err = cstore.Import("john2", armor)
assert.NoError(t, err)
john2, err := cstore.Get("john2")
assert.NoError(t, err)
assert.Equal(t, john.GetPubKey().Address(), johnAddr)
assert.Equal(t, john.GetName(), "john")
assert.Equal(t, john, john2)
}
//
func TestExportImportPubKey(t *testing.T) {
// make the storage with reasonable defaults
db := dbm.NewMemDB()
cstore := New(
db,
)
// CreateMnemonic a private-public key pair and ensure consistency
notPasswd := "n9y25ah7"
info, _, err := cstore.CreateMnemonic("john", English, notPasswd, Secp256k1)
assert.Nil(t, err)
assert.NotEqual(t, info, "")
assert.Equal(t, info.GetName(), "john")
addr := info.GetPubKey().Address()
john, err := cstore.Get("john")
assert.NoError(t, err)
assert.Equal(t, john.GetName(), "john")
assert.Equal(t, john.GetPubKey().Address(), addr)
// Export the public key only
armor, err := cstore.ExportPubKey("john")
assert.NoError(t, err)
// Import it under a different name
err = cstore.ImportPubKey("john-pubkey-only", armor)
assert.NoError(t, err)
// Ensure consistency
john2, err := cstore.Get("john-pubkey-only")
assert.NoError(t, err)
// Compare the public keys
assert.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
// Ensure the original key hasn't changed
john, err = cstore.Get("john")
assert.NoError(t, err)
assert.Equal(t, john.GetPubKey().Address(), addr)
assert.Equal(t, john.GetName(), "john")
// Ensure keys cannot be overwritten
err = cstore.ImportPubKey("john-pubkey-only", armor)
assert.NotNil(t, err)
}
// TestAdvancedKeyManagement verifies update, import, export functionality
func TestAdvancedKeyManagement(t *testing.T) {
// make the storage with reasonable defaults
cstore := New(
dbm.NewMemDB(),
)
algo := Secp256k1
n1, n2 := "old-name", "new name"
p1, p2 := "1234", "foobar"
// make sure key works with initial password
_, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
assertPassword(t, cstore, n1, p1, p2)
// update password requires the existing password
err = cstore.Update(n1, "jkkgkg", p2)
assert.NotNil(t, err)
assertPassword(t, cstore, n1, p1, p2)
// then it changes the password when correct
err = cstore.Update(n1, p1, p2)
assert.NoError(t, err)
// p2 is now the proper one!
assertPassword(t, cstore, n1, p2, p1)
// exporting requires the proper name and passphrase
_, err = cstore.Export(n1 + ".notreal")
assert.NotNil(t, err)
_, err = cstore.Export(" " + n1)
assert.NotNil(t, err)
_, err = cstore.Export(n1 + " ")
assert.NotNil(t, err)
_, err = cstore.Export("")
assert.NotNil(t, err)
exported, err := cstore.Export(n1)
require.Nil(t, err, "%+v", err)
// import succeeds
err = cstore.Import(n2, exported)
assert.NoError(t, err)
// second import fails
err = cstore.Import(n2, exported)
assert.NotNil(t, err)
}
// TestSeedPhrase verifies restoring from a seed phrase
func TestSeedPhrase(t *testing.T) {
// make the storage with reasonable defaults
cstore := New(
dbm.NewMemDB(),
)
algo := Secp256k1
n1, n2 := "lost-key", "found-again"
p1, p2 := "1234", "foobar"
// make sure key works with initial password
info, mnemonic, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
assert.Equal(t, n1, info.GetName())
assert.NotEmpty(t, mnemonic)
// now, let us delete this key
err = cstore.Delete(n1, p1)
require.Nil(t, err, "%+v", err)
_, err = cstore.Get(n1)
require.NotNil(t, err)
// let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, 0)
newInfo, err := cstore.Derive(n2, mnemonic, p2, params)
require.NoError(t, err)
assert.Equal(t, n2, newInfo.GetName())
assert.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
assert.Equal(t, info.GetPubKey(), newInfo.GetPubKey())
}
func ExampleNew() {
// Select the encryption and storage for your cryptostore
cstore := New(
dbm.NewMemDB(),
)
sec := Secp256k1
// Add keys and see they return in alphabetical order
bob, _, err := cstore.CreateMnemonic("Bob", English, "friend", sec)
if err != nil {
// this should never happen
fmt.Println(err)
} else {
// return info here just like in List
fmt.Println(bob.GetName())
}
cstore.CreateMnemonic("Alice", English, "secret", sec)
cstore.CreateMnemonic("Carl", English, "mitm", sec)
info, _ := cstore.List()
for _, i := range info {
fmt.Println(i.GetName())
}
// We need to use passphrase to generate a signature
tx := []byte("deadbeef")
sig, pub, err := cstore.Sign("Bob", "friend", tx)
if err != nil {
fmt.Println("don't accept real passphrase")
}
// and we can validate the signature with publicly available info
binfo, _ := cstore.Get("Bob")
if !binfo.GetPubKey().Equals(bob.GetPubKey()) {
fmt.Println("Get and Create return different keys")
}
if pub.Equals(binfo.GetPubKey()) {
fmt.Println("signed by Bob")
}
if !pub.VerifyBytes(tx, sig) {
fmt.Println("invalid signature")
}
// Output:
// Bob
// Alice
// Bob
// Carl
// signed by Bob
}

12
crypto/keys/keys.go Normal file
View File

@ -0,0 +1,12 @@
package keys
// SigningAlgo defines an algorithm to derive key-pairs which can be used for cryptographic signing.
type SigningAlgo string
const (
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
Secp256k1 = SigningAlgo("secp256k1")
// Ed25519 represents the Ed25519 signature system.
// It is currently not supported for end-user keys (wallets/ledgers).
Ed25519 = SigningAlgo("ed25519")
)

2
crypto/keys/keys.toml Normal file
View File

@ -0,0 +1,2 @@
output = "text"
keydir = ".mykeys"

115
crypto/keys/mintkey.go Normal file
View File

@ -0,0 +1,115 @@
package keys
import (
"encoding/hex"
"fmt"
cmn "github.com/tendermint/tmlibs/common"
"github.com/cosmos/cosmos-sdk/crypto/keys/bcrypt"
"github.com/tendermint/tendermint/crypto"
)
const (
blockTypePrivKey = "TENDERMINT PRIVATE KEY"
blockTypeKeyInfo = "TENDERMINT KEY INFO"
blockTypePubKey = "TENDERMINT PUBLIC KEY"
)
func armorInfoBytes(bz []byte) string {
return armorBytes(bz, blockTypeKeyInfo)
}
func armorPubKeyBytes(bz []byte) string {
return armorBytes(bz, blockTypePubKey)
}
func armorBytes(bz []byte, blockType string) string {
header := map[string]string{
"type": "Info",
"version": "0.0.0",
}
return crypto.EncodeArmor(blockType, header, bz)
}
func unarmorInfoBytes(armorStr string) (bz []byte, err error) {
return unarmorBytes(armorStr, blockTypeKeyInfo)
}
func unarmorPubKeyBytes(armorStr string) (bz []byte, err error) {
return unarmorBytes(armorStr, blockTypePubKey)
}
func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
bType, header, bz, err := crypto.DecodeArmor(armorStr)
if err != nil {
return
}
if bType != blockType {
err = fmt.Errorf("Unrecognized armor type %q, expected: %q", bType, blockType)
return
}
if header["version"] != "0.0.0" {
err = fmt.Errorf("Unrecognized version: %v", header["version"])
return
}
return
}
func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
header := map[string]string{
"kdf": "bcrypt",
"salt": fmt.Sprintf("%X", saltBytes),
}
armorStr := crypto.EncodeArmor(blockTypePrivKey, header, encBytes)
return armorStr
}
func unarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) {
var privKey crypto.PrivKey
blockType, header, encBytes, err := crypto.DecodeArmor(armorStr)
if err != nil {
return privKey, err
}
if blockType != blockTypePrivKey {
return privKey, fmt.Errorf("Unrecognized armor type: %v", blockType)
}
if header["kdf"] != "bcrypt" {
return privKey, fmt.Errorf("Unrecognized KDF type: %v", header["KDF"])
}
if header["salt"] == "" {
return privKey, fmt.Errorf("Missing salt bytes")
}
saltBytes, err := hex.DecodeString(header["salt"])
if err != nil {
return privKey, fmt.Errorf("Error decoding salt: %v", err.Error())
}
privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase)
return privKey, err
}
func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) {
saltBytes = crypto.CRandBytes(16)
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016)
if err != nil {
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
}
key = crypto.Sha256(key) // Get 32 bytes
privKeyBytes := privKey.Bytes()
return saltBytes, crypto.EncryptSymmetric(privKeyBytes, key)
}
func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016)
if err != nil {
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
}
key = crypto.Sha256(key) // Get 32 bytes
privKeyBytes, err := crypto.DecryptSymmetric(encBytes, key)
if err != nil {
return privKey, err
}
privKey, err = crypto.PrivKeyFromBytes(privKeyBytes)
return privKey, err
}

144
crypto/keys/types.go Normal file
View File

@ -0,0 +1,144 @@
package keys
import (
ccrypto "github.com/cosmos/cosmos-sdk/crypto"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
)
// Keybase exposes operations on a generic keystore
type Keybase interface {
// CRUD on the keystore
List() ([]Info, error)
Get(name string) (Info, error)
Delete(name, passphrase string) error
// Sign some bytes, looking up the private key to use
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error)
// CreateMnemonic creates a new mnemonic, and derives a hierarchical deterministic
// key from that.
CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)
// CreateFundraiserKey takes a mnemonic and derives, a password
CreateFundraiserKey(name, mnemonic, passwd string) (info Info, err error)
// Derive derives a key from the passed mnemonic using a BIP44 path.
Derive(name, mnemonic, passwd string, params hd.BIP44Params) (Info, error)
// Create, store, and return a new Ledger key reference
CreateLedger(name string, path ccrypto.DerivationPath, algo SigningAlgo) (info Info, err error)
// Create, store, and return a new offline key reference
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error)
// The following operations will *only* work on locally-stored keys
Update(name, oldpass, newpass string) error
Import(name string, armor string) (err error)
ImportPubKey(name string, armor string) (err error)
Export(name string) (armor string, err error)
ExportPubKey(name string) (armor string, err error)
}
// Info is the publicly exposed information about a keypair
type Info interface {
// Human-readable type for key listing
GetType() string
// Name of the key
GetName() string
// Public key
GetPubKey() crypto.PubKey
}
var _ Info = &localInfo{}
var _ Info = &ledgerInfo{}
var _ Info = &offlineInfo{}
// localInfo is the public information about a locally stored key
type localInfo struct {
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
PrivKeyArmor string `json:"privkey.armor"`
}
func newLocalInfo(name string, pub crypto.PubKey, privArmor string) Info {
return &localInfo{
Name: name,
PubKey: pub,
PrivKeyArmor: privArmor,
}
}
func (i localInfo) GetType() string {
return "local"
}
func (i localInfo) GetName() string {
return i.Name
}
func (i localInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
// ledgerInfo is the public information about a Ledger key
type ledgerInfo struct {
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
Path ccrypto.DerivationPath `json:"path"`
}
func newLedgerInfo(name string, pub crypto.PubKey, path ccrypto.DerivationPath) Info {
return &ledgerInfo{
Name: name,
PubKey: pub,
Path: path,
}
}
func (i ledgerInfo) GetType() string {
return "ledger"
}
func (i ledgerInfo) GetName() string {
return i.Name
}
func (i ledgerInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
// offlineInfo is the public information about an offline key
type offlineInfo struct {
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
}
func newOfflineInfo(name string, pub crypto.PubKey) Info {
return &offlineInfo{
Name: name,
PubKey: pub,
}
}
func (i offlineInfo) GetType() string {
return "offline"
}
func (i offlineInfo) GetName() string {
return i.Name
}
func (i offlineInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
// encoding info
func writeInfo(i Info) []byte {
return cdc.MustMarshalBinary(i)
}
// decoding info
func readInfo(bz []byte) (info Info, err error) {
err = cdc.UnmarshalBinary(bz, &info)
return
}

19
crypto/keys/wire.go Normal file
View File

@ -0,0 +1,19 @@
package keys
import (
ccrypto "github.com/cosmos/cosmos-sdk/crypto"
amino "github.com/tendermint/go-amino"
tcrypto "github.com/tendermint/tendermint/crypto"
)
var cdc = amino.NewCodec()
func init() {
tcrypto.RegisterAmino(cdc)
cdc.RegisterInterface((*Info)(nil), nil)
cdc.RegisterConcrete(ccrypto.PrivKeyLedgerSecp256k1{},
"tendermint/PrivKeyLedgerSecp256k1", nil)
cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil)
cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil)
cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil)
}

19
crypto/ledger_common.go Normal file
View File

@ -0,0 +1,19 @@
package crypto
import (
ledger "github.com/zondax/ledger-goclient"
)
var device *ledger.Ledger
// Ledger derivation path
type DerivationPath = []uint32
// getLedger gets a copy of the device, and caches it
func getLedger() (*ledger.Ledger, error) {
var err error
if device == nil {
device, err = ledger.FindLedger()
}
return device, err
}

126
crypto/ledger_secp256k1.go Normal file
View File

@ -0,0 +1,126 @@
package crypto
import (
"fmt"
secp256k1 "github.com/btcsuite/btcd/btcec"
ledger "github.com/zondax/ledger-goclient"
tcrypto "github.com/tendermint/tendermint/crypto"
)
func pubkeyLedgerSecp256k1(device *ledger.Ledger, path DerivationPath) (pub tcrypto.PubKey, err error) {
key, err := device.GetPublicKeySECP256K1(path)
if err != nil {
return nil, fmt.Errorf("error fetching public key: %v", err)
}
var p tcrypto.PubKeySecp256k1
// Reserialize in the 33-byte compressed format
cmp, err := secp256k1.ParsePubKey(key[:], secp256k1.S256())
copy(p[:], cmp.SerializeCompressed())
pub = p
return
}
func signLedgerSecp256k1(device *ledger.Ledger, path DerivationPath, msg []byte) (sig tcrypto.Signature, err error) {
bsig, err := device.SignSECP256K1(path, msg)
if err != nil {
return sig, err
}
sig = tcrypto.SignatureSecp256k1FromBytes(bsig)
return
}
// PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano
// we cache the PubKey from the first call to use it later
type PrivKeyLedgerSecp256k1 struct {
// PubKey should be private, but we want to encode it via go-amino
// so we can view the address later, even without having the ledger
// attached
CachedPubKey tcrypto.PubKey
Path DerivationPath
}
// NewPrivKeyLedgerSecp256k1 will generate a new key and store the
// public key for later use.
func NewPrivKeyLedgerSecp256k1(path DerivationPath) (tcrypto.PrivKey, error) {
var pk PrivKeyLedgerSecp256k1
pk.Path = path
// cache the pubkey for later use
pubKey, err := pk.getPubKey()
if err != nil {
return nil, err
}
pk.CachedPubKey = pubKey
return &pk, err
}
// ValidateKey allows us to verify the sanity of a key
// after loading it from disk
func (pk PrivKeyLedgerSecp256k1) ValidateKey() error {
// getPubKey will return an error if the ledger is not
pub, err := pk.getPubKey()
if err != nil {
return err
}
// verify this matches cached address
if !pub.Equals(pk.CachedPubKey) {
return fmt.Errorf("cached key does not match retrieved key")
}
return nil
}
// AssertIsPrivKeyInner fulfils PrivKey Interface
func (pk *PrivKeyLedgerSecp256k1) AssertIsPrivKeyInner() {}
// Bytes fulfils PrivKey Interface - but it stores the cached pubkey so we can verify
// the same key when we reconnect to a ledger
func (pk PrivKeyLedgerSecp256k1) Bytes() []byte {
return cdc.MustMarshalBinaryBare(pk)
}
// Sign calls the ledger and stores the PubKey for future use
//
// Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes,
// returning an error, so this should only trigger if the privkey is held
// in memory for a while before use.
func (pk PrivKeyLedgerSecp256k1) Sign(msg []byte) (tcrypto.Signature, error) {
dev, err := getLedger()
if err != nil {
return nil, err
}
sig, err := signLedgerSecp256k1(dev, pk.Path, msg)
if err != nil {
return nil, err
}
return sig, nil
}
// PubKey returns the stored PubKey
func (pk PrivKeyLedgerSecp256k1) PubKey() tcrypto.PubKey {
return pk.CachedPubKey
}
// getPubKey reads the pubkey the ledger itself
// since this involves IO, it may return an error, which is not exposed
// in the PubKey interface, so this function allows better error handling
func (pk PrivKeyLedgerSecp256k1) getPubKey() (key tcrypto.PubKey, err error) {
dev, err := getLedger()
if err != nil {
return key, fmt.Errorf("cannot connect to Ledger device - error: %v", err)
}
key, err = pubkeyLedgerSecp256k1(dev, pk.Path)
if err != nil {
return key, fmt.Errorf("please open Cosmos app on the Ledger device - error: %v", err)
}
return key, err
}
// Equals fulfils PrivKey Interface - makes sure both keys refer to the
// same
func (pk PrivKeyLedgerSecp256k1) Equals(other tcrypto.PrivKey) bool {
if ledger, ok := other.(*PrivKeyLedgerSecp256k1); ok {
return pk.CachedPubKey.Equals(ledger.CachedPubKey)
}
return false
}

65
crypto/ledger_test.go Normal file
View File

@ -0,0 +1,65 @@
package crypto
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
tcrypto "github.com/tendermint/tendermint/crypto"
)
func TestRealLedgerSecp256k1(t *testing.T) {
if os.Getenv("WITH_LEDGER") == "" {
t.Skip("Set WITH_LEDGER to run code on real ledger")
}
msg := []byte("kuhehfeohg")
path := DerivationPath{44, 60, 0, 0, 0}
priv, err := NewPrivKeyLedgerSecp256k1(path)
require.Nil(t, err, "%+v", err)
pub := priv.PubKey()
sig, err := priv.Sign(msg)
require.Nil(t, err)
valid := pub.VerifyBytes(msg, sig)
assert.True(t, valid)
// now, let's serialize the key and make sure it still works
bs := priv.Bytes()
priv2, err := tcrypto.PrivKeyFromBytes(bs)
require.Nil(t, err, "%+v", err)
// make sure we get the same pubkey when we load from disk
pub2 := priv2.PubKey()
require.Equal(t, pub, pub2)
// signing with the loaded key should match the original pubkey
sig, err = priv2.Sign(msg)
require.Nil(t, err)
valid = pub.VerifyBytes(msg, sig)
assert.True(t, valid)
// make sure pubkeys serialize properly as well
bs = pub.Bytes()
bpub, err := tcrypto.PubKeyFromBytes(bs)
require.NoError(t, err)
assert.Equal(t, pub, bpub)
}
// TestRealLedgerErrorHandling calls. These tests assume
// the ledger is not plugged in....
func TestRealLedgerErrorHandling(t *testing.T) {
if os.Getenv("WITH_LEDGER") != "" {
t.Skip("Skipping on WITH_LEDGER as it tests unplugged cases")
}
// first, try to generate a key, must return an error
// (no panic)
path := DerivationPath{44, 60, 0, 0, 0}
_, err := NewPrivKeyLedgerSecp256k1(path)
require.Error(t, err)
}

View File

@ -3,7 +3,7 @@ package app
import (
"encoding/json"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -1,7 +1,6 @@
package app
import (
"encoding/json"
"fmt"
"os"
"testing"
@ -16,8 +15,8 @@ import (
"github.com/cosmos/cosmos-sdk/x/stake"
gen "github.com/cosmos/cosmos-sdk/x/stake/types"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
)
@ -76,7 +75,7 @@ func TestGenesis(t *testing.T) {
bapp = NewBasecoinApp(logger, db)
// Initialize stake data with default genesis state
stakedata := gen.DefaultGenesisState()
genState, err := json.Marshal(stakedata)
genState, err := bapp.cdc.MarshalJSON(stakedata)
if err != nil {
panic(err)
}

View File

@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -3,7 +3,7 @@ package app
import (
"encoding/json"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -1,7 +1,6 @@
package app
import (
"encoding/json"
"os"
"testing"
@ -9,15 +8,41 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
)
func setGenesis(bapp *DemocoinApp, trend string, accs ...auth.BaseAccount) error {
genaccs := make([]*types.GenesisAccount, len(accs))
for i, acc := range accs {
genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, "foobart"})
}
genesisState := types.GenesisState{
Accounts: genaccs,
CoolGenesis: cool.Genesis{trend},
}
stateBytes, err := wire.MarshalJSONIndent(bapp.cdc, genesisState)
if err != nil {
return err
}
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
return nil
}
func TestGenesis(t *testing.T) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
@ -34,21 +59,8 @@ func TestGenesis(t *testing.T) {
}
acc := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
err = setGenesis(bapp, "ice-cold", baseAcc)
require.Nil(t, err)
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
// A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)

View File

@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -4,7 +4,7 @@ import (
"bytes"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
)
// Validator implements sdk.Validator

View File

@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/examples/democoin/mock"

View File

@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
@ -16,8 +16,9 @@ import (
)
var (
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
priv1 = crypto.GenPrivKeyEd25519()
pubKey = priv1.PubKey()
addr1 = pubKey.Address()
quizMsg1 = MsgQuiz{
Sender: addr1,

View File

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/store"

View File

@ -41,13 +41,13 @@ func (msg MsgSetTrend) String() string {
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg MsgSetTrend) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
return sdk.ErrUnknownAddress(msg.Sender.String()).TraceSDK("")
}
if strings.Contains(msg.Cool, "hot") {
return sdk.ErrUnauthorized("").Trace("hot is not cool")
return sdk.ErrUnauthorized("").TraceSDK("hot is not cool")
}
if strings.Contains(msg.Cool, "warm") {
return sdk.ErrUnauthorized("").Trace("warm is not very cool")
return sdk.ErrUnauthorized("").TraceSDK("warm is not very cool")
}
return nil
}
@ -91,7 +91,7 @@ func (msg MsgQuiz) String() string {
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg MsgQuiz) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
return sdk.ErrUnknownAddress(msg.Sender.String()).TraceSDK("")
}
return nil
}

View File

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/examples/democoin/mock"
@ -89,7 +89,7 @@ func handleSeqOracle(ctx sdk.Context, key sdk.StoreKey, o seqOracle) sdk.Error {
seq := getSequence(ctx, key)
if seq != o.Seq {
return sdk.NewError(sdk.CodespaceUndefined, 1, "")
return sdk.NewError(sdk.CodespaceRoot, 1, "")
}
bz := wire.NewCodec().MustMarshalBinary(seq + 1)
@ -119,7 +119,7 @@ func TestOracle(t *testing.T) {
ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz})
ork := NewKeeper(sdk.NewPrefixStoreGetter(key, []byte("oracle")), cdc, valset, sdk.NewRat(2, 3), 100)
h := seqHandler(ork, key, sdk.CodespaceUndefined)
h := seqHandler(ork, key, sdk.CodespaceRoot)
// Nonmock.Validator signed, transaction failed
msg := Msg{seqOracle{0, 0}, []byte("randomguy")}

View File

@ -11,8 +11,8 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth/mock"
"github.com/cosmos/cosmos-sdk/x/bank"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
)
var (

View File

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk/types"

View File

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"

View File

@ -6,7 +6,7 @@ import (
"strconv"
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
)
// generate the mine message

View File

@ -8,7 +8,7 @@ import (
"math"
"strconv"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)

View File

@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"

View File

@ -1,7 +1,7 @@
package simplestake
import (
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"

View File

@ -8,8 +8,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"

View File

@ -3,7 +3,7 @@ package simplestake
import (
"encoding/json"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)

View File

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)

View File

@ -1,6 +1,6 @@
package simplestake
import crypto "github.com/tendermint/go-crypto"
import "github.com/tendermint/tendermint/crypto"
type bondInfo struct {
PubKey crypto.PubKey

Binary file not shown.

View File

@ -7,7 +7,7 @@ import (
"github.com/spf13/viper"
"github.com/tendermint/abci/server"
"github.com/tendermint/tendermint/abci/server"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -4,7 +4,7 @@ import (
"encoding/json"
"path/filepath"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"

View File

@ -14,9 +14,9 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/words"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/tendermint/tendermint/crypto"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
pvm "github.com/tendermint/tendermint/privval"
@ -459,22 +459,17 @@ func SimpleAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState j
func GenerateCoinKey() (sdk.Address, string, error) {
// construct an in-memory key store
codec, err := words.LoadCodec("english")
if err != nil {
return nil, "", err
}
keybase := keys.New(
dbm.NewMemDB(),
codec,
)
// generate a private key, with recovery phrase
info, secret, err := keybase.Create("name", "pass", keys.AlgoEd25519)
info, secret, err := keybase.CreateMnemonic("name", keys.English, "pass", keys.Secp256k1)
if err != nil {
return nil, "", err
}
addr := info.PubKey.Address()
return addr, secret, nil
addr := info.GetPubKey().Address()
return sdk.Address(addr), secret, nil
}
// GenerateSaveCoinKey returns the address of a public key, along with the secret
@ -496,10 +491,10 @@ func GenerateSaveCoinKey(clientRoot, keyName, keyPass string, overwrite bool) (s
}
// generate a private key, with recovery phrase
info, secret, err := keybase.Create(keyName, keyPass, keys.AlgoEd25519)
info, secret, err := keybase.CreateMnemonic(keyName, keys.English, keyPass, keys.Secp256k1)
if err != nil {
return nil, "", err
}
addr := info.PubKey.Address()
return addr, secret, nil
addr := info.GetPubKey().Address()
return sdk.Address(addr), secret, nil
}

View File

@ -5,8 +5,8 @@ import (
"fmt"
"path/filepath"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"

View File

@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
)
// TestInitApp makes sure we can initialize this thing without an error

View File

@ -5,7 +5,7 @@ import (
"io/ioutil"
"os"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
)

View File

@ -5,7 +5,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/abci/server"
"github.com/tendermint/tendermint/abci/server"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/node"
@ -88,6 +88,7 @@ func startInProcess(ctx *Context, appCreator AppCreator) error {
proxy.NewLocalClientCreator(app),
node.DefaultGenesisDocProviderFunc(cfg),
node.DefaultDBProvider,
node.DefaultMetricsProvider,
ctx.Logger.With("module", "node"))
if err != nil {
return err

View File

@ -11,7 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/server/mock"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/tendermint/abci/server"
"github.com/tendermint/tendermint/abci/server"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tmlibs/log"
)

View File

@ -4,8 +4,9 @@ import (
"fmt"
"sync"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-amino"
"github.com/tendermint/iavl"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
@ -171,7 +172,13 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
break
}
res.Value = value
res.Proof = proof.Bytes()
cdc := amino.NewCodec()
p, err := cdc.MarshalBinary(proof)
if err != nil {
res.Log = err.Error()
break
}
res.Proof = p
} else {
_, res.Value = tree.GetVersioned(key, height)
}

View File

@ -5,8 +5,8 @@ import (
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/iavl"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"

View File

@ -6,7 +6,7 @@ import (
"golang.org/x/crypto/ripemd160"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/merkle"

View File

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/merkle"

View File

@ -1,6 +1,6 @@
package types
import abci "github.com/tendermint/abci/types"
import abci "github.com/tendermint/tendermint/abci/types"
// initialize application state at genesis
type InitChainer func(ctx Context, req abci.RequestInitChain) abci.ResponseInitChain

View File

@ -5,7 +5,7 @@ import (
"errors"
"fmt"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tmlibs/bech32"
cmn "github.com/tendermint/tmlibs/common"
)

View File

@ -6,7 +6,7 @@ import (
"github.com/golang/protobuf/proto"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
)

View File

@ -11,7 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
)
type MockLogger struct {

View File

@ -5,7 +5,7 @@ import (
cmn "github.com/tendermint/tmlibs/common"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
)
// ABCICodeType - combined codetype / codespace
@ -147,49 +147,80 @@ func ErrMemoTooLarge(msg string) Error {
//----------------------------------------
// Error & sdkError
type cmnError = cmn.Error
// sdk Error type
type Error interface {
Error() string
// Implements cmn.Error
// Error() string
// Stacktrace() cmn.Error
// Trace(offset int, format string, args ...interface{}) cmn.Error
// Data() interface{}
cmnError
// convenience
TraceSDK(format string, args ...interface{}) Error
// set codespace
WithDefaultCodespace(CodespaceType) Error
Code() CodeType
Codespace() CodespaceType
ABCILog() string
ABCICode() ABCICodeType
WithDefaultCodespace(codespace CodespaceType) Error
Trace(msg string) Error
T() interface{}
Result() Result
QueryResult() abci.ResponseQuery
}
// NewError - create an error
func NewError(codespace CodespaceType, code CodeType, msg string) Error {
return newError(codespace, code, msg)
// NewError - create an error.
func NewError(codespace CodespaceType, code CodeType, format string, args ...interface{}) Error {
return newError(codespace, code, format, args...)
}
func newErrorWithRootCodespace(code CodeType, msg string) *sdkError {
return newError(CodespaceRoot, code, msg)
func newErrorWithRootCodespace(code CodeType, format string, args ...interface{}) *sdkError {
return newError(CodespaceRoot, code, format, args...)
}
func newError(codespace CodespaceType, code CodeType, msg string) *sdkError {
if msg == "" {
msg = CodeToDefaultMsg(code)
func newError(codespace CodespaceType, code CodeType, format string, args ...interface{}) *sdkError {
if format == "" {
format = CodeToDefaultMsg(code)
}
return &sdkError{
codespace: codespace,
code: code,
err: cmn.NewErrorWithT(code, msg),
cmnError: cmn.NewError(format, args...),
}
}
type sdkError struct {
codespace CodespaceType
code CodeType
err cmn.Error
cmnError
}
// Implements Error.
func (err *sdkError) WithDefaultCodespace(cs CodespaceType) Error {
codespace := err.codespace
if codespace == CodespaceUndefined {
codespace = cs
}
return &sdkError{
codespace: cs,
code: err.code,
cmnError: err.cmnError,
}
}
// Implements ABCIError.
func (err *sdkError) TraceSDK(format string, args ...interface{}) Error {
err.Trace(1, format, args...)
return err
}
// Implements ABCIError.
// Overrides err.Error.Error().
func (err *sdkError) Error() string {
return fmt.Sprintf("error{%d:%d,%#v}", err.codespace, err.code, err.err)
return fmt.Sprintf("Error{%d:%d,%#v}", err.codespace, err.code, err.cmnError)
}
// Implements ABCIError.
@ -215,33 +246,7 @@ Code: %v
ABCICode: %v
Error: %#v
=== /ABCI Log ===
`, err.codespace, err.code, err.ABCICode(), err.err)
}
// Add tracing information with msg.
func (err *sdkError) Trace(msg string) Error {
return &sdkError{
codespace: err.codespace,
code: err.code,
err: err.err.Trace(msg),
}
}
// Implements Error.
func (err *sdkError) WithDefaultCodespace(cs CodespaceType) Error {
codespace := err.codespace
if codespace == CodespaceUndefined {
codespace = cs
}
return &sdkError{
codespace: codespace,
code: err.code,
err: err.err,
}
}
func (err *sdkError) T() interface{} {
return err.err.T()
`, err.codespace, err.code, err.ABCICode(), err.cmnError)
}
func (err *sdkError) Result() Result {

View File

@ -1,6 +1,8 @@
package types
import (
"encoding/json"
"math/big"
)
@ -46,14 +48,25 @@ func unmarshalAmino(i *big.Int, text string) (err error) {
return i.UnmarshalText([]byte(text))
}
// MarshalJSON for custom encodig scheme
// MarshalJSON for custom encoding scheme
// Must be encoded as a string for JSON precision
func marshalJSON(i *big.Int) ([]byte, error) {
return i.MarshalText()
text, err := i.MarshalText()
if err != nil {
return nil, err
}
return json.Marshal(string(text))
}
// UnmarshalJSON for custom decoding scheme
// Must be encoded as a string for JSON precision
func unmarshalJSON(i *big.Int, bz []byte) error {
return i.UnmarshalText(bz)
var text string
err := json.Unmarshal(bz, &text)
if err != nil {
return err
}
return i.UnmarshalText([]byte(text))
}
// Int wraps integer with 256 bit range bound

View File

@ -9,7 +9,7 @@ import (
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"

View File

@ -1,8 +1,8 @@
package types
import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
)

View File

@ -3,7 +3,7 @@ package types
import (
"fmt"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
)

View File

@ -4,8 +4,8 @@ import (
"bytes"
"encoding/json"
"github.com/tendermint/go-amino"
"github.com/tendermint/go-crypto"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
)
// amino codec to marshal/unmarshal

View File

@ -5,7 +5,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
)
// Account is a standard account using a sequence number for replay protection

View File

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"

View File

@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -69,7 +69,11 @@ func newTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums
sigs := make([]StdSignature, len(privs))
for i, priv := range privs {
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "")
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), AccountNumber: accNums[i], Sequence: seqs[i]}
sig, err := priv.Sign(signBytes)
if err != nil {
panic(err)
}
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
}
tx := NewStdTx(msgs, fee, sigs, "")
return tx
@ -79,7 +83,11 @@ func newTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey,
sigs := make([]StdSignature, len(privs))
for i, priv := range privs {
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo)
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), AccountNumber: accNums[i], Sequence: seqs[i]}
sig, err := priv.Sign(signBytes)
if err != nil {
panic(err)
}
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
}
tx := NewStdTx(msgs, fee, sigs, memo)
return tx
@ -89,7 +97,11 @@ func newTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey,
func newTestTxWithSignBytes(msgs []sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee, signBytes []byte, memo string) sdk.Tx {
sigs := make([]StdSignature, len(privs))
for i, priv := range privs {
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), AccountNumber: accNums[i], Sequence: seqs[i]}
sig, err := priv.Sign(signBytes)
if err != nil {
panic(err)
}
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
}
tx := NewStdTx(msgs, fee, sigs, memo)
return tx
@ -356,7 +368,7 @@ func TestAnteHandlerMemoGas(t *testing.T) {
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeOutOfGas)
// tx with memo doesn't have enough gas
fee = NewStdFee(1001, sdk.NewCoin("atom", 0))
fee = NewStdFee(801, sdk.NewCoin("atom", 0))
tx = newTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, "abcininasidniandsinasindiansdiansdinaisndiasndiadninsd")
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeOutOfGas)

View File

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk/types"

Some files were not shown because too many files have changed in this diff Show More