diff --git a/Gopkg.lock b/Gopkg.lock index 05019f84a1..d64538d36b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -489,14 +489,6 @@ revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e" version = "v0.23.1-rc0" -[[projects]] - digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e" - name = "github.com/tendermint/tmlibs" - packages = ["cli"] - pruneopts = "UT" - revision = "49596e0a1f48866603813df843c9409fc19805c6" - version = "v0.9.0" - [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" name = "github.com/zondax/ledger-goclient" @@ -677,7 +669,6 @@ "github.com/tendermint/tendermint/rpc/lib/server", "github.com/tendermint/tendermint/types", "github.com/tendermint/tendermint/version", - "github.com/tendermint/tmlibs/cli", "github.com/zondax/ledger-goclient", "golang.org/x/crypto/blowfish", ] diff --git a/PENDING.md b/PENDING.md index cafe2025af..95dcc7c47d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -4,6 +4,7 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) * [x/stake] Validator.Owner renamed to Validator.Operator + * [\#595](https://github.com/cosmos/cosmos-sdk/issues/595) Connections to the REST server are now secured using Transport Layer Security by default. The --insecure flag is provided to switch back to insecure HTTP. * Gaia CLI (`gaiacli`) * [x/stake] Validator.Owner renamed to Validator.Operator @@ -17,6 +18,8 @@ BREAKING CHANGES utilize a validator's operator address must now use the new Bech32 prefix, `cosmosvaloper`. * [cli] [\#2190](https://github.com/cosmos/cosmos-sdk/issues/2190) `gaiacli init --gen-txs` is now `gaiacli init --with-txs` to reduce confusion + * [cli] \#2073 --from can now be either an address or a key name + * Gaia * Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013) @@ -34,6 +37,8 @@ BREAKING CHANGES * `cosmosaccaddr` / `cosmosaccpub` => `cosmos` / `cosmospub` * `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub` * [x/stake] [#1013] TendermintUpdates now uses transient store + * [x/gov] [#2195] Governance uses BFT Time + * [x/gov] \#2256 Removed slashing for governance non-voting validators * SDK * [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal @@ -42,12 +47,15 @@ BREAKING CHANGES * [types] [\#2119](https://github.com/cosmos/cosmos-sdk/issues/2119) Parsed error messages and ABCI log errors to make them more human readable. * [simulation] Rename TestAndRunTx to Operation [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153) * [simulation] Remove log and testing.TB from Operation and Invariants, in favor of using errors \#2282 + * [simulation] Remove usage of keys and addrs in the types, in favor of simulation.Account \#2384 * [tools] Removed gocyclo [#2211](https://github.com/cosmos/cosmos-sdk/issues/2211) * [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441) * [baseapp] [\#1921](https://github.com/cosmos/cosmos-sdk/issues/1921) Add minimumFees field to BaseApp. * [store] Change storeInfo within the root multistore to use tmhash instead of ripemd160 \#2308 * [codec] \#2324 All referrences to wire have been renamed to codec. Additionally, wire.NewCodec is now codec.New(). * [types] \#2343 Make sdk.Msg have a names field, to facilitate automatic tagging. + * [baseapp] \#2366 Automatically add action tags to all messages + * [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index * Tendermint @@ -73,6 +81,8 @@ FEATURES * [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT. * [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) New `sign` command to sign transactions generated with the --generate-only flag. * [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) New `broadcast` command to broadcast transactions generated offline and signed with the `sign` command. + * [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced + new commission flags for validator commands `create-validator` and `edit-validator`. * Gaia * [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address` @@ -85,6 +95,8 @@ FEATURES * [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) allow operations to specify future operations * [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) Add benchmarking capabilities, with makefile commands "test_sim_gaia_benchmark, test_sim_gaia_profile" * [simulation] [\#2349](https://github.com/cosmos/cosmos-sdk/issues/2349) Add time-based future scheduled operations to simulator + * [x/stake] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Implement + basis for the validator commission model. * Tendermint @@ -121,6 +133,7 @@ IMPROVEMENTS * [simulation] Logs get written to file if large, and also get printed on panics \#2285 * [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable * [x/stake] Add stake `Queriers` for Gaia-lite endpoints. This increases the staking endpoints performance by reusing the staking `keeper` logic for queries. [#2249](https://github.com/cosmos/cosmos-sdk/pull/2149) + * [types/decimal] \#2378 - Added truncate functionality to decimal * Tendermint @@ -143,5 +156,6 @@ BUG FIXES loading a Ledger device at runtime. * [\#2158](https://github.com/cosmos/cosmos-sdk/issues/2158) Fix non-deterministic ordering of validator iteration when slashing in `gov EndBlocker` * [simulation] \#1924 Make simulation stop on SIGTERM + * [\#2388](https://github.com/cosmos/cosmos-sdk/issues/2388) Remove dependency on deprecated tendermint/tmlibs repository. * Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 63d7b17e8d..cbf81d87d3 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -539,6 +539,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re if mode != runTxModeCheck { msgResult = handler(ctx, msg) } + msgResult.Tags = append(msgResult.Tags, sdk.MakeTag("action", []byte(msg.Name()))) // NOTE: GasWanted is determined by ante handler and // GasUsed by the GasMeter diff --git a/client/context/context.go b/client/context/context.go index db6fbef7d3..31cfab705a 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -16,6 +16,9 @@ import ( tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" "os" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client/keys" + cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" ) const ctxAccStoreName = "acc" @@ -23,22 +26,24 @@ const ctxAccStoreName = "acc" // CLIContext implements a typical CLI context created in SDK modules for // transaction handling and queries. type CLIContext struct { - Codec *codec.Codec - AccDecoder auth.AccountDecoder - Client rpcclient.Client - Logger io.Writer - Height int64 - NodeURI string - FromAddressName string - AccountStore string - TrustNode bool - UseLedger bool - Async bool - JSON bool - PrintResponse bool - Certifier tmlite.Certifier - DryRun bool - GenerateOnly bool + Codec *codec.Codec + AccDecoder auth.AccountDecoder + Client rpcclient.Client + Logger io.Writer + Height int64 + NodeURI string + From string + AccountStore string + TrustNode bool + UseLedger bool + Async bool + JSON bool + PrintResponse bool + Certifier tmlite.Certifier + DryRun bool + GenerateOnly bool + fromAddress types.AccAddress + fromName string } // NewCLIContext returns a new initialized CLIContext with parameters from the @@ -51,20 +56,25 @@ func NewCLIContext() CLIContext { rpc = rpcclient.NewHTTP(nodeURI, "/websocket") } + from := viper.GetString(client.FlagFrom) + fromAddress, fromName := fromFields(from) + return CLIContext{ - Client: rpc, - NodeURI: nodeURI, - AccountStore: ctxAccStoreName, - FromAddressName: viper.GetString(client.FlagFrom), - Height: viper.GetInt64(client.FlagHeight), - TrustNode: viper.GetBool(client.FlagTrustNode), - UseLedger: viper.GetBool(client.FlagUseLedger), - Async: viper.GetBool(client.FlagAsync), - JSON: viper.GetBool(client.FlagJson), - PrintResponse: viper.GetBool(client.FlagPrintResponse), - Certifier: createCertifier(), - DryRun: viper.GetBool(client.FlagDryRun), - GenerateOnly: viper.GetBool(client.FlagGenerateOnly), + Client: rpc, + NodeURI: nodeURI, + AccountStore: ctxAccStoreName, + From: viper.GetString(client.FlagFrom), + Height: viper.GetInt64(client.FlagHeight), + TrustNode: viper.GetBool(client.FlagTrustNode), + UseLedger: viper.GetBool(client.FlagUseLedger), + Async: viper.GetBool(client.FlagAsync), + JSON: viper.GetBool(client.FlagJson), + PrintResponse: viper.GetBool(client.FlagPrintResponse), + Certifier: createCertifier(), + DryRun: viper.GetBool(client.FlagDryRun), + GenerateOnly: viper.GetBool(client.FlagGenerateOnly), + fromAddress: fromAddress, + fromName: fromName, } } @@ -94,18 +104,51 @@ func createCertifier() tmlite.Certifier { errMsg.WriteString("--node ") } if errMsg.Len() != 0 { - fmt.Printf("must specify these options: %s when --trust-node is false\n", errMsg.String()) + fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String()) os.Exit(1) } certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI) if err != nil { - panic(err) + fmt.Printf("Create certifier failed: %s\n", err.Error()) + fmt.Printf("Please check network connection and verify the address of the node to connect to\n") + os.Exit(1) } return certifier } +func fromFields(from string) (fromAddr types.AccAddress, fromName string) { + if from == "" { + return nil, "" + } + + keybase, err := keys.GetKeyBase() + if err != nil { + fmt.Println("no keybase found") + os.Exit(1) + } + + var info cskeys.Info + if addr, err := types.AccAddressFromBech32(from); err == nil { + info, err = keybase.GetByAddress(addr) + if err != nil { + fmt.Printf("could not find key %s\n", from) + os.Exit(1) + } + } else { + info, err = keybase.Get(from) + if err != nil { + fmt.Printf("could not find key %s\n", from) + os.Exit(1) + } + } + + fromAddr = info.GetAddress() + fromName = info.GetName() + return +} + // WithCodec returns a copy of the context with an updated codec. func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext { ctx.Codec = cdc @@ -131,10 +174,9 @@ func (ctx CLIContext) WithAccountStore(accountStore string) CLIContext { return ctx } -// WithFromAddressName returns a copy of the context with an updated from -// address. -func (ctx CLIContext) WithFromAddressName(addrName string) CLIContext { - ctx.FromAddressName = addrName +// WithFrom returns a copy of the context with an updated from address or name. +func (ctx CLIContext) WithFrom(from string) CLIContext { + ctx.From = from return ctx } diff --git a/client/context/query.go b/client/context/query.go index 9eb70fd8cc..35402fbfbe 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -4,7 +4,6 @@ import ( "fmt" "io" - "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -84,22 +83,13 @@ func (ctx CLIContext) GetAccount(address []byte) (auth.Account, error) { } // GetFromAddress returns the from address from the context's name. -func (ctx CLIContext) GetFromAddress() (from sdk.AccAddress, err error) { - if ctx.FromAddressName == "" { - return nil, errors.Errorf("must provide a from address name") - } +func (ctx CLIContext) GetFromAddress() (sdk.AccAddress, error) { + return ctx.fromAddress, nil +} - keybase, err := keys.GetKeyBase() - if err != nil { - return nil, err - } - - info, err := keybase.Get(ctx.FromAddressName) - if err != nil { - return nil, errors.Errorf("no key for: %s", ctx.FromAddressName) - } - - return sdk.AccAddress(info.GetPubKey().Address()), nil +// GetFromName returns the key name for the current context. +func (ctx CLIContext) GetFromName() (string, error) { + return ctx.fromName, nil } // GetAccountNumber returns the next account number for the given account diff --git a/client/flags.go b/client/flags.go index 55b29b53ca..d0d783d3a7 100644 --- a/client/flags.go +++ b/client/flags.go @@ -58,7 +58,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { // PostCommands adds common flags for commands to post tx func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { - c.Flags().String(FlagFrom, "", "Name of private key with which to sign") + c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx") c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx") c.Flags().String(FlagMemo, "", "Memo to send along with transaction") diff --git a/client/keys/add.go b/client/keys/add.go index c421728196..be2825a55d 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -46,7 +46,6 @@ phrase, otherwise, a new key will be generated.`, return cmd } -// nolint: gocyclo // TODO remove the above when addressing #1446 func runAddCmd(cmd *cobra.Command, args []string) error { var kb keys.Keybase diff --git a/client/keys/show.go b/client/keys/show.go index c7cae1f7cc..82c6f9883d 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -9,7 +9,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/tendermint/tmlibs/cli" + "github.com/tendermint/tendermint/libs/cli" ) const ( diff --git a/client/lcd/certificates.go b/client/lcd/certificates.go new file mode 100644 index 0000000000..f47f2397c7 --- /dev/null +++ b/client/lcd/certificates.go @@ -0,0 +1,174 @@ +package lcd + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "fmt" + "io/ioutil" + "math/big" + "net" + "os" + "strings" + "time" +) + +// default: 30 days +const defaultValidFor = 30 * 24 * time.Hour + +func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateKey, err error) { + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + notBefore := time.Now() + notAfter := notBefore.Add(defaultValidFor) + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + err = fmt.Errorf("failed to generate serial number: %s", err) + return + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Gaia Lite"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + } + hosts := strings.Split(host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + certBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + err = fmt.Errorf("couldn't create certificate: %s", err) + return + } + return +} + +func writeCertAndPrivKey(certBytes []byte, priv *ecdsa.PrivateKey) (certFile string, keyFile string, err error) { + if priv == nil { + err = errors.New("private key is nil") + return + } + certFile, err = writeCertificateFile(certBytes) + if err != nil { + return + } + keyFile, err = writeKeyFile(priv) + return +} + +func writeCertificateFile(certBytes []byte) (filename string, err error) { + f, err := ioutil.TempFile("", "cert_") + if err != nil { + return + } + defer f.Close() + filename = f.Name() + if err := pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil { + return filename, fmt.Errorf("failed to write data to %s: %s", filename, err) + } + return +} + +func writeKeyFile(priv *ecdsa.PrivateKey) (filename string, err error) { + f, err := ioutil.TempFile("", "key_") + if err != nil { + return + } + defer f.Close() + filename = f.Name() + block, err := pemBlockForKey(priv) + if err != nil { + return + } + if err := pem.Encode(f, block); err != nil { + return filename, fmt.Errorf("failed to write data to %s: %s", filename, err) + } + return +} + +func pemBlockForKey(priv *ecdsa.PrivateKey) (*pem.Block, error) { + b, err := x509.MarshalECPrivateKey(priv) + if err != nil { + return nil, fmt.Errorf("unable to marshal ECDSA private key: %v", err) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil + +} + +func genCertKeyFilesAndReturnFingerprint(sslHosts string) (certFile, keyFile string, fingerprint string, err error) { + certBytes, priv, err := generateSelfSignedCert(sslHosts) + if err != nil { + return + } + certFile, keyFile, err = writeCertAndPrivKey(certBytes, priv) + cleanupFunc := func() { + os.Remove(certFile) + os.Remove(keyFile) + } + // Either of the files could have been written already, + // thus clean up regardless of the error. + if err != nil { + defer cleanupFunc() + return + } + fingerprint, err = fingerprintForCertificate(certBytes) + if err != nil { + defer cleanupFunc() + return + } + return +} + +func fingerprintForCertificate(certBytes []byte) (string, error) { + cert, err := x509.ParseCertificate(certBytes) + if err != nil { + return "", err + } + h := sha256.New() + h.Write(cert.Raw) + fingerprintBytes := h.Sum(nil) + var buf bytes.Buffer + for i, b := range fingerprintBytes { + if i > 0 { + fmt.Fprintf(&buf, ":") + } + fmt.Fprintf(&buf, "%02X", b) + } + return fmt.Sprintf("SHA256 Fingerprint=%s", buf.String()), nil +} + +func fingerprintFromFile(certFile string) (string, error) { + f, err := os.Open(certFile) + if err != nil { + return "", err + } + defer f.Close() + data, err := ioutil.ReadAll(f) + if err != nil { + return "", err + } + block, _ := pem.Decode(data) + if block == nil { + return "", fmt.Errorf("couldn't find PEM data in %s", certFile) + } + return fingerprintForCertificate(block.Bytes) +} diff --git a/client/lcd/certificates_test.go b/client/lcd/certificates_test.go new file mode 100644 index 0000000000..14bddfa0f8 --- /dev/null +++ b/client/lcd/certificates_test.go @@ -0,0 +1,93 @@ +package lcd + +import ( + "crypto/ecdsa" + "crypto/x509" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGenerateSelfSignedCert(t *testing.T) { + host := "127.0.0.1,localhost,::1" + certBytes, _, err := generateSelfSignedCert(host) + require.Nil(t, err) + cert, err := x509.ParseCertificate(certBytes) + require.Nil(t, err) + require.Equal(t, 2, len(cert.IPAddresses)) + require.Equal(t, 1, len(cert.DNSNames)) + require.True(t, cert.IsCA) +} + +func TestWriteCertAndPrivKey(t *testing.T) { + expectedPerm := "-rw-------" + derBytes, priv, err := generateSelfSignedCert("localhost") + require.Nil(t, err) + type args struct { + certBytes []byte + priv *ecdsa.PrivateKey + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"valid certificate", args{derBytes, priv}, false}, + {"garbage", args{[]byte("some garbage"), nil}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotCertFile, gotKeyFile, err := writeCertAndPrivKey(tt.args.certBytes, tt.args.priv) + defer os.Remove(gotCertFile) + defer os.Remove(gotKeyFile) + if tt.wantErr { + require.NotNil(t, err) + return + } + require.Nil(t, err) + info, err := os.Stat(gotCertFile) + require.Nil(t, err) + require.True(t, info.Mode().IsRegular()) + require.Equal(t, expectedPerm, info.Mode().String()) + info, err = os.Stat(gotKeyFile) + require.Nil(t, err) + require.True(t, info.Mode().IsRegular()) + require.Equal(t, expectedPerm, info.Mode().String()) + }) + } +} + +func TestFingerprintFromFile(t *testing.T) { + cert := `-----BEGIN CERTIFICATE----- +MIIBbDCCARGgAwIBAgIQSuFKYv/22v+cxtVgMUrQADAKBggqhkjOPQQDAjASMRAw +DgYDVQQKEwdBY21lIENvMB4XDTE4MDkyMDIzNDQyNloXDTE5MDkyMDIzNDQyNlow +EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDIo +ujAesRczcPVAWiLhpeV1B7hS/RI2LJaGj3QjyJ8hiUthJTPIamr8m7LuS/U5fS0o +hY297YeTIGo9YkxClICjSTBHMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr +BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MA8GA1UdEQQIMAaHBH8AAAEwCgYIKoZI +zj0EAwIDSQAwRgIhAKnwbhX9FrGG1otCVLwhClQ3RaLxnNpCgIGTqSimb34cAiEA +stMN+IqMCKWlZyGqxGIiyksMLMEU3lRqKNQn2EoAZJY= +-----END CERTIFICATE-----` + wantFingerprint := `SHA256 Fingerprint=0B:ED:9A:AA:A2:D1:7E:B2:53:56:F6:FC:C0:E6:1A:69:70:21:A2:B0:90:FC:AF:BB:EF:AE:2C:78:52:AB:68:40` + certFile, err := ioutil.TempFile("", "test_cert_") + require.Nil(t, err) + _, err = certFile.Write([]byte(cert)) + require.Nil(t, err) + err = certFile.Close() + require.Nil(t, err) + defer os.Remove(certFile.Name()) + fingerprint, err := fingerprintFromFile(certFile.Name()) + require.Nil(t, err) + require.Equal(t, wantFingerprint, fingerprint) + + // test failure + emptyFile, err := ioutil.TempFile("", "test_cert_") + require.Nil(t, err) + err = emptyFile.Close() + require.Nil(t, err) + defer os.Remove(emptyFile.Name()) + _, err = fingerprintFromFile(emptyFile.Name()) + require.NotNil(t, err) +} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 263b112b27..30fa314c53 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -120,6 +120,11 @@ func TestKeys(t *testing.T) { } func TestVersion(t *testing.T) { + // skip the test if the VERSION environment variable has not been set + if version.Version == "" { + t.SkipNow() + } + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() @@ -295,7 +300,7 @@ func TestCoinSend(t *testing.T) { require.Equal(t, http.StatusOK, res.StatusCode, body) } -func TestIBCTransfer(t *testing.T) { +func DisabledTestIBCTransfer(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) diff --git a/client/lcd/root.go b/client/lcd/root.go index ed5baa688e..36fbd42d0b 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -1,6 +1,8 @@ package lcd import ( + "errors" + "net" "net/http" "os" @@ -13,7 +15,6 @@ import ( auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest" slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" "github.com/gorilla/mux" @@ -24,35 +25,84 @@ import ( tmserver "github.com/tendermint/tendermint/rpc/lib/server" ) +const ( + flagListenAddr = "laddr" + flagCORS = "cors" + flagMaxOpenConnections = "max-open" + flagInsecure = "insecure" + flagSSLHosts = "ssl-hosts" + flagSSLCertFile = "ssl-certfile" + flagSSLKeyFile = "ssl-keyfile" +) + // ServeCommand will generate a long-running rest server // (aka Light Client Daemon) that exposes functionality similar // to the cli, but over rest func ServeCommand(cdc *codec.Codec) *cobra.Command { - flagListenAddr := "laddr" - flagCORS := "cors" - flagMaxOpenConnections := "max-open" cmd := &cobra.Command{ Use: "rest-server", Short: "Start LCD (light-client daemon), a local REST server", - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, args []string) (err error) { listenAddr := viper.GetString(flagListenAddr) handler := createHandler(cdc) logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") maxOpen := viper.GetInt(flagMaxOpenConnections) + sslHosts := viper.GetString(flagSSLHosts) + certFile := viper.GetString(flagSSLCertFile) + keyFile := viper.GetString(flagSSLKeyFile) + cleanupFunc := func() {} - listener, err := tmserver.StartHTTPServer( - listenAddr, handler, logger, - tmserver.Config{MaxOpenConnections: maxOpen}, - ) - if err != nil { - return err + var listener net.Listener + var fingerprint string + if viper.GetBool(flagInsecure) { + listener, err = tmserver.StartHTTPServer( + listenAddr, handler, logger, + tmserver.Config{MaxOpenConnections: maxOpen}, + ) + if err != nil { + return + } + } else { + if certFile != "" { + // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 + err = validateCertKeyFiles(certFile, keyFile) + if err != nil { + return err + } + // cert/key pair is provided, read the fingerprint + fingerprint, err = fingerprintFromFile(certFile) + if err != nil { + return err + } + } else { + // if certificate is not supplied, generate a self-signed one + certFile, keyFile, fingerprint, err = genCertKeyFilesAndReturnFingerprint(sslHosts) + if err != nil { + return err + } + cleanupFunc = func() { + os.Remove(certFile) + os.Remove(keyFile) + } + defer cleanupFunc() + } + listener, err = tmserver.StartHTTPAndTLSServer( + listenAddr, handler, + certFile, keyFile, + logger, + tmserver.Config{MaxOpenConnections: maxOpen}, + ) + if err != nil { + return + } + logger.Info(fingerprint) } - logger.Info("REST server started") // wait forever and cleanup cmn.TrapSignal(func() { + defer cleanupFunc() err := listener.Close() logger.Error("error closing listener", "err", err) }) @@ -62,6 +112,10 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { } cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") + cmd.Flags().Bool(flagInsecure, false, "Do not set up SSL/TLS layer") + cmd.Flags().String(flagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") + cmd.Flags().String(flagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") + cmd.Flags().String(flagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)") cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") @@ -90,10 +144,22 @@ func createHandler(cdc *codec.Codec) http.Handler { tx.RegisterRoutes(cliCtx, r, cdc) auth.RegisterRoutes(cliCtx, r, cdc, "acc") bank.RegisterRoutes(cliCtx, r, cdc, kb) - ibc.RegisterRoutes(cliCtx, r, cdc, kb) stake.RegisterRoutes(cliCtx, r, cdc, kb) slashing.RegisterRoutes(cliCtx, r, cdc, kb) gov.RegisterRoutes(cliCtx, r, cdc) return r } + +func validateCertKeyFiles(certFile, keyFile string) error { + if keyFile == "" { + return errors.New("a key file is required") + } + if _, err := os.Stat(certFile); err != nil { + return err + } + if _, err := os.Stat(keyFile); err != nil { + return err + } + return nil +} diff --git a/client/rpc/validators.go b/client/rpc/validators.go index f708cb1456..0583dc0545 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" - tmTypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" ) @@ -78,7 +77,7 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) { return nil, err } - if !bytes.Equal(check.ValidatorsHash(), tmTypes.NewValidatorSet(validatorsRes.Validators).Hash()) { + if !bytes.Equal(check.ValidatorsHash(), tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) { return nil, fmt.Errorf("got invalid validatorset") } } diff --git a/client/utils/utils.go b/client/utils/utils.go index 81fd138595..76ee91585e 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -8,9 +8,9 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" - auth "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - amino "github.com/tendermint/go-amino" + "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/libs/common" ) @@ -25,8 +25,13 @@ func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) return err } + name, err := cliCtx.GetFromName() + if err != nil { + return err + } + if txBldr.SimulateGas || cliCtx.DryRun { - txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs) + txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) if err != nil { return err } @@ -36,13 +41,13 @@ func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) return nil } - passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName) + passphrase, err := keys.GetPassphrase(name) if err != nil { return err } // build and sign the transaction - txBytes, err := txBldr.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs) + txBytes, err := txBldr.BuildAndSign(name, passphrase, msgs) if err != nil { return err } @@ -196,7 +201,13 @@ func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg return } if txBldr.SimulateGas { - txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs) + var name string + name, err = cliCtx.GetFromName() + if err != nil { + return + } + + txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) if err != nil { return } diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 41f91c6029..ad9330ba9b 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -18,7 +18,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" @@ -42,7 +41,6 @@ type GaiaApp struct { // keys to access the substores keyMain *sdk.KVStoreKey keyAccount *sdk.KVStoreKey - keyIBC *sdk.KVStoreKey keyStake *sdk.KVStoreKey tkeyStake *sdk.TransientStoreKey keySlashing *sdk.KVStoreKey @@ -57,7 +55,6 @@ type GaiaApp struct { accountMapper auth.AccountMapper feeCollectionKeeper auth.FeeCollectionKeeper bankKeeper bank.Keeper - ibcMapper ibc.Mapper stakeKeeper stake.Keeper slashingKeeper slashing.Keeper distrKeeper distr.Keeper @@ -77,7 +74,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), - keyIBC: sdk.NewKVStoreKey("ibc"), keyStake: sdk.NewKVStoreKey("stake"), tkeyStake: sdk.NewTransientStoreKey("transient_stake"), keyDistr: sdk.NewKVStoreKey("distr"), @@ -99,7 +95,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio // add handlers app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection) app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) - app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) @@ -117,7 +112,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.bankKeeper)). - AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.bankKeeper)). AddRoute("stake", stake.NewHandler(app.stakeKeeper)). AddRoute("distr", distr.NewHandler(app.distrKeeper)). AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). @@ -132,7 +126,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) - app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keyDistr, + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keyDistr, app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams) app.MountStoresTransient(app.tkeyParams, app.tkeyStake, app.tkeyDistr) err := app.LoadLatestVersion(app.keyMain) @@ -146,7 +140,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio // custom tx codec func MakeCodec() *codec.Codec { var cdc = codec.New() - ibc.RegisterCodec(cdc) bank.RegisterCodec(cdc) stake.RegisterCodec(cdc) distr.RegisterCodec(cdc) diff --git a/cmd/gaia/app/benchmarks/txsize_test.go b/cmd/gaia/app/benchmarks/txsize_test.go new file mode 100644 index 0000000000..83e51c0416 --- /dev/null +++ b/cmd/gaia/app/benchmarks/txsize_test.go @@ -0,0 +1,35 @@ +package app + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/tendermint/tendermint/crypto/secp256k1" +) + +// This will fail half the time with the second output being 173 +// This is due to secp256k1 signatures not being constant size. +// This will be resolved when updating to tendermint v0.24.0 +// nolint: vet +func ExampleTxSendSize() { + cdc := app.MakeCodec() + priv1 := secp256k1.GenPrivKeySecp256k1([]byte{0}) + addr1 := sdk.AccAddress(priv1.PubKey().Address()) + priv2 := secp256k1.GenPrivKeySecp256k1([]byte{1}) + addr2 := sdk.AccAddress(priv2.PubKey().Address()) + coins := []sdk.Coin{sdk.NewCoin("denom", sdk.NewInt(10))} + msg1 := bank.MsgSend{ + Inputs: []bank.Input{bank.NewInput(addr1, coins)}, + Outputs: []bank.Output{bank.NewOutput(addr2, coins)}, + } + sig, _ := priv1.Sign(msg1.GetSignBytes()) + sigs := []auth.StdSignature{auth.StdSignature{nil, sig, 0, 0}} + tx := auth.NewStdTx([]sdk.Msg{msg1}, auth.NewStdFee(0, coins...), sigs, "") + fmt.Println(len(cdc.MustMarshalBinaryBare([]sdk.Msg{msg1}))) + fmt.Println(len(cdc.MustMarshalBinaryBare(tx))) + // output: 80 + // 173 +} diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 078eece110..6097b5a08c 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -42,14 +41,14 @@ func init() { flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit") } -func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { +func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { var genesisAccounts []GenesisAccount // Randomly generate some genesis accounts for _, acc := range accs { coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}} genesisAccounts = append(genesisAccounts, GenesisAccount{ - Address: acc, + Address: acc.Address, Coins: coins, }) } @@ -61,10 +60,10 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json // XXX Try different numbers of initially bonded validators numInitiallyBonded := int64(50) for i := 0; i < int(numInitiallyBonded); i++ { - validator := stake.NewValidator(sdk.ValAddress(accs[i]), keys[i].PubKey(), stake.Description{}) + validator := stake.NewValidator(sdk.ValAddress(accs[i].Address), accs[i].PubKey, stake.Description{}) validator.Tokens = sdk.NewDec(100) validator.DelegatorShares = sdk.NewDec(100) - delegation := stake.Delegation{accs[i], sdk.ValAddress(accs[i]), sdk.NewDec(100), 0} + delegation := stake.Delegation{accs[i].Address, sdk.ValAddress(accs[i].Address), sdk.NewDec(100), 0} validators = append(validators, validator) delegations = append(delegations, delegation) } diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 8ac8f55598..3c847dc90e 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -238,6 +238,9 @@ func TestGaiaCLICreateValidator(t *testing.T) { cvStr += fmt.Sprintf(" --pubkey=%s", barCeshPubKey) cvStr += fmt.Sprintf(" --amount=%v", "2steak") cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally") + cvStr += fmt.Sprintf(" --commission-rate=%v", "0.05") + cvStr += fmt.Sprintf(" --commission-max-rate=%v", "0.20") + cvStr += fmt.Sprintf(" --commission-max-change-rate=%v", "0.10") initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(1)) diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index d178f04847..d0e2182b93 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -15,7 +15,6 @@ import ( bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" distrcmd "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" @@ -52,20 +51,8 @@ func main() { ) tx.AddCommands(tendermintCmd, cdc) - //Add IBC commands - ibcCmd := &cobra.Command{ - Use: "ibc", - Short: "Inter-Blockchain Communication subcommands", - } - ibcCmd.AddCommand( - client.PostCommands( - ibccmd.IBCTransferCmd(cdc), - ibccmd.IBCRelayCmd(cdc), - )...) - rootCmd.AddCommand( tendermintCmd, - ibcCmd, lcd.ServeCommand(cdc), client.LineBreak, ) diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index 7200ef0b65..182e789bea 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -24,7 +24,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" @@ -132,7 +131,6 @@ type GaiaApp struct { // keys to access the substores keyMain *sdk.KVStoreKey keyAccount *sdk.KVStoreKey - keyIBC *sdk.KVStoreKey keyStake *sdk.KVStoreKey tkeyStake *sdk.TransientStoreKey keySlashing *sdk.KVStoreKey @@ -142,7 +140,6 @@ type GaiaApp struct { accountMapper auth.AccountMapper feeCollectionKeeper auth.FeeCollectionKeeper bankKeeper bank.Keeper - ibcMapper ibc.Mapper stakeKeeper stake.Keeper slashingKeeper slashing.Keeper paramsKeeper params.Keeper @@ -160,7 +157,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), - keyIBC: sdk.NewKVStoreKey("ibc"), keyStake: sdk.NewKVStoreKey("stake"), tkeyStake: sdk.NewTransientStoreKey("transient_stake"), keySlashing: sdk.NewKVStoreKey("slashing"), @@ -176,7 +172,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp // add handlers app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) - app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) @@ -184,7 +179,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.bankKeeper)). - AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.bankKeeper)). AddRoute("stake", stake.NewHandler(app.stakeKeeper)) // initialize BaseApp @@ -192,7 +186,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) - app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing) + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keySlashing) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) @@ -206,7 +200,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp // custom tx codec func MakeCodec() *codec.Codec { var cdc = codec.New() - ibc.RegisterCodec(cdc) bank.RegisterCodec(cdc) stake.RegisterCodec(cdc) slashing.RegisterCodec(cdc) diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index 0bf7ad9903..c9bfd08129 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/secp256k1" dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/types" ) var _ Keybase = dbKeybase{} @@ -41,6 +42,8 @@ const ( French // Italian is currently not supported. Italian + addressSuffix = "address" + infoSuffix = "info" ) var ( @@ -179,11 +182,16 @@ func (kb dbKeybase) List() ([]Info, error) { 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 + key := string(iter.Key()) + + // need to include only keys in storage that have an info suffix + if strings.HasSuffix(key, infoSuffix) { + info, err := readInfo(iter.Value()) + if err != nil { + return nil, err + } + res = append(res, info) } - res = append(res, info) } return res, nil } @@ -197,6 +205,15 @@ func (kb dbKeybase) Get(name string) (Info, error) { return readInfo(bs) } +func (kb dbKeybase) GetByAddress(address types.AccAddress) (Info, error) { + ik := kb.db.Get(addrKey(address)) + if len(ik) == 0 { + return nil, fmt.Errorf("key with address %s not found", address) + } + bs := kb.db.Get(ik) + 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 []byte, pub tmcrypto.PubKey, err error) { @@ -347,6 +364,7 @@ func (kb dbKeybase) Delete(name, passphrase string) error { if err != nil { return err } + kb.db.DeleteSync(addrKey(linfo.GetAddress())) kb.db.DeleteSync(infoKey(name)) return nil case ledgerInfo: @@ -354,9 +372,11 @@ func (kb dbKeybase) Delete(name, passphrase string) error { if passphrase != "yes" { return fmt.Errorf("enter 'yes' exactly to delete the key - this cannot be undone") } + kb.db.DeleteSync(addrKey(info.GetAddress())) kb.db.DeleteSync(infoKey(name)) return nil } + return nil } @@ -413,9 +433,16 @@ func (kb dbKeybase) writeOfflineKey(pub tmcrypto.PubKey, name string) Info { func (kb dbKeybase) writeInfo(info Info, name string) { // write the info by key - kb.db.SetSync(infoKey(name), writeInfo(info)) + key := infoKey(name) + kb.db.SetSync(key, writeInfo(info)) + // store a pointer to the infokey by address for fast lookup + kb.db.SetSync(addrKey(info.GetAddress()), key) +} + +func addrKey(address types.AccAddress) []byte { + return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix)) } func infoKey(name string) []byte { - return []byte(fmt.Sprintf("%s.info", name)) + return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) } diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index 652a36bcb0..2a602461a1 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/types" ) func init() { @@ -20,8 +21,9 @@ func init() { // 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( - dbm.NewMemDB(), + db, ) algo := Secp256k1 @@ -51,6 +53,12 @@ func TestKeyManagement(t *testing.T) { require.NoError(t, err) _, err = cstore.Get(n3) require.NotNil(t, err) + _, err = cstore.GetByAddress(accAddr(i2)) + require.NoError(t, err) + addr, err := types.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") + require.NoError(t, err) + _, err = cstore.GetByAddress(addr) + require.NotNil(t, err) // list shows them in order keyS, err := cstore.List() @@ -92,6 +100,11 @@ func TestKeyManagement(t *testing.T) { keyS, err = cstore.List() require.NoError(t, err) require.Equal(t, 1, len(keyS)) + + // addr cache gets nuked + err = cstore.Delete(n2, p2) + require.NoError(t, err) + require.False(t, db.Has(addrKey(i2.GetAddress()))) } // TestSignVerify does some detailed checks on how we sign and validate @@ -387,3 +400,7 @@ func ExampleNew() { // Carl // signed by Bob } + +func accAddr(info Info) types.AccAddress { + return (types.AccAddress)(info.GetPubKey().Address()) +} \ No newline at end of file diff --git a/crypto/keys/types.go b/crypto/keys/types.go index a686264897..c5e97d5fba 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -5,14 +5,15 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/cosmos/cosmos-sdk/types" ) // Keybase exposes operations on a generic keystore type Keybase interface { - // CRUD on the keystore List() ([]Info, error) Get(name string) (Info, error) + GetByAddress(address types.AccAddress) (Info, error) Delete(name, passphrase string) error // Sign some bytes, looking up the private key to use @@ -73,6 +74,8 @@ type Info interface { GetName() string // Public key GetPubKey() crypto.PubKey + // Address + GetAddress() types.AccAddress } var _ Info = &localInfo{} @@ -106,6 +109,10 @@ func (i localInfo) GetPubKey() crypto.PubKey { return i.PubKey } +func (i localInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + // ledgerInfo is the public information about a Ledger key type ledgerInfo struct { Name string `json:"name"` @@ -133,6 +140,10 @@ func (i ledgerInfo) GetPubKey() crypto.PubKey { return i.PubKey } +func (i ledgerInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + // offlineInfo is the public information about an offline key type offlineInfo struct { Name string `json:"name"` @@ -158,6 +169,10 @@ func (i offlineInfo) GetPubKey() crypto.PubKey { return i.PubKey } +func (i offlineInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + // encoding info func writeInfo(i Info) []byte { return cdc.MustMarshalBinary(i) diff --git a/docs/config.js b/docs/.vuepress/config.js similarity index 96% rename from docs/config.js rename to docs/.vuepress/config.js index 5de1661790..5a30b44232 100644 --- a/docs/config.js +++ b/docs/.vuepress/config.js @@ -24,8 +24,8 @@ module.exports = { children: [ "/getting-started/voyager", "/getting-started/installation", - "/getting-started/full-node", - "/getting-started/create-testnet" + "/getting-started/join-testnet", + "/getting-started/networks" ] }, { diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js new file mode 100644 index 0000000000..f24006cbaa --- /dev/null +++ b/docs/.vuepress/enhanceApp.js @@ -0,0 +1,3 @@ +export default ({ router }) => { + router.addRoutes([{ path: "/testnet/", redirect: "/" }]) +} diff --git a/docs/.vuepress/override.styl b/docs/.vuepress/override.styl new file mode 100644 index 0000000000..39bdc860df --- /dev/null +++ b/docs/.vuepress/override.styl @@ -0,0 +1,4 @@ +$accentColor = #304DE9 +$textColor = #15192C +$borderColor = #eaecef +$codeBgColor = #282c34 diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md index 78451c0ef2..30cb5d9bcb 100644 --- a/docs/DOCS_README.md +++ b/docs/DOCS_README.md @@ -24,7 +24,7 @@ on the website. ## Config.js -The [config.js](./config.js) generates the sidebar and Table of Contents +The [config.js](./.vuepress/config.js) generates the sidebar and Table of Contents on the website docs. Note the use of relative links and the omission of file extensions. Additional features are available to improve the look of the sidebar. @@ -59,9 +59,34 @@ to send users to the GitHub. ## Building Locally -Not currently possible but coming soon! Doing so requires -assets held in the (private) website repo, installing -[VuePress](https://vuepress.vuejs.org/), and modifying the `config.js`. +To build and serve the documentation locally, run: + +``` +npm install -g vuepress +``` + +then change the following line in the `config.js`: + +``` +base: "/docs/", +``` + +to: + +``` +base: "/", +``` + +Finally, go up one directory to the root of the repo and run: + +``` +# from root of repo +vuepress build docs +cd dist/docs +python -m SimpleHTTPServer 8080 +``` + +then navigate to localhost:8080 in your browser. ## Consistency diff --git a/docs/clients/service-providers.md b/docs/clients/service-providers.md new file mode 100644 index 0000000000..d7f69fb10e --- /dev/null +++ b/docs/clients/service-providers.md @@ -0,0 +1,111 @@ +# Integrate a Cosmos-SDK based blockchain as a Service Provider + +We define 'service providers' as entities providing services for end-users that involve some form of interaction with a Cosmos-SDK based blockchain (this includes the Cosmos Hub). More specifically, this document will be focused around interactions with tokens. + +This section does not concern wallet builders that want to provide [Light-Client](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/light) functionalities. Service providers are expected to act as trusted point of contact to the blockchain for their end-users. + +## High-level description of the architecture + +There are three main pieces to consider: + +- Full-nodes: To interact with the blockchain. +- Rest Server: This acts as a relayer for HTTP calls. +- Rest API: Define available endpoints for the Rest Server. + +## Running a Full-Node + +### Installation and configuration + +We will describe the steps to run and interract with a full-node for the Cosmos Hub. For other SDK-based blockchain, the process should be similar. + +First, you need to [install the software](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/getting-started/installation.md). + +Then, you can start [running a full-node](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/getting-started/full-node.md). + +### Command-Line interface + +Next you will find a few useful CLI commands to interact with the Full-Node. + +#### Creating a key-pair + +To generate a new key (default ed25519 elliptic curve): + +```bash +gaiacli keys add +``` + +You will be asked to create a passwords (at least 8 characters) for this key-pair. The command returns 4 informations: + +- `NAME`: Name of your key +- `ADDRESS`: Your address. Used to receive funds. +- `PUBKEY`: Your public key. Useful for validators. +- `Seed phrase`: 12-words phrase. **Save this seed phrase somewhere safe**. It is used to recover your private key in case you forget the password. + +You can see all your available keys by typing: + +```bash +gaiacli keys list +``` + +#### Checking your balance + +After receiving tokens to your address, you can view your account's balance by typing: + +```bash +gaiacli account +``` + +*Note: When you query an account balance with zero tokens, you will get this error: No account with address was found in the state. This is expected! We're working on improving our error messages.* + +#### Sending coins via the CLI + +Here is the command to send coins via the CLI: + +```bash +gaiacli send --amount=10faucetToken --chain-id= --name= --to= +``` + +Flags: +- `--amount`: This flag accepts the format ``. +- `--chain-id`: This flag allows you to specify the id of the chain. There will be different ids for different testnet chains and main chain. +- `--name`: Name of the key of the sending account. +- `--to`: Address of the recipient. + +#### Help + +If you need to do something else, the best command you can run is: + +```bash +gaiacli +``` + +It will display all the available commands. For each command, you can use the `--help` flag to get further information. + +## Setting up the Rest Server + +The Rest Server acts as an intermediary between the front-end and the full-node. You don't need to run the Rest Server on the same machine as the full-node. If you intend to run the Rest Server on another machine, you need to go through the [Installation and configuration](#installation-and-configuration) again on this machine. + +To start the Rest server: + +```bash +gaiacli advanced rest-server --trust-node=false --node= +``` + +Flags: +- `--trust-node`: A boolean. If `true`, light-client verification is enabled. If `false`, it is disabled. For service providers, this should be set to `false`. +- `--node`: This is where you indicate the address and the port of your full-node. The format is . If the full-node is on the same machine, the address should be "tcp://localhost". +- `--laddr`: This flag allows you to specify the address and port for the Rest Server. You will mostly use this flag only to specify the port, in which case just input "localhost" for the address. The format is . + +### Listening for incoming transaction + +The recommended way to listen for incoming transaction is to periodically query the blockchain through the following endpoint of the LCD: + +[`/bank/balance/{account}`](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#bankbalanceaccount---get) + +## Rest API + +The Rest API documents all the available endpoints that you can use to interract with your full node. It can be found [here](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md). + +The API is divided into ICS standards for each category of endpoints. For example, the [ICS20](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#ics20---tokenapi) describes the API to interact with tokens. + +To give more flexibility to implementers, we have separated the different steps that are involved in the process of sending transactions. You will be able to generate unsigned transactions (example with [coin transfer](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#post-banktransfers)), [sign](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#post-authtxsign) and [broadcast](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#post-authtxbroadcast) them with different API endpoints. This allows service providers to use their own signing mechanism for instance. \ No newline at end of file diff --git a/docs/getting-started/create-testnet.md b/docs/getting-started/create-testnet.md deleted file mode 100644 index 74fd1d5ef2..0000000000 --- a/docs/getting-started/create-testnet.md +++ /dev/null @@ -1,27 +0,0 @@ -## Create your Own Testnet - -To create your own testnet, first each validator will need to install gaiad and run gen-tx - -```bash -gaiad init gen-tx --name -``` - -This populations `$HOME/.gaiad/gen-tx/` with a json file. - -Now these json files need to be aggregated together via Github, a Google form, pastebin or other methods. - -Place all files on one computer in `$HOME/.gaiad/gen-tx/` - -```bash -gaiad init --with-txs -o --chain= -``` - -This will generate a `genesis.json` in `$HOME/.gaiad/config/genesis.json` distribute this file to all validators on your testnet. - -### Export state - -To export state and reload (useful for testing purposes): - -``` -gaiad export > genesis.json; cp genesis.json ~/.gaiad/config/genesis.json; gaiad start -``` diff --git a/docs/getting-started/full-node.md b/docs/getting-started/join-testnet.md similarity index 100% rename from docs/getting-started/full-node.md rename to docs/getting-started/join-testnet.md diff --git a/docs/getting-started/networks.md b/docs/getting-started/networks.md new file mode 100644 index 0000000000..abd2999baa --- /dev/null +++ b/docs/getting-started/networks.md @@ -0,0 +1,209 @@ +# Networks + +There are a variety of ways to setup either local or remote networks with automation, detailed below. +All the required files are found in the [networks directory](https://github.com/cosmos/cosmos-sdk/tree/develop/networks) and additionally the `local` or `remote` sub-directories. + +## Local Testnet + +From the [networks/local directory](https://github.com/cosmos/cosmos-sdk/tree/develop/networks/local): + +### Requirements + +- [Install gaia](https://cosmos.network/docs/getting-started/installation.html) +- [Install docker](https://docs.docker.com/engine/installation/) +- [Install docker-compose](https://docs.docker.com/compose/install/) + +### Build + +Build the `gaiad` binary and the `tendermint/gaiadnode` docker image. + +Note the binary will be mounted into the container so it can be updated without +rebuilding the image. + +``` +cd $GOPATH/src/github.com/cosmos/cosmos-sdk + +# Build the linux binary in ./build +make build-linux + +# Build tendermint/gaiadnode image +make build-docker-gaiadnode +``` + +### Run a testnet + +To start a 4 node testnet run: + +``` +make localnet-start +``` + +This command creates a 4-node network using the gaiadnode image. +The ports for each node are found in this table: + +| Node ID | P2P Port | RPC Port | +| --------|-------|------| +| `gaianode0` | `26656` | `26657` | +| `gaianode1` | `26659` | `26660` | +| `gaianode2` | `26661` | `26662` | +| `gaianode3` | `26663` | `26664` | + +To update the binary, just rebuild it and restart the nodes: + +``` +make build-linux localnet-stop localnet-start +``` + +### Configuration + +The `make localnet-start` creates files for a 4-node testnet in `./build` by calling the `gaiad testnet` command. +This outputs a handful of files in the `./build` directory: + +```tree -L 2 build/ +build/ +├── gaiacli +├── gaiad +├── gentxs +│   ├── node0.json +│   ├── node1.json +│   ├── node2.json +│   └── node3.json +├── node0 +│   ├── gaiacli +│   │   ├── key_seed.json +│   │   └── keys +│   └── gaiad +│   ├── ${LOG:-gaiad.log} +│   ├── config +│   └── data +├── node1 +│   ├── gaiacli +│   │   └── key_seed.json +│   └── gaiad +│   ├── ${LOG:-gaiad.log} +│   ├── config +│   └── data +├── node2 +│   ├── gaiacli +│   │   └── key_seed.json +│   └── gaiad +│   ├── ${LOG:-gaiad.log} +│   ├── config +│   └── data +└── node3 + ├── gaiacli + │   └── key_seed.json + └── gaiad + ├── ${LOG:-gaiad.log} + ├── config + └── data +``` + +Each `./build/nodeN` directory is mounted to the `/gaiad` directory in each container. + +### Logging + +Logs are saved under each `./build/nodeN/gaiad/gaia.log`. Watch them stream in with, for example: + +``` +tail -f build/node0/gaiad/gaia.log +``` + +### Special binaries + +If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume. For example: + +``` +# Run with custom binary +BINARY=gaiafoo make localnet-start +``` + +## Remote Testnet + +The following should be run from the [networks directory](https://github.com/cosmos/cosmos-sdk/tree/develop/networks). + +### Terraform & Ansible + +Automated deployments are done using [Terraform](https://www.terraform.io/) to create servers on AWS then +[Ansible](http://www.ansible.com/) to create and manage testnets on those servers. + +### Prerequisites + +- Install [Terraform](https://www.terraform.io/downloads.html) and [Ansible](http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) on a Linux machine. +- Create an [AWS API token](https://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html) with EC2 create capability. +- Create SSH keys + +``` +export AWS_ACCESS_KEY_ID="2345234jk2lh4234" +export AWS_SECRET_ACCESS_KEY="234jhkg234h52kh4g5khg34" +export TESTNET_NAME="remotenet" +export CLUSTER_NAME= "remotenetvalidators" +export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa" +export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub" +``` + +These will be used by both `terraform` and `ansible`. + +### Create a remote network + +``` +SERVERS=1 REGION_LIMIT=1 make validators-start +``` + +The testnet name is what's going to be used in --chain-id, while the cluster name is the administrative tag in AWS for the servers. The code will create SERVERS amount of servers in each availability zone up to the number of REGION_LIMITs, starting at us-east-2. (us-east-1 is excluded.) The below BaSH script does the same, but sometimes it's more comfortable for input. + +``` +./new-testnet.sh "$TESTNET_NAME" "$CLUSTER_NAME" 1 1 +``` + +### Quickly see the /status endpoint + +``` +make validators-status +``` + +### Delete servers + +``` +make validators-stop +``` + +### Logging + +You can ship logs to Logz.io, an Elastic stack (Elastic search, Logstash and Kibana) service provider. You can set up your nodes to log there automatically. Create an account and get your API key from the notes on [this page](https://app.logz.io/#/dashboard/data-sources/Filebeat), then: + +``` +yum install systemd-devel || echo "This will only work on RHEL-based systems." +apt-get install libsystemd-dev || echo "This will only work on Debian-based systems." + +go get github.com/mheese/journalbeat +ansible-playbook -i inventory/digital_ocean.py -l remotenet logzio.yml -e LOGZIO_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 +``` + +### Monitoring + +You can install the DataDog agent with: + +``` +make datadog-install +``` + +### Single-node testnet + +To create a single node testnet: + +``` +cd $GOPATH/src/github.com/cosmos/cosmos-sdk + +# Clear the build folder +rm -rf ./build + +# Build binary +make build-linux + +# Create configuration +docker run -v `pwd`/build:/gaiad tendermint/gaiadnode testnet -o . --v 1 + +# Run the node +docker run -v `pwd`/build:/gaiad tendermint/gaiadnode +``` diff --git a/docs/light/getting_started.md b/docs/light/getting_started.md index 5f11956c05..21497477a7 100644 --- a/docs/light/getting_started.md +++ b/docs/light/getting_started.md @@ -1,6 +1,6 @@ # Getting Started -To start a rest server, we need to specify the following parameters: +To start a REST server, we need to specify the following parameters: | Parameter | Type | Default | Required | Description | | ----------- | --------- | ----------------------- | -------- | ---------------------------------------------------- | | chain-id | string | null | true | chain id of the full node to connect | @@ -12,9 +12,25 @@ To start a rest server, we need to specify the following parameters: Sample command: ```bash -gaiacli light-client --chain-id=test --laddr=tcp://localhost:1317 --node tcp://localhost:46657 --trust-node=false +gaiacli rest-server --chain-id=test \ + --laddr=tcp://localhost:1317 \ + --node tcp://localhost:46657 \ + --trust-node=false ``` +The server listens on HTTPS by default. You can set the SSL certificate to be used by the server with these additional flags: + +```bash +gaiacli rest-server --chain-id=test \ + --laddr=tcp://localhost:1317 \ + --node tcp://localhost:46657 \ + --trust-node=false \ + --certfile=mycert.pem --keyfile=mykey.key +``` + +If no certificate/keyfile pair is supplied, a self-signed certificate will be generated and its fingerprint printed out. +Append `--insecure` to the command line if you want to disable the secure layer and listen on an insecure HTTP port. + ## Gaia Light Use Cases LCD could be very helpful for related service providers. For a wallet service provider, LCD could diff --git a/docs/spec/bank/WIP_keeper.md b/docs/spec/bank/WIP_keeper.md new file mode 100644 index 0000000000..9667fc5dd0 --- /dev/null +++ b/docs/spec/bank/WIP_keeper.md @@ -0,0 +1,25 @@ +WORK IN PROGRESS +See PR comments here https://github.com/cosmos/cosmos-sdk/pull/2072 + +# Keeper + +## Denom Metadata + +The BankKeeper contains a store that stores the metadata of different token denoms. Denoms are referred to by their name, same as the `denom` field in sdk.Coin. The different attributes of a denom are stored in the denom metadata store under the key `[denom name]:[attribute name]`. The default attributes in the store are explained below. However, this can be extended by the developer or through SoftwareUpgrade proposals. + +### Decimals `int8` + +- `Base Unit` = The common standard for the default "standard" size of a token. Examples: 1 Bitcoin or 1 Ether. +- `Smallest Unit` = The smallest possible denomination of a token. A fraction of the base unit. Examples: 1 satoshi or 1 wei. + +All amounts throughout the SDK are denominated in the smallest unit of a token, so that all amounts can be expressed as integers. However, UIs typically want to display token values in the base unit, so the Decimals metadata field standardizes the number of digits that come after the decimal place in the base unit. + +`1 [Base Unit] = 10^(N) [Smallest Unit]` + +### TotalSupply `sdk.Integer` + +The TotalSupply of a denom is the total amount of a token that exists (known to the chain) across all accounts and modules. It is denominated in the `smallest unit` of a denom. It can be changed by the Keeper functions `MintCoins` and `BurnCoins`. `AddCoins` and `SubtractCoins` are used when adding or subtracting coins for an account, but not removing them from total supply (for example, when moving the coins to the control of the staking module). + +### Aliases `[]string` + +Aliases is an array of strings that are "alternative names" for a token. As an example, while the Ether's denom name might be `ether`, a possible alias could be `ETH`. This field can be useful for UIs and clients. It is intended that this field can be modified by a governance mechanism. diff --git a/docs/spec/bank/state.md b/docs/spec/bank/state.md deleted file mode 100644 index 8c2c47fd4e..0000000000 --- a/docs/spec/bank/state.md +++ /dev/null @@ -1,2 +0,0 @@ -- SDK related specifications (ie. how multistore, signatures, etc. work). -- Basecoin (SendTx) diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 728ded3713..5b577cec39 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -13,13 +13,13 @@ has to be created and the previous one rendered inactive. ```go type DepositProcedure struct { MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period. - MaxDepositPeriod int64 // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months + MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months } ``` ```go type VotingProcedure struct { - VotingPeriod int64 // Length of the voting period. Initial value: 2 weeks + VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks } ``` @@ -28,7 +28,6 @@ type TallyingProcedure struct { Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 GovernancePenalty sdk.Dec // Penalty if validator does not vote - GracePeriod int64 // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply } ``` @@ -97,10 +96,10 @@ type Proposal struct { Type ProposalType // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit Deposits []Deposit // List of deposits on the proposal - SubmitBlock int64 // Height of the block where TxGovSubmitProposal was included + SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included Submitter sdk.Address // Address of the submitter - VotingStartBlock int64 // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached + VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached CurrentStatus ProposalStatus // Current status of the proposal YesVotes sdk.Dec @@ -137,7 +136,7 @@ For pseudocode purposes, here are the two function we will use to read or write * `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the `ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if - `CurrentBlock == VotingStartBlock + activeProcedure.VotingPeriod`. If it is, + `CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is, then the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted and, if not, applies `GovernancePenalty`. If the proposal is accepted, deposits are refunded. After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated. @@ -159,7 +158,7 @@ And the pseudocode for the `ProposalProcessingQueue`: proposal = load(Governance, ) // proposal is a const key votingProcedure = load(GlobalParams, 'VotingProcedure') - if (CurrentBlock == proposal.VotingStartBlock + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive) + if (CurrentTime == proposal.VotingStartTime + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive) // End of voting period, tally @@ -192,14 +191,10 @@ And the pseudocode for the `ProposalProcessingQueue`: tallyingProcedure = load(GlobalParams, 'TallyingProcedure') - // Slash validators that did not vote, or update tally if they voted + // Update tally if validator voted they voted for each validator in validators - if (validator.bondHeight < CurrentBlock - tallyingProcedure.GracePeriod) - // only slash if validator entered validator set before grace period - if (!tmpValMap(validator).HasVoted) - slash validator by tallyingProcedure.GovernancePenalty - else - proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus)) + if tmpValMap(validator).HasVoted + proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus)) diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index f2d6f98541..9454aca7dd 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -43,12 +43,13 @@ type Params struct { Validators are identified according to the `OperatorAddr`, an SDK validator address for the operator of the validator. -Validators also have a `ConsPubKey`, the public key of the validator. - -Validators are indexed in the store using the following maps: +Validators also have a `ConsPubKey`, the public key of the validator used in +Tendermint consensus. The validator can be retrieved from it's `ConsPubKey` +once it can be converted into the corresponding `ConsAddr`. Validators are +indexed in the store using the following maps: - Validators: `0x02 | OperatorAddr -> amino(validator)` -- ValidatorsByPubKey: `0x03 | ConsPubKey -> OperatorAddr` +- ValidatorsByConsAddr: `0x03 | ConsAddr -> OperatorAddr` - ValidatorsByPower: `0x05 | power | blockHeight | blockTx -> OperatorAddr` `Validators` is the primary index - it ensures that each operator can have only one @@ -69,7 +70,7 @@ validator. ```golang type Validator struct { - ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator + ConsPubKey crypto.PubKey // Tendermint consensus pubkey of validator Jailed bool // has the validator been jailed? Status sdk.BondStatus // validator status (bonded/unbonding/unbonded) diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index 148c1c415f..6ed90343b6 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -1,86 +1,107 @@ -## Transaction Overview +# Transaction Overview In this section we describe the processing of the transactions and the corresponding updates to the state. Transactions: - - TxCreateValidator - - TxEditValidator - - TxDelegation - - TxStartUnbonding - - TxCompleteUnbonding - - TxRedelegate - - TxCompleteRedelegation + +* TxCreateValidator +* TxEditValidator +* TxDelegation +* TxStartUnbonding +* TxCompleteUnbonding +* TxRedelegate +* TxCompleteRedelegation Other important state changes: - - Update Validators + +* Update Validators Other notes: - - `tx` denotes a reference to the transaction being processed - - `sender` denotes the address of the sender of the transaction - - `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and - modify objects from the store - - `sdk.Dec` refers to a decimal type specified by the SDK. -### TxCreateValidator +* `tx` denotes a reference to the transaction being processed +* `sender` denotes the address of the sender of the transaction +* `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and + modify objects from the store +* `sdk.Dec` refers to a decimal type specified by the SDK. - - triggers: `distribution.CreateValidatorDistribution` +## TxCreateValidator + +* triggers: `distribution.CreateValidatorDistribution` A validator is created using the `TxCreateValidator` transaction. ```golang type TxCreateValidator struct { - Operator sdk.Address - ConsensusPubKey crypto.PubKey - GovernancePubKey crypto.PubKey - SelfDelegation coin.Coin + Description Description + Commission Commission - Description Description - Commission sdk.Dec - CommissionMax sdk.Dec - CommissionMaxChange sdk.Dec + DelegatorAddr sdk.AccAddress + ValidatorAddr sdk.ValAddress + PubKey crypto.PubKey + Delegation sdk.Coin } - createValidator(tx TxCreateValidator): - validator = getValidator(tx.Operator) - if validator != nil return // only one validator per address + ok := validatorExists(tx.ValidatorAddr) + if ok return err // only one validator per address - validator = NewValidator(operatorAddr, ConsensusPubKey, GovernancePubKey, Description) - init validator poolShares, delegatorShares set to 0 - init validator commision fields from tx - validator.PoolShares = 0 + ok := validatorByPubKeyExists(tx.PubKey) + if ok return err // only one validator per public key + err := validateDenom(tx.Delegation.Denom) + if err != nil return err // denomination must be valid + + validator := NewValidator(tx.ValidatorAddr, tx.PubKey, tx.Description) + + err := setInitialCommission(validator, tx.Commission, blockTime) + if err != nil return err // must be able to set initial commission correctly + + // set the validator and public key setValidator(validator) + setValidatorByPubKeyIndex(validator) - txDelegate = TxDelegate(tx.Operator, tx.Operator, tx.SelfDelegation) - delegate(txDelegate, validator) // see delegate function in [TxDelegate](TxDelegate) - return + // delegate coins from tx.DelegatorAddr to the validator + err := delegate(tx.DelegatorAddr, tx.Delegation, validator) + if err != nil return err // must be able to set delegation correctly + + tags := createTags(tx) + return tags ``` -### TxEditValidator +## TxEditValidator -If either the `Description` (excluding `DateBonded` which is constant), -`Commission`, or the `GovernancePubKey` need to be updated, the -`TxEditCandidacy` transaction should be sent from the operator account: +If either the `Description`, `Commission`, or the `ValidatorAddr` need to be +updated, the `TxEditCandidacy` transaction should be sent from the operator +account: ```golang type TxEditCandidacy struct { - GovernancePubKey crypto.PubKey - Commission sdk.Dec - Description Description + Description Description + ValidatorAddr sdk.ValAddress + CommissionRate sdk.Dec } editCandidacy(tx TxEditCandidacy): - validator = getValidator(tx.ValidatorAddr) + validator, ok := getValidator(tx.ValidatorAddr) + if !ok return err // validator must exist - if tx.Commission > CommissionMax || tx.Commission < 0 then fail - if rateChange(tx.Commission) > CommissionMaxChange then fail - validator.Commission = tx.Commission + // Attempt to update the validator's description. The description provided + // must be valid. + description, err := updateDescription(validator, tx.Description) + if err != nil return err - if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey - if tx.Description != nil validator.Description = tx.Description + // a validator is not required to update it's commission rate + if tx.CommissionRate != nil { + // Attempt to update a validator's commission rate. The rate provided + // must be valid. It's rate can only be updated once a day. + err := updateValidatorCommission(validator, tx.CommissionRate) + if err != nil return err + } - setValidator(store, validator) - return + // set the validator and public key + setValidator(validator) + + tags := createTags(tx) + return tags ``` ### TxDelegate diff --git a/docs/validators/validator-setup.md b/docs/validators/validator-setup.md index 8a842ea205..dbcc84b3d5 100644 --- a/docs/validators/validator-setup.md +++ b/docs/validators/validator-setup.md @@ -35,9 +35,16 @@ gaiacli stake create-validator \ --address-validator= --moniker="choose a moniker" \ --chain-id= \ - --name= + --name= \ + --commission-rate="0.10" \ + --commission-max-rate="0.20" \ + --commission-max-change-rate="0.01" ``` +__Note__: When specifying commission parameters, the `commission-max-change-rate` +is used to measure % _point_ change over the `commission-rate`. E.g. 1% to 2% is +a 100% rate increase, but only 1 percentage point. + ### Edit Validator Description You can edit your validator's public description. This info is to identify your validator, and will be relied on by delegators to decide which validators to stake to. Make sure to provide input for every flag below, otherwise the field will default to empty (`--moniker` defaults to the machine name). @@ -52,9 +59,17 @@ gaiacli stake edit-validator --identity=6A0D65E29A4CBC8E --details="To infinity and beyond!" --chain-id= \ - --name= + --name= \ + --commission-rate="0.10" ``` +__Note__: The `commission-rate` value must adhere to the following invariants: + +- Must be between 0 and the validator's `commission-max-rate` +- Must not exceed the validator's `commission-max-change-rate` which is maximum + % point change rate **per day**. In other words, a validator can only change + its commission once per day and within `commission-max-change-rate` bounds. + ### View Validator Description View the validator's information with this command: diff --git a/networks/README.md b/networks/README.md index 322d37340d..221f6e3858 100644 --- a/networks/README.md +++ b/networks/README.md @@ -1,65 +1,6 @@ -# Terraform & Ansible +# Networks -Automated deployments are done using [Terraform](https://www.terraform.io/) to create servers on AWS then -[Ansible](http://www.ansible.com/) to create and manage testnets on those servers. +Here contains the files required for automated deployment of either local or remote testnets. -## Prerequisites - -- Install [Terraform](https://www.terraform.io/downloads.html) and [Ansible](http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) on a Linux machine. -- Create an [AWS API token](https://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html) with EC2 create capability. -- Create SSH keys - -``` -export AWS_ACCESS_KEY_ID="2345234jk2lh4234" -export AWS_SECRET_ACCESS_KEY="234jhkg234h52kh4g5khg34" -export TESTNET_NAME="remotenet" -export CLUSTER_NAME= "remotenetvalidators" -export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa" -export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub" -``` - -These will be used by both `terraform` and `ansible`. - -## Create a remote network - -``` -SERVERS=1 REGION_LIMIT=1 make validators-start -``` - -The testnet name is what's going to be used in --chain-id, while the cluster name is the administrative tag in AWS for the servers. The code will create SERVERS amount of servers in each availability zone up to the number of REGION_LIMITs, starting at us-east-2. (us-east-1 is excluded.) The below BaSH script does the same, but sometimes it's more comfortable for input. - -``` -./new-testnet.sh "$TESTNET_NAME" "$CLUSTER_NAME" 1 1 -``` - -## Quickly see the /status endpoint - -``` -make validators-status -``` - -## Delete servers - -``` -make validators-stop -``` - -## Logging - -You can ship logs to Logz.io, an Elastic stack (Elastic search, Logstash and Kibana) service provider. You can set up your nodes to log there automatically. Create an account and get your API key from the notes on [this page](https://app.logz.io/#/dashboard/data-sources/Filebeat), then: - -``` -yum install systemd-devel || echo "This will only work on RHEL-based systems." -apt-get install libsystemd-dev || echo "This will only work on Debian-based systems." - -go get github.com/mheese/journalbeat -ansible-playbook -i inventory/digital_ocean.py -l remotenet logzio.yml -e LOGZIO_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 -``` - -## Monitoring - -You can install the DataDog agent with: - -``` -make datadog-install -``` +Doing so is best accomplished using the `make` targets. For more information, see the +[networks documentation](/docs/getting-started/networks.md) diff --git a/networks/local/README.md b/networks/local/README.md deleted file mode 100644 index ef5e27803d..0000000000 --- a/networks/local/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Local Cluster with Docker Compose - -## Requirements - -- [Install gaia](https://cosmos.network/docs/getting-started/installation.html) -- [Install docker](https://docs.docker.com/engine/installation/) -- [Install docker-compose](https://docs.docker.com/compose/install/) - -## Build - -Build the `gaiad` binary and the `tendermint/gaiadnode` docker image. - -Note the binary will be mounted into the container so it can be updated without -rebuilding the image. - -``` -cd $GOPATH/src/github.com/cosmos/cosmos-sdk - -# Build the linux binary in ./build -make build-linux - -# Build tendermint/gaiadnode image -make build-docker-gaiadnode -``` - -## Run a testnet - -To start a 4 node testnet run: - -``` -make localnet-start -``` - -The nodes bind their RPC servers to ports 26657, 26660, 26662, and 26664 on the host. -This file creates a 4-node network using the gaiadnode image. -The nodes of the network expose their P2P and RPC endpoints to the host machine on ports 26656-26657, 26659-26660, 26661-26662, and 26663-26664 respectively. - -To update the binary, just rebuild it and restart the nodes: - -``` -make build-linux -make localnet-stop -make localnet-start -``` - -## Configuration - -The `make localnet-start` creates files for a 4-node testnet in `./build` by calling the `gaiad testnet` command. - -The `./build` directory is mounted to the `/gaiad` mount point to attach the binary and config files to the container. - -For instance, to create a single node testnet: - -``` -cd $GOPATH/src/github.com/cosmos/cosmos-sdk - -# Clear the build folder -rm -rf ./build - -# Build binary -make build-linux - -# Create configuration -docker run -v `pwd`/build:/gaiad tendermint/gaiadnode testnet -o . --v 1 - -#Run the node -docker run -v `pwd`/build:/gaiad tendermint/gaiadnode -``` - -## Logging - -Log is saved under the attached volume, in the `gaiad.log` file and written on the screen. - -## Special binaries - -If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume. diff --git a/tools/Makefile b/tools/Makefile index 87544107c5..c24e886ffb 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,7 +12,6 @@ INEFFASSIGN = github.com/gordonklaus/ineffassign MISSPELL = github.com/client9/misspell/cmd/misspell ERRCHECK = github.com/kisielk/errcheck UNPARAM = mvdan.cc/unparam -GOCYCLO = github.com/alecthomas/gocyclo DEP_CHECK := $(shell command -v dep 2> /dev/null) GOLINT_CHECK := $(shell command -v golint 2> /dev/null) @@ -22,7 +21,6 @@ INEFFASSIGN_CHECK := $(shell command -v ineffassign 2> /dev/null) MISSPELL_CHECK := $(shell command -v misspell 2> /dev/null) ERRCHECK_CHECK := $(shell command -v errcheck 2> /dev/null) UNPARAM_CHECK := $(shell command -v unparam 2> /dev/null) -# GOCYCLO_CHECK := $(shell command -v gocyclo 2> /dev/null) check_tools: ifndef DEP_CHECK @@ -68,11 +66,6 @@ ifndef UNPARAM_CHECK else @echo "Found unparam in path." endif -ifndef GOCYCLO_CHECK - @echo "No gocyclo in path. Install with 'make get_tools'." -else - @echo "Found gocyclo in path." -endif get_tools: ifdef DEP_CHECK @@ -126,12 +119,6 @@ else @echo "Installing unparam" go get -v $(UNPARAM) endif -# ifdef GOCYCLO_CHECK -# @echo "gocyclo is already installed. Run 'make update_tools' to update." -# else -# @echo "Installing gocyclo" -# go get -v $(GOCYCLO) -# endif update_tools: @echo "Updating dep" @@ -153,8 +140,6 @@ update_dev_tools: go get -u -v $(ERRCHECK) @echo "Updating unparam" go get -u -v $(UNPARAM) - # @echo "Updating goyclo" - # go get -u -v $(GOCYCLO) # To avoid unintended conflicts with file names, always add to .PHONY # unless there is a reason not to. diff --git a/x/auth/ante.go b/x/auth/ante.go index 7075bdd684..5c3a35fc33 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -23,7 +23,6 @@ const ( // NewAnteHandler returns an AnteHandler that checks // and increments sequence numbers, checks signatures & account numbers, // and deducts fees from the first signer. -// nolint: gocyclo func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler { return func( diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 242614d118..2a1e51f0de 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -43,7 +43,6 @@ func init() { } // SendRequestHandlerFn - http request handler to send coins to a address -// nolint: gocyclo func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // collect data diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index cefa2a460b..0b8ec3026f 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -18,19 +18,18 @@ import ( // SimulateSingleInputMsgSend tests and runs a single msg send, with one input and one output, where both // accounts already exist. func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - fromKey := simulation.RandomKey(r, keys) - fromAddr := sdk.AccAddress(fromKey.PubKey().Address()) - toKey := simulation.RandomKey(r, keys) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + fromAcc := simulation.RandomAcc(r, accs) + toAcc := simulation.RandomAcc(r, accs) // Disallow sending money to yourself for { - if !fromKey.Equals(toKey) { + if !fromAcc.PubKey.Equals(toAcc.PubKey) { break } - toKey = simulation.RandomKey(r, keys) + toAcc = simulation.RandomAcc(r, accs) } - toAddr := sdk.AccAddress(toKey.PubKey().Address()) - initFromCoins := mapper.GetAccount(ctx, fromAddr).GetCoins() + toAddr := toAcc.Address + initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).GetCoins() if len(initFromCoins) == 0 { return "skipping, no coins at all", nil, nil @@ -43,7 +42,7 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation } action = fmt.Sprintf("%s is sending %s %s to %s", - fromAddr.String(), + fromAcc.Address.String(), amt.String(), initFromCoins[denomIndex].Denom, toAddr.String(), @@ -51,10 +50,10 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}} var msg = bank.MsgSend{ - Inputs: []bank.Input{bank.NewInput(fromAddr, coins)}, + Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)}, Outputs: []bank.Output{bank.NewOutput(toAddr, coins)}, } - goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromKey}) + goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}) if goErr != nil { return "", nil, goErr } diff --git a/x/bank/simulation/sim_test.go b/x/bank/simulation/sim_test.go index 8115355077..819289845d 100644 --- a/x/bank/simulation/sim_test.go +++ b/x/bank/simulation/sim_test.go @@ -5,8 +5,6 @@ import ( "math/rand" "testing" - "github.com/tendermint/tendermint/crypto" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" @@ -26,8 +24,8 @@ func TestBankWithRandomMessages(t *testing.T) { panic(err) } - appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { - mock.RandomSetGenesis(r, mapp, accs, []string{"stake"}) + appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage { + simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"}) return json.RawMessage("{}") } diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index a2b626b2c6..ab7168aca3 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -91,11 +91,14 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) { } // This is nolinted as someone is in the process of refactoring this to remove the goto -// nolint: gocyclo func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) { cliCtx := context.NewCLIContext() - passphrase, err := keys.ReadPassphraseFromStdin(cliCtx.FromAddressName) + name, err := cliCtx.GetFromName() + if err != nil { + panic(err) + } + passphrase, err := keys.ReadPassphraseFromStdin(name) if err != nil { panic(err) } @@ -202,7 +205,12 @@ func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []b txBldr := authtxb.NewTxBuilderFromCLI().WithSequence(sequence).WithCodec(c.cdc) cliCtx := context.NewCLIContext() - res, err := txBldr.BuildAndSign(cliCtx.FromAddressName, passphrase, []sdk.Msg{msg}) + name, err := cliCtx.GetFromName() + if err != nil { + panic(err) + } + + res, err := txBldr.BuildAndSign(name, passphrase, []sdk.Msg{msg}) if err != nil { panic(err) } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index e44ea240be..d151f8aa58 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -35,7 +35,6 @@ type transferBody struct { // TransferRequestHandler - http request handler to transfer coins to a address // on a different chain via IBC -// nolint: gocyclo func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) diff --git a/x/mock/simulation/constants.go b/x/mock/simulation/constants.go index 985a22dcac..544da50e3e 100644 --- a/x/mock/simulation/constants.go +++ b/x/mock/simulation/constants.go @@ -5,10 +5,10 @@ const ( pastEvidenceFraction float64 = 0.5 // Minimum time per block - minTimePerBlock int64 = 86400 / 2 + minTimePerBlock int64 = 1000 / 2 // Maximum time per block - maxTimePerBlock int64 = 86400 + maxTimePerBlock int64 = 1000 // Number of keys numKeys int = 250 diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 96dbff5725..25748e15bd 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -15,17 +15,15 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mock" ) // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool) error { @@ -33,16 +31,16 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) } -func initChain(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress, setups []RandSetup, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage) (validators map[string]mockValidator) { - res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, keys, accs)}) +func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseapp.BaseApp, + appStateFn func(r *rand.Rand, accounts []Account) json.RawMessage) (validators map[string]mockValidator) { + res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)}) validators = make(map[string]mockValidator) for _, validator := range res.Validators { validators[string(validator.Address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} } for i := 0; i < len(setups); i++ { - setups[i](r, keys) + setups[i](r, accounts) } return @@ -56,7 +54,7 @@ func randTimestamp(r *rand.Rand) time.Time { // SimulateFromSeed tests an application by running the provided // operations, testing the provided invariants, but using the provided seed. func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, seed int64, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool) (simError error) { @@ -69,7 +67,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix()) timeDiff := maxTimePerBlock - minTimePerBlock - keys, accs := mock.GeneratePrivKeyAddressPairsFromRand(r, numKeys) + accs := RandomAccounts(r, numKeys) // Setup event stats events := make(map[string]uint) @@ -77,7 +75,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, events[what]++ } - validators := initChain(r, keys, accs, setups, app, appStateFn) + validators := initChain(r, accs, setups, app, appStateFn) header := abci.Header{Height: 0, Time: timestamp} opCount := 0 @@ -139,10 +137,10 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, thisBlockSize := getBlockSize(r, blockSize) // Run queued operations. Ignores blocksize if blocksize is too small - numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, logWriter, displayLogs, event) - numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, keys, logWriter, displayLogs, event) + numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, accs, logWriter, displayLogs, event) + numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, accs, logWriter, displayLogs, event) thisBlockSize = thisBlockSize - numQueuedOpsRan - numQueuedTimeOpsRan - operations := blockSimulator(thisBlockSize, r, app, ctx, keys, header, logWriter) + operations := blockSimulator(thisBlockSize, r, app, ctx, accs, header, logWriter) opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan res := app.EndBlock(abci.RequestEndBlock{}) @@ -176,7 +174,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize // memory overhead func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, displayLogs func()) func( - blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, privKeys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { + blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { totalOpWeight := 0 for i := 0; i < len(ops); i++ { totalOpWeight += ops[i].Weight @@ -193,9 +191,9 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f return ops[0].Op } return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - keys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { + accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { for j := 0; j < blocksize; j++ { - logUpdate, futureOps, err := selectOp(r)(r, app, ctx, keys, event) + logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) if err != nil { displayLogs() tb.Fatalf("error on operation %d within block %d, %v", header.Height, opCount, err) @@ -264,14 +262,14 @@ func queueOperations(queuedOperations map[int][]Operation, queuedTimeOperations // nolint: errcheck func runQueuedOperations(queueOperations map[int][]Operation, height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { + accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { if queuedOps, ok := queueOperations[height]; ok { numOps := len(queuedOps) for i := 0; i < numOps; i++ { // For now, queued operations cannot queue more operations. // If a need arises for us to support queued messages to queue more messages, this can // be changed. - logUpdate, _, err := queuedOps[i](r, app, ctx, privKeys, event) + logUpdate, _, err := queuedOps[i](r, app, ctx, accounts, event) logWriter(logUpdate) if err != nil { displayLogs() @@ -285,14 +283,14 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, tb tes } func runQueuedTimeOperations(queueOperations []FutureOperation, currentTime time.Time, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { + accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { numOpsRan = 0 for len(queueOperations) > 0 && currentTime.After(queueOperations[0].BlockTime) { // For now, queued operations cannot queue more operations. // If a need arises for us to support queued messages to queue more messages, this can // be changed. - logUpdate, _, err := queueOperations[0].Op(r, app, ctx, privKeys, event) + logUpdate, _, err := queueOperations[0].Op(r, app, ctx, accounts, event) logWriter(logUpdate) if err != nil { displayLogs() diff --git a/x/mock/simulation/types.go b/x/mock/simulation/types.go index a32edea6d6..2e68a428ea 100644 --- a/x/mock/simulation/types.go +++ b/x/mock/simulation/types.go @@ -23,11 +23,11 @@ type ( // Operations can optionally provide a list of "FutureOperations" to run later // These will be ran at the beginning of the corresponding block. Operation func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, event func(string), + accounts []Account, event func(string), ) (action string, futureOperations []FutureOperation, err error) // RandSetup performs the random setup the mock module needs. - RandSetup func(r *rand.Rand, privKeys []crypto.PrivKey) + RandSetup func(r *rand.Rand, accounts []Account) // An Invariant is a function which tests a particular invariant. // If the invariant has been broken, it should return an error @@ -35,6 +35,15 @@ type ( // The simulator will then halt and print the logs. Invariant func(app *baseapp.BaseApp) error + // Account contains a privkey, pubkey, address tuple + // eventually more useful data can be placed in here. + // (e.g. number of coins) + Account struct { + PrivKey crypto.PrivKey + PubKey crypto.PubKey + Address sdk.AccAddress + } + mockValidator struct { val abci.Validator livenessState int @@ -70,3 +79,8 @@ func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant { return nil } } + +// nolint +func (acc Account) Equals(acc2 Account) bool { + return acc.Address.Equals(acc2.Address) +} diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index a46d6dd549..54bfabc73f 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -9,10 +9,12 @@ import ( "testing" "time" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mock" ) // shamelessly copied from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326 @@ -56,10 +58,10 @@ func DisplayEvents(events map[string]uint) { } } -// Pick a random key from an array -func RandomKey(r *rand.Rand, keys []crypto.PrivKey) crypto.PrivKey { - return keys[r.Intn( - len(keys), +// RandomAcc pick a random account from an array +func RandomAcc(r *rand.Rand, accs []Account) Account { + return accs[r.Intn( + len(accs), )] } @@ -68,6 +70,25 @@ func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { return sdk.NewInt(int64(r.Intn(int(max.Int64())))) } +// RandomAccounts generates n random accounts +func RandomAccounts(r *rand.Rand, n int) []Account { + accs := make([]Account, n) + for i := 0; i < n; i++ { + // don't need that much entropy for simulation + privkeySeed := make([]byte, 15) + r.Read(privkeySeed) + useSecp := r.Int63()%2 == 0 + if useSecp { + accs[i].PrivKey = secp256k1.GenPrivKeySecp256k1(privkeySeed) + } else { + accs[i].PrivKey = ed25519.GenPrivKeyFromSecret(privkeySeed) + } + accs[i].PubKey = accs[i].PrivKey.PubKey() + accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) + } + return accs +} + // Builds a function to add logs for this particular block func addLogMessage(testingmode bool, blockLogBuilders []*strings.Builder, height int) func(string) { if testingmode { @@ -92,6 +113,15 @@ func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invari } } +// RandomSetGenesis wraps mock.RandomSetGenesis, but using simulation accounts +func RandomSetGenesis(r *rand.Rand, app *mock.App, accs []Account, denoms []string) { + addrs := make([]sdk.AccAddress, len(accs)) + for i := 0; i < len(accs); i++ { + addrs[i] = accs[i].Address + } + mock.RandomSetGenesis(r, app, addrs, denoms) +} + // Creates a function to print out the logs func logPrinter(testingmode bool, logs []*strings.Builder) func() { if testingmode {