Merge remote-tracking branch 'origin/develop' into rigel/fee-distribution

This commit is contained in:
rigelrozanski 2018-09-25 17:45:48 -04:00
commit 47e899bb61
51 changed files with 1182 additions and 438 deletions

9
Gopkg.lock generated
View File

@ -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",
]

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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
View 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)
}

View 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)
}

View File

@ -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})

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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)

View 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
}

View File

@ -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)
}

View File

@ -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))

View File

@ -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,
)

View File

@ -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)

View File

@ -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))
}

View File

@ -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())
}

View File

@ -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)

View File

@ -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"
]
},
{

View File

@ -0,0 +1,3 @@
export default ({ router }) => {
router.addRoutes([{ path: "/testnet/", redirect: "/" }])
}

View File

@ -0,0 +1,4 @@
$accentColor = #304DE9
$textColor = #15192C
$borderColor = #eaecef
$codeBgColor = #282c34

View File

@ -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

View 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.

View File

@ -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
```

View 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
```

View File

@ -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

View 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.

View File

@ -1,2 +0,0 @@
- SDK related specifications (ie. how multistore, signatures, etc. work).
- Basecoin (SendTx)

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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(

View File

@ -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

View File

@ -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
}

View File

@ -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("{}")
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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)
}

View File

@ -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 {