Merge remote-tracking branch 'origin/develop' into rigel/fee-distribution
This commit is contained in:
commit
47e899bb61
9
Gopkg.lock
generated
9
Gopkg.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
14
PENDING.md
14
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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 (
|
||||
|
||||
174
client/lcd/certificates.go
Normal file
174
client/lcd/certificates.go
Normal file
@ -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)
|
||||
}
|
||||
93
client/lcd/certificates_test.go
Normal file
93
client/lcd/certificates_test.go
Normal file
@ -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)
|
||||
}
|
||||
@ -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})
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
35
cmd/gaia/app/benchmarks/txsize_test.go
Normal file
35
cmd/gaia/app/benchmarks/txsize_test.go
Normal file
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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())
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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"
|
||||
]
|
||||
},
|
||||
{
|
||||
3
docs/.vuepress/enhanceApp.js
Normal file
3
docs/.vuepress/enhanceApp.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default ({ router }) => {
|
||||
router.addRoutes([{ path: "/testnet/", redirect: "/" }])
|
||||
}
|
||||
4
docs/.vuepress/override.styl
Normal file
4
docs/.vuepress/override.styl
Normal file
@ -0,0 +1,4 @@
|
||||
$accentColor = #304DE9
|
||||
$textColor = #15192C
|
||||
$borderColor = #eaecef
|
||||
$codeBgColor = #282c34
|
||||
@ -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
|
||||
|
||||
|
||||
111
docs/clients/service-providers.md
Normal file
111
docs/clients/service-providers.md
Normal file
@ -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 <your_key_name>
|
||||
```
|
||||
|
||||
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 <YOUR_ADDRESS>
|
||||
```
|
||||
|
||||
*Note: When you query an account balance with zero tokens, you will get this error: No account with address <YOUR_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_of_testnet_chain> --name=<key_name> --to=<destination_address>
|
||||
```
|
||||
|
||||
Flags:
|
||||
- `--amount`: This flag accepts the format `<value|coinName>`.
|
||||
- `--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=<full_node_address:full_node_port>
|
||||
```
|
||||
|
||||
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 <full_node_address:full_node_port>. 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 <rest_server_address:port>.
|
||||
|
||||
### 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.
|
||||
@ -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 <account_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=<chain-name>
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
209
docs/getting-started/networks.md
Normal file
209
docs/getting-started/networks.md
Normal file
@ -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
|
||||
```
|
||||
@ -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
|
||||
|
||||
25
docs/spec/bank/WIP_keeper.md
Normal file
25
docs/spec/bank/WIP_keeper.md
Normal file
@ -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.
|
||||
@ -1,2 +0,0 @@
|
||||
- SDK related specifications (ie. how multistore, signatures, etc. work).
|
||||
- Basecoin (SendTx)
|
||||
@ -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, <proposalID|'proposal'>) // 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))
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -35,9 +35,16 @@ gaiacli stake create-validator \
|
||||
--address-validator=<account_cosmosval>
|
||||
--moniker="choose a moniker" \
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name>
|
||||
--name=<key_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=<chain_id> \
|
||||
--name=<key_name>
|
||||
--name=<key_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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.
|
||||
@ -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.
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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("{}")
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user