diff --git a/CHANGELOG.md b/CHANGELOG.md index a88cf406df..7fd480a797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## 0.31.2 + +BREAKING CHANGES + +* SDK + * [\#3592](https://github.com/cosmos/cosmos-sdk/issues/3592) Drop deprecated keybase implementation's + New constructor in favor of a new + crypto/keys.New(string, string) implementation that + returns a lazy keybase instance. Remove client.MockKeyBase, + superseded by crypto/keys.NewInMemory() + +IMPROVEMENTS + +* SDK + * [\#3604](https://github.com/cosmos/cosmos-sdk/pulls/3604) Improve SDK funds related error messages and allow for unicode in + JSON ABCI log. + +* Tendermint + * [\#3563](https://github.com/cosmos/cosmos-sdk/3563) Update to Tendermint version `0.30.0-rc0` + + +BUG FIXES + +* Gaia + * [\#3585] Fix setting the tx hash in `NewResponseFormatBroadcastTxCommit`. + * [\#3585] Return an empty `TxResponse` when Tendermint returns an empty + `ResultBroadcastTx`. + +* SDK + * [\#3582](https://github.com/cosmos/cosmos-sdk/pull/3582) Running `make test_unit` was failing due to a missing tag + * [\#3617](https://github.com/cosmos/cosmos-sdk/pull/3582) Fix fee comparison when the required fees does not contain any denom + present in the tx fees. + ## 0.31.0 BREAKING CHANGES diff --git a/Makefile b/Makefile index de913e82ca..b8cdbbed85 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ test_ledger: @go test -v `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger' test_unit: - @VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION) -tags='test_ledger_mock' + @VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION) -tags='ledger test_ledger_mock' test_race: @VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION) diff --git a/PENDING.md b/PENDING.md index 164614789d..861cce4314 100644 --- a/PENDING.md +++ b/PENDING.md @@ -48,4 +48,4 @@ BUG FIXES * SDK -* Tendermint \ No newline at end of file +* Tendermint diff --git a/client/keys.go b/client/keys.go deleted file mode 100644 index 3e0b61d461..0000000000 --- a/client/keys.go +++ /dev/null @@ -1,14 +0,0 @@ -package client - -import ( - dbm "github.com/tendermint/tendermint/libs/db" - - "github.com/cosmos/cosmos-sdk/crypto/keys" -) - -// MockKeyBase generates an in-memory keybase that will be discarded -// useful for --dry-run to generate a seed phrase without -// storing the key -func MockKeyBase() keys.Keybase { - return keys.New(dbm.NewMemDB()) -} diff --git a/client/keys/add.go b/client/keys/add.go index d9dfe985fc..9b437a340b 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -77,7 +77,7 @@ the flag --nosort is set. 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") + cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation") return cmd } @@ -104,7 +104,7 @@ func runAddCmd(_ *cobra.Command, args []string) error { if viper.GetBool(flagDryRun) { // we throw this away, so don't enforce args, // we want to get a new random seed phrase quickly - kb = client.MockKeyBase() + kb = keys.NewInMemory() encryptPassword = app.DefaultKeyPass } else { kb, err = NewKeyBaseFromHomeFlag() @@ -309,7 +309,7 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error { // function to just create a new seed to display in the UI before actually persisting it in the keybase func generateMnemonic(algo keys.SigningAlgo) string { - kb := client.MockKeyBase() + kb := keys.NewInMemory() pass := app.DefaultKeyPass name := "inmemorykey" _, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo) diff --git a/client/keys/add_ledger_test.go b/client/keys/add_ledger_test.go new file mode 100644 index 0000000000..d830fdfd7e --- /dev/null +++ b/client/keys/add_ledger_test.go @@ -0,0 +1,55 @@ +//+build ledger,test_ledger_mock + +package keys + +import ( + "bufio" + "strings" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/tests" + + "github.com/cosmos/cosmos-sdk/client" + + "github.com/stretchr/testify/assert" +) + +func Test_runAddCmdLedger(t *testing.T) { + cmd := addKeyCommand() + assert.NotNil(t, cmd) + + // Prepare a keybase + kbHome, kbCleanUp := tests.NewTestCaseDir(t) + assert.NotNil(t, kbHome) + defer kbCleanUp() + viper.Set(cli.HomeFlag, kbHome) + viper.Set(client.FlagUseLedger, true) + + /// Test Text + viper.Set(cli.OutputFlag, OutputFormatText) + // Now enter password + cleanUp1 := client.OverrideStdin(bufio.NewReader(strings.NewReader("test1234\ntest1234\n"))) + defer cleanUp1() + err := runAddCmd(cmd, []string{"keyname1"}) + assert.NoError(t, err) + + // Now check that it has been stored properly + kb, err := NewKeyBaseFromHomeFlag() + assert.NoError(t, err) + assert.NotNil(t, kb) + key1, err := kb.Get("keyname1") + assert.NoError(t, err) + assert.NotNil(t, key1) + + assert.Equal(t, "keyname1", key1.GetName()) + assert.Equal(t, keys.TypeLedger, key1.GetType()) + assert.Equal(t, + "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", + sdk.MustBech32ifyAccPub(key1.GetPubKey())) +} diff --git a/client/keys/add_test.go b/client/keys/add_test.go index f20ba27b1e..bca21393af 100644 --- a/client/keys/add_test.go +++ b/client/keys/add_test.go @@ -23,10 +23,6 @@ func Test_runAddCmdBasic(t *testing.T) { cmd := addKeyCommand() assert.NotNil(t, cmd) - // Missing input (enter password) - err := runAddCmd(cmd, []string{"keyname"}) - assert.EqualError(t, err, "EOF") - // Prepare a keybase kbHome, kbCleanUp := tests.NewTestCaseDir(t) assert.NotNil(t, kbHome) @@ -38,7 +34,7 @@ func Test_runAddCmdBasic(t *testing.T) { // Now enter password cleanUp1 := client.OverrideStdin(bufio.NewReader(strings.NewReader("test1234\ntest1234\n"))) defer cleanUp1() - err = runAddCmd(cmd, []string{"keyname1"}) + err := runAddCmd(cmd, []string{"keyname1"}) assert.NoError(t, err) /// Test Text - Replace? >> FAIL diff --git a/client/keys/keys/keys.db/LOCK b/client/keys/keys/keys.db/LOCK deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/client/keys/utils.go b/client/keys/utils.go index d772b81514..d5c2f2f55b 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -14,11 +14,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// KeyDBName is the directory under root where we store the keys +// available output formats. const ( - KeyDBName = "keys" OutputFormatText = "text" OutputFormatJSON = "json" + + // defaultKeyDBName is the client's subdirectory where keys are stored. + defaultKeyDBName = "keys" ) type bechKeyOutFn func(keyInfo keys.Info) (KeyOutput, error) @@ -87,7 +89,7 @@ func NewKeyBaseFromDir(rootDir string) (keys.Keybase, error) { func NewInMemoryKeyBase() keys.Keybase { return keys.NewInMemory() } func getLazyKeyBaseFromDir(rootDir string) (keys.Keybase, error) { - return keys.NewLazyKeybase(KeyDBName, filepath.Join(rootDir, "keys")), nil + return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys")), nil } // create a list of KeyOutput in bech32 format diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 9e6f08fe9a..ae126ef1d5 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -682,7 +682,7 @@ func doTransferWithGas( ) (res *http.Response, body string, receiveAddr sdk.AccAddress) { // create receive address - kb := client.MockKeyBase() + kb := crkeys.NewInMemory() receiveInfo, _, err := kb.CreateMnemonic( "receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), @@ -724,7 +724,7 @@ func doTransferWithGasAccAuto( ) (res *http.Response, body string, receiveAddr sdk.AccAddress) { // create receive address - kb := client.MockKeyBase() + kb := crkeys.NewInMemory() receiveInfo, _, err := kb.CreateMnemonic( "receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), diff --git a/crypto/keys/codec.go b/crypto/keys/codec.go index f6c1a013da..737836f919 100644 --- a/crypto/keys/codec.go +++ b/crypto/keys/codec.go @@ -1,10 +1,9 @@ package keys import ( - amino "github.com/tendermint/go-amino" + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto/encoding/amino" - - ccrypto "github.com/cosmos/cosmos-sdk/crypto" ) var cdc = amino.NewCodec() @@ -12,8 +11,7 @@ var cdc = amino.NewCodec() func init() { cryptoAmino.RegisterAmino(cdc) cdc.RegisterInterface((*Info)(nil), nil) - cdc.RegisterConcrete(ccrypto.PrivKeyLedgerSecp256k1{}, - "tendermint/PrivKeyLedgerSecp256k1", nil) + cdc.RegisterConcrete(hd.BIP44Params{}, "crypto/keys/hd/BIP44Params", nil) cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil) cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil) cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil) diff --git a/crypto/keys/hd/hdpath.go b/crypto/keys/hd/hdpath.go index 050b0a39e3..5c1ad0359b 100644 --- a/crypto/keys/hd/hdpath.go +++ b/crypto/keys/hd/hdpath.go @@ -33,25 +33,25 @@ const ( // 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 +// m / purpose' / coinType' / account' / change / addressIndex // call String() on a BIP44Params instance. type BIP44Params struct { - purpose uint32 - coinType uint32 - account uint32 - change bool - addressIdx uint32 + Purpose uint32 `json:"purpose"` + CoinType uint32 `json:"coinType"` + Account uint32 `json:"account"` + Change bool `json:"change"` + AddressIndex uint32 `json:"addressIndex"` } // NewParams creates a BIP 44 parameter object from the params: -// m / purpose' / coin_type' / account' / change / address_index +// m / purpose' / coinType' / account' / change / addressIndex func NewParams(purpose, coinType, account uint32, change bool, addressIdx uint32) *BIP44Params { return &BIP44Params{ - purpose: purpose, - coinType: coinType, - account: account, - change: change, - addressIdx: addressIdx, + Purpose: purpose, + CoinType: coinType, + Account: account, + Change: change, + AddressIndex: addressIdx, } } @@ -105,11 +105,11 @@ func NewParamsFromPath(path string) (*BIP44Params, error) { } return &BIP44Params{ - purpose: purpose, - coinType: coinType, - account: account, - change: change > 0, - addressIdx: addressIdx, + Purpose: purpose, + CoinType: coinType, + Account: account, + Change: change > 0, + AddressIndex: addressIdx, }, nil } @@ -139,32 +139,32 @@ func NewFundraiserParams(account uint32, addressIdx uint32) *BIP44Params { // DerivationPath returns the BIP44 fields as an array. func (p BIP44Params) DerivationPath() []uint32 { change := uint32(0) - if p.change { + if p.Change { change = 1 } return []uint32{ - p.purpose, - p.coinType, - p.account, + p.Purpose, + p.CoinType, + p.Account, change, - p.addressIdx, + p.AddressIndex, } } func (p BIP44Params) String() string { var changeStr string - if p.change { + if p.Change { changeStr = "1" } else { changeStr = "0" } - // m / purpose' / coin_type' / account' / change / address_index + // m / Purpose' / coin_type' / Account' / Change / address_index return fmt.Sprintf("%d'/%d'/%d'/%s/%d", - p.purpose, - p.coinType, - p.account, + p.Purpose, + p.CoinType, + p.Account, changeStr, - p.addressIdx) + p.AddressIndex) } // ComputeMastersFromSeed returns the master public key, master secret, and chain code in hex. diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index 1a7b06d165..f79ed20253 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -77,14 +77,15 @@ 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 { +// newDbKeybase creates a new keybase instance using the passed DB for reading and writing keys. +func newDbKeybase(db dbm.DB) Keybase { return dbKeybase{ db: db, } } -// NewInMemory creates a new keybase on top of in-memory storage instance. +// NewInMemory creates a transient keybase on top of in-memory storage +// instance useful for testing purposes and on-the-fly key generation. func NewInMemory() Keybase { return dbKeybase{dbm.NewMemDB()} } // CreateMnemonic generates a new key and persists it to storage, encrypted @@ -284,9 +285,9 @@ func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcr return nil, err } case ledgerInfo: - return nil, errors.New("Only works on local private keys") + return nil, errors.New("only works on local private keys") case offlineInfo: - return nil, errors.New("Only works on local private keys") + return nil, errors.New("only works on local private keys") } return priv, nil } @@ -428,7 +429,8 @@ func (kb dbKeybase) writeOfflineKey(name string, pub tmcrypto.PubKey) Info { func (kb dbKeybase) writeInfo(name string, info Info) { // write the info by key key := infoKey(name) - kb.db.SetSync(key, writeInfo(info)) + serializedInfo := writeInfo(info) + kb.db.SetSync(key, serializedInfo) // store a pointer to the infokey by address for fast lookup kb.db.SetSync(addrKey(info.GetAddress()), key) } diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index f5c214a37b..6374eaafa3 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -2,7 +2,6 @@ package keys import ( "fmt" - "io/ioutil" "testing" "github.com/stretchr/testify/assert" @@ -10,42 +9,24 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/hd" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" - "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" - dbm "github.com/tendermint/tendermint/libs/db" ) func init() { mintkey.BcryptSecurityParameter = 1 } -func TestKeybaseOpenClose(t *testing.T) { - dir, err := ioutil.TempDir("", "TestKeybaseOpenClose") - assert.Nil(t, err) - - kb := New(dbm.NewDB("TestKeybaseOpenClose", dbm.LevelDBBackend, dir)) - kb.CloseDB() - - // The DB has been closed. At the moment, the expected behaviour is to panic - assert.Panics(t, func() { - _, _ = kb.CreateAccount( - "some_account", - "key pair crucial catch public canyon evil outer stage ten gym tornado", - "", "", 0, 1) - }) -} - func TestLanguage(t *testing.T) { - kb := New(dbm.NewMemDB()) + kb := NewInMemory() _, _, err := kb.CreateMnemonic("something", Japanese, "no_pass", Secp256k1) assert.Error(t, err) assert.Equal(t, "unsupported language: only english is supported", err.Error()) } func TestCreateAccountInvalidMnemonic(t *testing.T) { - kb := New(dbm.NewMemDB()) + kb := NewInMemory() _, err := kb.CreateAccount( "some_account", "malarkey pair crucial catch public canyon evil outer stage ten gym tornado", @@ -55,39 +36,53 @@ func TestCreateAccountInvalidMnemonic(t *testing.T) { } func TestCreateLedgerUnsupportedAlgo(t *testing.T) { - kb := New(dbm.NewMemDB()) + kb := NewInMemory() _, err := kb.CreateLedger("some_account", Ed25519, 0, 1) assert.Error(t, err) assert.Equal(t, "unsupported signing algo: only secp256k1 is supported", err.Error()) } func TestCreateLedger(t *testing.T) { - kb := New(dbm.NewMemDB()) + kb := NewInMemory() // test_cover and test_unit will result in different answers // test_cover does not compile some dependencies so ledger is disabled // test_unit may add a ledger mock // both cases are acceptable - ledger, err := kb.CreateLedger("some_account", Secp256k1, 0, 1) + ledger, err := kb.CreateLedger("some_account", Secp256k1, 3, 1) if err != nil { assert.Error(t, err) assert.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error()) assert.Nil(t, ledger) - } else { - // The mock is available, check that the address is correct - pubKey := ledger.GetPubKey() - addr, err := sdk.Bech32ifyAccPub(pubKey) - assert.NoError(t, err) - assert.Equal(t, "cosmospub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxvlqvd65", addr) + t.Skip("ledger nano S: support for ledger devices is not available in this executable") + return } + + // The mock is available, check that the address is correct + pubKey := ledger.GetPubKey() + pk, err := sdk.Bech32ifyAccPub(pubKey) + assert.NoError(t, err) + assert.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) + + // Check that restoring the key gets the same results + restoredKey, err := kb.Get("some_account") + assert.NotNil(t, restoredKey) + assert.Equal(t, "some_account", restoredKey.GetName()) + assert.Equal(t, TypeLedger, restoredKey.GetType()) + pubKey = restoredKey.GetPubKey() + pk, err = sdk.Bech32ifyAccPub(pubKey) + assert.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) + + linfo := restoredKey.(ledgerInfo) + assert.Equal(t, "44'/118'/3'/0/1", linfo.GetPath().String()) + } // TestKeyManagement makes sure we can manipulate these keys well func TestKeyManagement(t *testing.T) { // make the storage with reasonable defaults - db := dbm.NewMemDB() - cstore := New(db) + cstore := NewInMemory() algo := Secp256k1 n1, n2, n3 := "personal", "business", "other" @@ -118,7 +113,7 @@ func TestKeyManagement(t *testing.T) { require.NotNil(t, err) _, err = cstore.GetByAddress(accAddr(i2)) require.NoError(t, err) - addr, err := types.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") + addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") require.NoError(t, err) _, err = cstore.GetByAddress(addr) require.NotNil(t, err) @@ -165,13 +160,12 @@ func TestKeyManagement(t *testing.T) { // addr cache gets nuked - and test skip flag err = cstore.Delete(n2, "", true) require.NoError(t, err) - require.False(t, db.Has(addrKey(i2.GetAddress()))) } // TestSignVerify does some detailed checks on how we sign and validate // signatures func TestSignVerify(t *testing.T) { - cstore := New(dbm.NewMemDB()) + cstore := NewInMemory() algo := Secp256k1 n1, n2, n3 := "some dude", "a dudette", "dude-ish" @@ -253,12 +247,8 @@ func assertPassword(t *testing.T, cstore Keybase, name, pass, badpass string) { // TestExportImport tests exporting and importing func TestExportImport(t *testing.T) { - // make the storage with reasonable defaults - db := dbm.NewMemDB() - cstore := New( - db, - ) + cstore := NewInMemory() info, _, err := cstore.CreateMnemonic("john", English, "secretcpw", Secp256k1) require.NoError(t, err) @@ -286,10 +276,7 @@ func TestExportImport(t *testing.T) { // func TestExportImportPubKey(t *testing.T) { // make the storage with reasonable defaults - db := dbm.NewMemDB() - cstore := New( - db, - ) + cstore := NewInMemory() // CreateMnemonic a private-public key pair and ensure consistency notPasswd := "n9y25ah7" @@ -327,11 +314,8 @@ func TestExportImportPubKey(t *testing.T) { // TestAdvancedKeyManagement verifies update, import, export functionality func TestAdvancedKeyManagement(t *testing.T) { - // make the storage with reasonable defaults - cstore := New( - dbm.NewMemDB(), - ) + cstore := NewInMemory() algo := Secp256k1 n1, n2 := "old-name", "new name" @@ -379,9 +363,7 @@ func TestAdvancedKeyManagement(t *testing.T) { func TestSeedPhrase(t *testing.T) { // make the storage with reasonable defaults - cstore := New( - dbm.NewMemDB(), - ) + cstore := NewInMemory() algo := Secp256k1 n1, n2 := "lost-key", "found-again" @@ -410,9 +392,7 @@ func TestSeedPhrase(t *testing.T) { func ExampleNew() { // Select the encryption and storage for your cryptostore - cstore := New( - dbm.NewMemDB(), - ) + cstore := NewInMemory() sec := Secp256k1 @@ -460,6 +440,6 @@ func ExampleNew() { // signed by Bob } -func accAddr(info Info) types.AccAddress { - return (types.AccAddress)(info.GetPubKey().Address()) +func accAddr(info Info) sdk.AccAddress { + return (sdk.AccAddress)(info.GetPubKey().Address()) } diff --git a/crypto/keys/lazy_keybase.go b/crypto/keys/lazy_keybase.go index c260839722..ce3a371c8f 100644 --- a/crypto/keys/lazy_keybase.go +++ b/crypto/keys/lazy_keybase.go @@ -15,7 +15,8 @@ type lazyKeybase struct { dir string } -func NewLazyKeybase(name, dir string) Keybase { +// New creates a new instance of a lazy keybase. +func New(name, dir string) Keybase { return lazyKeybase{name: name, dir: dir} } @@ -25,7 +26,7 @@ func (lkb lazyKeybase) List() ([]Info, error) { return nil, err } defer db.Close() - return New(db).List() + return newDbKeybase(db).List() } func (lkb lazyKeybase) Get(name string) (Info, error) { @@ -34,7 +35,7 @@ func (lkb lazyKeybase) Get(name string) (Info, error) { return nil, err } defer db.Close() - return New(db).Get(name) + return newDbKeybase(db).Get(name) } func (lkb lazyKeybase) GetByAddress(address types.AccAddress) (Info, error) { @@ -43,7 +44,7 @@ func (lkb lazyKeybase) GetByAddress(address types.AccAddress) (Info, error) { return nil, err } defer db.Close() - return New(db).GetByAddress(address) + return newDbKeybase(db).GetByAddress(address) } func (lkb lazyKeybase) Delete(name, passphrase string, skipPass bool) error { @@ -52,7 +53,7 @@ func (lkb lazyKeybase) Delete(name, passphrase string, skipPass bool) error { return err } defer db.Close() - return New(db).Delete(name, passphrase, skipPass) + return newDbKeybase(db).Delete(name, passphrase, skipPass) } func (lkb lazyKeybase) Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) { @@ -61,7 +62,7 @@ func (lkb lazyKeybase) Sign(name, passphrase string, msg []byte) ([]byte, crypto return nil, nil, err } defer db.Close() - return New(db).Sign(name, passphrase, msg) + return newDbKeybase(db).Sign(name, passphrase, msg) } func (lkb lazyKeybase) CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error) { @@ -70,7 +71,7 @@ func (lkb lazyKeybase) CreateMnemonic(name string, language Language, passwd str return nil, "", err } defer db.Close() - return New(db).CreateMnemonic(name, language, passwd, algo) + return newDbKeybase(db).CreateMnemonic(name, language, passwd, algo) } func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) { @@ -79,7 +80,7 @@ func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd return nil, err } defer db.Close() - return New(db).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, account, index) + return newDbKeybase(db).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, account, index) } func (lkb lazyKeybase) Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error) { @@ -88,7 +89,7 @@ func (lkb lazyKeybase) Derive(name, mnemonic, bip39Passwd, encryptPasswd string, return nil, err } defer db.Close() - return New(db).Derive(name, mnemonic, bip39Passwd, encryptPasswd, params) + return newDbKeybase(db).Derive(name, mnemonic, bip39Passwd, encryptPasswd, params) } func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, account uint32, index uint32) (info Info, err error) { @@ -97,7 +98,7 @@ func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, account uint3 return nil, err } defer db.Close() - return New(db).CreateLedger(name, algo, account, index) + return newDbKeybase(db).CreateLedger(name, algo, account, index) } func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error) { @@ -106,7 +107,7 @@ func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey) (info In return nil, err } defer db.Close() - return New(db).CreateOffline(name, pubkey) + return newDbKeybase(db).CreateOffline(name, pubkey) } func (lkb lazyKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error { @@ -115,7 +116,7 @@ func (lkb lazyKeybase) Update(name, oldpass string, getNewpass func() (string, e return err } defer db.Close() - return New(db).Update(name, oldpass, getNewpass) + return newDbKeybase(db).Update(name, oldpass, getNewpass) } func (lkb lazyKeybase) Import(name string, armor string) (err error) { @@ -124,7 +125,7 @@ func (lkb lazyKeybase) Import(name string, armor string) (err error) { return err } defer db.Close() - return New(db).Import(name, armor) + return newDbKeybase(db).Import(name, armor) } func (lkb lazyKeybase) ImportPubKey(name string, armor string) (err error) { @@ -133,7 +134,7 @@ func (lkb lazyKeybase) ImportPubKey(name string, armor string) (err error) { return err } defer db.Close() - return New(db).ImportPubKey(name, armor) + return newDbKeybase(db).ImportPubKey(name, armor) } func (lkb lazyKeybase) Export(name string) (armor string, err error) { @@ -142,7 +143,7 @@ func (lkb lazyKeybase) Export(name string) (armor string, err error) { return "", err } defer db.Close() - return New(db).Export(name) + return newDbKeybase(db).Export(name) } func (lkb lazyKeybase) ExportPubKey(name string) (armor string, err error) { @@ -151,7 +152,7 @@ func (lkb lazyKeybase) ExportPubKey(name string) (armor string, err error) { return "", err } defer db.Close() - return New(db).ExportPubKey(name) + return newDbKeybase(db).ExportPubKey(name) } func (lkb lazyKeybase) ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) { @@ -160,7 +161,7 @@ func (lkb lazyKeybase) ExportPrivateKeyObject(name string, passphrase string) (c return nil, err } defer db.Close() - return New(db).ExportPrivateKeyObject(name, passphrase) + return newDbKeybase(db).ExportPrivateKeyObject(name, passphrase) } func (lkb lazyKeybase) CloseDB() {} diff --git a/crypto/keys/types.go b/crypto/keys/types.go index 52ef88b4b9..8a4ff7fc26 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -150,6 +150,10 @@ func (i ledgerInfo) GetAddress() types.AccAddress { return i.PubKey.Address().Bytes() } +func (i ledgerInfo) GetPath() hd.BIP44Params { + return i.Path +} + // offlineInfo is the public information about an offline key type offlineInfo struct { Name string `json:"name"` diff --git a/crypto/keys/types_test.go b/crypto/keys/types_test.go new file mode 100644 index 0000000000..621395b0c2 --- /dev/null +++ b/crypto/keys/types_test.go @@ -0,0 +1,40 @@ +package keys + +import ( + "encoding/hex" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/crypto/secp256k1" +) + +func Test_writeReadLedgerInfo(t *testing.T) { + var tmpKey secp256k1.PubKeySecp256k1 + bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") + copy(tmpKey[:], bz) + + lInfo := ledgerInfo{ + "some_name", + tmpKey, + *hd.NewFundraiserParams(5, 1)} + assert.Equal(t, TypeLedger, lInfo.GetType()) + assert.Equal(t, "44'/118'/5'/0/1", lInfo.GetPath().String()) + assert.Equal(t, + "cosmospub1addwnpepqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5wmxv8p", + types.MustBech32ifyAccPub(lInfo.GetPubKey())) + + // Serialize and restore + serialized := writeInfo(lInfo) + restoredInfo, err := readInfo(serialized) + assert.NoError(t, err) + assert.NotNil(t, restoredInfo) + + // Check both keys match + assert.Equal(t, lInfo.GetName(), restoredInfo.GetName()) + assert.Equal(t, lInfo.GetType(), restoredInfo.GetType()) + assert.Equal(t, lInfo.GetPubKey(), restoredInfo.GetPubKey()) + + assert.Equal(t, lInfo.GetPath(), restoredInfo.(ledgerInfo).GetPath()) +} diff --git a/crypto/ledger_test.go b/crypto/ledger_test.go index 6418a57324..ea2bc9681c 100644 --- a/crypto/ledger_test.go +++ b/crypto/ledger_test.go @@ -79,7 +79,7 @@ func TestPublicKeyHDPath(t *testing.T) { // Store and restore serializedPk := priv.Bytes() require.NotNil(t, serializedPk) - require.Equal(t, 44, len(serializedPk)) + require.True(t, len(serializedPk) >= 50) privKeys[i] = priv } diff --git a/docs/gaia/delegator-guide-cli.md b/docs/gaia/delegator-guide-cli.md index 3f88673f53..c790d9741a 100644 --- a/docs/gaia/delegator-guide-cli.md +++ b/docs/gaia/delegator-guide-cli.md @@ -4,10 +4,11 @@ This document contains all the necessary information for delegators to interact It also contains instructions on how to manage accounts, restore accounts from the fundraiser and use a ledger nano device. -__Very Important__: Please assure that you follow the steps described hereinafter +::: danger +**Very Important**: Please assure that you follow the steps described hereinafter carefully, as negligence in this significant process could lead to an indefinite loss of your Atoms. Therefore, read through the following instructions in their -entirety prior to proceeding and reach out to us in case you need support. +entirety prior to proceeding and reach out to us in case you need support. Please also note that you are about to interact with the Cosmos Hub, a blockchain technology containing highly experimental software. While the @@ -21,7 +22,8 @@ associated with cryptographic software (see also risk section of the the Tendermint Team may not be held liable for potential damages arising out of the use of the software. Any use of this open source software released under the Apache 2.0 license is done at your own risk and on a "AS IS" basis, without warranties or conditions -of any kind. +of any kind. +::: Please exercise extreme caution! @@ -36,9 +38,11 @@ Please exercise extreme caution! + [Connecting to a remote full-node](#connecting-to-a-remote-full-node) - [Setting up `gaiacli`](#setting-up-gaiacli) - [Querying the state](#querying-the-state) -- [Bonding Atoms and Withdrawing rewards](#bonding-atoms-and-withdrawing-rewards) -- [Participating in Governance](#participating-in-governance) -- [Signing transactions from an offline computer](#signing-transactions-from-an-offline-computer) +- [Sending Transactions](#sending-transactions) + + [A note on gas and fees](#a-note-on-gas-and-fees) + + [Bonding Atoms and Withdrawing rewards](#bonding-atoms-and-withdrawing-rewards) + + [Participating in Governance](#participating-in-governance) + + [Signing transactions from an offline computer](#signing-transactions-from-an-offline-computer) ## Installing `gaiacli` @@ -49,9 +53,17 @@ Please exercise extreme caution! ::: [**Download the binaries**] +Not available yet. [**Install from source**](https://cosmos.network/docs/gaia/installation.html) +::: tip +`gaiacli` is used from a terminal. To open the terminal, follow these steps: +- **Windows**: `Start` > `All Programs` > `Accessories` > `Command Prompt` +- **MacOS**: `Finder` > `Applications` > `Utilities` > `Terminal` +- **Linux**: `Ctrl` + `Alt` + `T` +::: + ## Cosmos Accounts At the core of every Cosmos account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to create any number of Cosmos accounts, i.e. pairs of private key/public key. This is called an HD wallet (see [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) for more information on the HD wallet specification). @@ -155,9 +167,9 @@ To create an account, you just need to have `gaiacli` installed. Before creating When you initialize your ledger, a 24-word mnemonic is generated and stored in the device. This mnemonic is compatible with Cosmos and Cosmos accounts can be derived from it. Therefore, all you have to do is make your ledger compatible with `gaiacli`. To do so, you need to go through the following steps: -1. Download the Ledger Live app [here](https://www.ledger.com/pages/ledger-live) +1. Download the Ledger Live app [here](https://www.ledger.com/pages/ledger-live). 2. Connect your ledger via USB and update to the latest firmware -3. Go to the ledger live app store, and download the "Cosmos" application (this can take a while) +3. Go to the ledger live app store, and download the "Cosmos" application (this can take a while). **Note: You may have to enable `Dev Mode` in the `Settings` of Ledger Live to be able to download the "Cosmos" application**. 4. Navigate to the Cosmos app on your ledger device Then, to create an account, use the following command: @@ -231,11 +243,15 @@ In order to connect to the full-node, you will need an address of the following ## Setting up `gaiacli` +::: tip +**Before setting up `gaiacli`, make sure you have set up a way to [access the Cosmos Hub network](#accessing-the-cosmos-hub-network)** +::: + ::: warning **Please check that you are always using the latest stable release of `gaiacli`** ::: -`gaiacli` is the tool that enables you to interact with the node that runs on the Cosmos Hub network, whether you run it yourself or not (see [accessing the cosmos hub network](#accession-the-cosmos-hub-network)). Let us set it up properly. +`gaiacli` is the tool that enables you to interact with the node that runs on the Cosmos Hub network, whether you run it yourself or not. Let us set it up properly. In order to set up `gaiacli`, use the following command: @@ -266,40 +282,44 @@ gaiacli config trust-node false Finally, let us set the `chain-id` of the blockchain we want to interact with: ```bash -gaiacli config chain-id gos-3 +gaiacli config chain-id gos-6 ``` ## Querying the state -[`gaiacli`](https://cosmos.network/docs/gaia/gaiacli.html) lets you query all relevant information from the blockchain, like account balances, amount of bonded tokens, outstanding rewards, governance proposals and more. Next is a list of the most useful commands for delegator. Please make sure you [set up gaiacli](#setting-up-gaiacli) before trying them. +::: tip +**Before you can bond atoms and withdraw rewards, you need to [set up `gaiacli`](#setting-up-gaiacli)** +::: + +`gaiacli` lets you query all relevant information from the blockchain, like account balances, amount of bonded tokens, outstanding rewards, governance proposals and more. Next is a list of the most useful commands for delegator. ```bash // query account balances and other account-related information gaiacli query account // query the list of validators -gaiacli query validators +gaiacli query staking validators -// query the information of a validator given their address (e.g. cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg) -gaiacli query validator +// query the information of a validator given their address (e.g. cosmosvaloper1n5pepvmgsfd3p2tqqgvt505jvymmstf6s9gw27) +gaiacli query staking validator // query all delegations made from a delegator given their address (e.g. cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg) -gaiacli query delegations +gaiacli query staking delegations -// query a specific delegation made from a delegator to a validator given their addresses -gaiacli query delegations +// query a specific delegation made from a delegator (e.g. cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg) to a validator (e.g. cosmosvaloper1n5pepvmgsfd3p2tqqgvt505jvymmstf6s9gw27) given their addresses +gaiacli query staking delegation // query the rewards of a delegator given a delegator address (e.g. cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg) gaiacli query distr rewards // query all proposals currently open for depositing -gaiacli query proposals --status deposit_period +gaiacli query gov proposals --status deposit_period // query all proposals currently open for voting -gaiacli query proposals --status voting_period +gaiacli query gov proposals --status voting_period // query a proposal given its proposalID -gaiacli query proposal +gaiacli query gov proposal ``` For more commands, just type: @@ -310,47 +330,7 @@ gaiacli query For each command, you can use the `-h` or `--help` flag to get more information. -## Bonding Atoms and Withdrawing rewards - -::: warning -**Before bonding Atoms, please read the [delegator faq](https://cosmos.network/resources/delegators) to understand the risk and responsabilities involved with delegating** -::: - -::: warning -**Note: These commands need to be run on an online computer. It is more secure to perform them commands using a ledger device. For the offline procedure, click [here](#signing-transactions-from-an-offline-computer).** -::: - -```bash -// Bond Atoms -// ex value for flags: =10000stake, =cosmosvaloper18thamkhnj9wz8pa4nhnp9rldprgant57pk2m8s, =0.001stake - -gaiacli tx staking --amount --validator --from --gas auto --gas-prices - -// Withdraw rewards - -gaiacli tx distr withdraw-rewards --from -``` - -::: tip -If you use a connected Ledger, you will be asked to confirm the transaction on the device before it is signed and broadcast to the network -::: - -To confirm that your transaction went through, you can use the following queries: - -```bash -// your balance should change after you bond Atoms or withdraw rewards -gaiacli query account - -// you should have delegations after you bond Atom -gaiacli query delegations - -// this returns your tx if it has been included -// use the tx hash that was displayed when you created the tx -gaiacli query tx - -``` - -Double check with a block explorer if you interact with the network through a trusted full-node. +## Sending Transactions ### A note on gas and fees @@ -364,11 +344,66 @@ The `gas` is dependent on the transaction. Different transaction require differe The `gasPrice` is the price of each unit of `gas`. Each validator sets a `min-gas-price` value, and will only include transactions that have a `gasPrice` greater than their `min-gas-price`. -The transaction `fees` are the product of `gas` and `gasPrice`. As a user, you have to input 2 out of 3. The higher the `gasPrice`, the higher the chance that your transaction will get included in a block. +The transaction `fees` are the product of `gas` and `gasPrice`. As a user, you have to input 2 out of 3. The higher the `gasPrice`/`fees`, the higher the chance that your transaction will get included in a block. + +### Bonding Atoms and Withdrawing rewards + +::: tip +**Before you can bond atoms and withdraw rewards, you need to [set up `gaiacli`](#setting-up-gaiacli) and [create an account](#creating-an-account)** +::: + +::: warning +**Before bonding Atoms, please read the [delegator faq](https://cosmos.network/resources/delegators) to understand the risk and responsabilities involved with delegating** +::: + +::: warning +**Note: These commands need to be run on an online computer. It is more secure to perform them commands using a ledger device. For the offline procedure, click [here](#signing-transactions-from-an-offline-computer).** +::: + +```bash +// Bond a certain amount of Atoms to a given validator +// ex value for flags: =cosmosvaloper18thamkhnj9wz8pa4nhnp9rldprgant57pk2m8s, =10000stake, =0.001stake + +gaiacli tx staking delegate --from --gas auto --gas-prices + + +// Withdraw all rewards +// ex value for flag: =0.001stake + +gaiacli tx distr withdraw-all-rewards --from --gas auto --gas-prices + + +// Unbond a certain amount of Atoms from a given validator +// You will have to wait 3 weeks before your Atoms are fully unbonded and transferrable +// ex value for flags: =cosmosvaloper18thamkhnj9wz8pa4nhnp9rldprgant57pk2m8s, =10000stake, =0.001stake + +gaiacli tx staking unbond --from --gas auto --gas-prices +``` + +::: tip +If you use a connected Ledger, you will be asked to confirm the transaction on the device before it is signed and broadcast to the network +::: + +To confirm that your transaction went through, you can use the following queries: + +```bash +// your balance should change after you bond Atoms or withdraw rewards +gaiacli query account + +// you should have delegations after you bond Atom +gaiacli query staking delegations + +// this returns your tx if it has been included +// use the tx hash that was displayed when you created the tx +gaiacli query tx + +``` + +Double check with a block explorer if you interact with the network through a trusted full-node. ## Participating in governance -### Primer on governance +#### Primer on governance The Cosmos Hub has a built-in governance system that lets bonded Atom holders vote on proposals. There are three types of proposal: @@ -382,7 +417,11 @@ Once the `deposit` reaches `minDeposit`, the proposal enters the `voting_period` At the end of the voting period, the proposal is accepted if there are more than 50% `Yes` votes (excluding `Abstain ` votes) and less than 33.33% of `NoWithVeto` votes (excluding `Abstain` votes). -### In practice +#### In practice + +::: tip +**Before you can bond atoms and withdraw rewards, you need to [bond Atoms](#bonding-atoms-and-withdrawing-rewards)** +::: ::: warning **Note: These commands need to be run on an online computer. It is more secure to perform them commands using a ledger device. For the offline procedure, click [here](#signing-transactions-from-an-offline-computer).** @@ -408,7 +447,7 @@ gaiacli tx gov deposit --gas auto --gas-prices gaiacli tx gov vote