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:
parent
ac3adff1e8
commit
59aadf42aa
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
36
Gopkg.toml
36
Gopkg.toml
|
@ -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]]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ func NewCoreContextFromViper() CoreContext {
|
|||
Client: rpc,
|
||||
Decoder: nil,
|
||||
AccountStore: "acc",
|
||||
UseLedger: viper.GetBool(client.FlagUseLedger),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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))
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
)
|
|
@ -0,0 +1,2 @@
|
|||
output = "text"
|
||||
keydir = ".mykeys"
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")}
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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.
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
19
types/int.go
19
types/int.go
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue