From d06c589436ba44b7448487146fe5e7b38ff5c471 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Fri, 21 Sep 2018 23:44:39 +0800 Subject: [PATCH 1/5] Merge PR #2370: Improve error handler in certifier creation --- client/context/context.go | 6 ++++-- client/rpc/validators.go | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/client/context/context.go b/client/context/context.go index db6fbef7d3..0497761c67 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -94,13 +94,15 @@ 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 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") } } From 7823b9bb100c92d2e8f2aa2efe7edd975ee0cc48 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Fri, 21 Sep 2018 08:54:32 -0700 Subject: [PATCH 2/5] Merge PR #2366: baseapp: Automatically add action tags to messages --- PENDING.md | 1 + baseapp/baseapp.go | 1 + 2 files changed, 2 insertions(+) diff --git a/PENDING.md b/PENDING.md index e07319d49e..10b0f93bb7 100644 --- a/PENDING.md +++ b/PENDING.md @@ -49,6 +49,7 @@ BREAKING CHANGES * [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 * 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 From e2da4caae435bee47f007eef2c292b04e42bcc27 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 21 Sep 2018 17:33:58 +0100 Subject: [PATCH 3/5] Merge PR #2364: Implement HTTPS for the LCD REST server In order to guarantee a secure connection between apps and the LCD the communication must be encrypted - even if clients and server run on the same local machine, credentials must never be transmitted in clear text. Upon start up, the server generates a self-signed certificate and a key. Both are stored as temporary files; removal is guaranteed on exit. This new behaviour is now enabled by default, though users are provided with a --insecure flag to switch it off. --- PENDING.md | 1 + client/lcd/certificates.go | 174 ++++++++++++++++++++++++++++++++ client/lcd/certificates_test.go | 93 +++++++++++++++++ client/lcd/root.go | 90 +++++++++++++++-- docs/light/getting_started.md | 20 +++- 5 files changed, 365 insertions(+), 13 deletions(-) create mode 100644 client/lcd/certificates.go create mode 100644 client/lcd/certificates_test.go diff --git a/PENDING.md b/PENDING.md index 10b0f93bb7..ab0a42d45d 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 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/root.go b/client/lcd/root.go index 5fa8dc0471..36fbd42d0b 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -1,6 +1,8 @@ package lcd import ( + "errors" + "net" "net/http" "os" @@ -23,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) }) @@ -61,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") @@ -95,3 +150,16 @@ func createHandler(cdc *codec.Codec) http.Handler { 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/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 From 0ac03014676a55ce4fdc58979620389e33a3b154 Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 21 Sep 2018 20:37:42 -0400 Subject: [PATCH 4/5] Merge PR #2371: how to build docs locally --- docs/{ => .vuepress}/config.js | 0 docs/.vuepress/enhanceApp.js | 3 +++ docs/.vuepress/override.styl | 4 ++++ docs/DOCS_README.md | 33 +++++++++++++++++++++++++++++---- 4 files changed, 36 insertions(+), 4 deletions(-) rename docs/{ => .vuepress}/config.js (100%) create mode 100644 docs/.vuepress/enhanceApp.js create mode 100644 docs/.vuepress/override.styl diff --git a/docs/config.js b/docs/.vuepress/config.js similarity index 100% rename from docs/config.js rename to docs/.vuepress/config.js 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 From 6835adb477953991b4db95f47b2ea215fa3a0b66 Mon Sep 17 00:00:00 2001 From: gamarin2 Date: Sat, 22 Sep 2018 02:41:16 +0200 Subject: [PATCH 5/5] Merge PR #1652: Service Provider doc * Add wip service provider doc * rm space * small modif * Finalize service provider doc * Integrate jb comments --- docs/clients/service-providers.md | 111 ++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 docs/clients/service-providers.md 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