Merge branch 'dev/change_dec_marshaljson' of github.com:cosmos/cosmos-sdk into dev/change_dec_marshaljson

This commit is contained in:
ValarDragon 2018-10-20 13:32:59 -07:00
commit 864ca4e982
178 changed files with 3327 additions and 3960 deletions

View File

@ -7,7 +7,7 @@
BUG FIXES
* Tendermint
- Fix unbounded consensus WAL growth
- Fix unbounded consensus WAL growth
## 0.24.1

44
Gopkg.lock generated
View File

@ -48,12 +48,19 @@
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
digest = "1:e8a3550c8786316675ff54ad6f09d265d129c9d986919af7f541afba50d87ce2"
name = "github.com/cosmos/go-bip39"
packages = ["."]
pruneopts = "UT"
revision = "52158e4697b87de16ed390e1bdaf813e581008fa"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
@ -390,8 +397,7 @@
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5"
digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
@ -408,7 +414,7 @@
"leveldb/util",
]
pruneopts = "UT"
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
[[projects]]
digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f"
@ -446,7 +452,7 @@
version = "v0.11.0"
[[projects]]
digest = "1:a69eebd15b05045ffdb10a984e001fadc5666f74383de3d2a9ee5862ee99cfdc"
digest = "1:f9c7a1f3ee087476f4883c33cc7c1bdbe56b9670b2fb27855ea2f386393272f5"
name = "github.com/tendermint/tendermint"
packages = [
"abci/client",
@ -512,8 +518,8 @@
"version",
]
pruneopts = "UT"
revision = "0c9c3292c918617624f6f3fbcd95eceade18bcd5"
version = "v0.25.0"
revision = "90eda9bfb6e6daeed1c8015df41cb36772d91778"
version = "v0.25.1-rc0"
[[projects]]
digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666"
@ -524,10 +530,10 @@
version = "v0.1.0"
[[projects]]
branch = "master"
digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a"
digest = "1:aaff04fa01d9b824fde6799759cc597b3ac3671b9ad31924c28b6557d0ee5284"
name = "golang.org/x/crypto"
packages = [
"bcrypt",
"blowfish",
"chacha20poly1305",
"curve25519",
@ -544,7 +550,8 @@
"salsa20/salsa",
]
pruneopts = "UT"
revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
source = "https://github.com/tendermint/crypto"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
@ -563,15 +570,14 @@
revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f"
[[projects]]
branch = "master"
digest = "1:8bc8ecef1d63576cfab4d08b44a1f255dd67e5b019b7a44837d62380f266a91c"
digest = "1:4bd75b1a219bc590b05c976bbebf47f4e993314ebb5c7cbf2efe05a09a184d54"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@ -597,12 +603,11 @@
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:1e6b0176e8c5dd8ff551af65c76f8b73a99bcf4d812cedff1b91711b7df4804c"
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "c7e5094acea1ca1b899e2259d80a6b0f882f81f8"
revision = "383e8b2c3b9e36c4076b235b32537292176bae20"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
@ -653,6 +658,7 @@
"github.com/bartekn/go-bip39",
"github.com/bgentry/speakeasy",
"github.com/btcsuite/btcd/btcec",
"github.com/cosmos/go-bip39",
"github.com/golang/protobuf/proto",
"github.com/gorilla/mux",
"github.com/mattn/go-isatty",
@ -699,7 +705,7 @@
"github.com/tendermint/tendermint/types",
"github.com/tendermint/tendermint/version",
"github.com/zondax/ledger-goclient",
"golang.org/x/crypto/blowfish",
"golang.org/x/crypto/bcrypt",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -57,24 +57,53 @@
[[override]]
name = "github.com/tendermint/tendermint"
version = "=0.25.0"
version = "=0.25.1-rc0"
## deps without releases:
[[override]]
name = "golang.org/x/crypto"
source = "https://github.com/tendermint/crypto"
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
[[constraint]]
name = "github.com/bartekn/go-bip39"
revision = "a05967ea095d81c8fe4833776774cfaff8e5036c"
name = "github.com/cosmos/go-bip39"
revision = "52158e4697b87de16ed390e1bdaf813e581008fa"
[[constraint]]
name = "github.com/zondax/ledger-goclient"
version = "=v0.1.0"
## transitive deps, with releases:
[[override]]
name = "github.com/davecgh/go-spew"
version = "=v1.1.0"
[[constraint]]
name = "github.com/rakyll/statik"
version = "=v0.1.4"
[[constraint]]
name = "github.com/mitchellh/go-homedir"
version = "1.0.0"
## transitive deps, without releases:
#
[[override]]
name = "github.com/syndtr/goleveldb"
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
[[override]]
name = "golang.org/x/sys"
revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf"
[[override]]
name = "google.golang.org/genproto"
revision = "383e8b2c3b9e36c4076b235b32537292176bae20"
[prune]
go-tests = true
unused-packages = true
[[constraint]]
name = "github.com/mitchellh/go-homedir"
version = "1.0.0"

View File

@ -20,6 +20,7 @@ BREAKING CHANGES
* [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
* [cli] [\#1184](https://github.com/cosmos/cosmos-sdk/issues/1184) Subcommands reorganisation, see [\#2390](https://github.com/cosmos/cosmos-sdk/pull/2390) for a comprehensive list of changes.
* [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on.
* Gaia
* Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013)
@ -44,6 +45,13 @@ BREAKING CHANGES
* [simulation] \#2162 Added back correct supply invariants
* [x/slashing] \#2430 Simulate more slashes, check if validator is jailed before jailing
* [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker
* [x/stake] \#1673 Validators are no longer deleted until they can no longer possibly be slashed
* [\#1890](https://github.com/cosmos/cosmos-sdk/issues/1890) Start chain with initial state + sequence of transactions
* [cli] Rename `gaiad init gentx` to `gaiad gentx`.
* [cli] Add `--skip-genesis` flag to `gaiad init` to prevent `genesis.json` generation.
* Drop `GenesisTx` in favor of a signed `StdTx` with only one `MsgCreateValidator` message.
* [cli] Port `gaiad init` and `gaiad testnet` to work with `StdTx` genesis transactions.
* [cli] Add `--moniker` flag to `gaiad init` to override moniker when generating `genesis.json` - i.e. it takes effect when running with the `--with-txs` flag, it is ignored otherwise.
* SDK
* [core] \#2219 Update to Tendermint 0.24.0
@ -69,11 +77,14 @@ BREAKING CHANGES
* [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index
* [x/staking] \#2236 more distribution hooks for distribution
* [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock
* [x/slashing] \#2480 Fix signing info handling bugs & faulty slashing
* [x/stake] \#2412 Added an unbonding validator queue to EndBlock to automatically update validator.Status when finished Unbonding
* [x/stake] \#2500 Block conflicting redelegations until we add an index
* [x/params] Global Paramstore refactored
* [types] \#2506 sdk.Dec MarshalJSON now marshals as a normal Decimal, with 10 digits of decimal precision
* [x/stake] \#2508 Utilize Tendermint power for validator power key
* [x/stake] \#2531 Remove all inflation logic
* [x/mint] \#2531 Add minting module and inflation logic
* Tendermint
* Update tendermint version from v0.23.0 to v0.25.0, notable changes
@ -114,6 +125,7 @@ FEATURES
* [cli] \#2220 Add `gaiacli config` feature to interactively create CLI config files to reduce the number of required flags
* [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced
new commission flags for validator commands `create-validator` and `edit-validator`.
* [stake][cli] [\#1890](https://github.com/cosmos/cosmos-sdk/issues/1890) Add `--genesis-format` flag to `gaiacli tx create-validator` to produce transactions in genesis-friendly format.
* Gaia
* [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address`
@ -141,6 +153,9 @@ IMPROVEMENTS
* Gaia REST API (`gaiacli advanced rest-server`)
* [x/stake] [\#2000](https://github.com/cosmos/cosmos-sdk/issues/2000) Added tests for new staking endpoints
* [gaia-lite] [\#2445](https://github.com/cosmos/cosmos-sdk/issues/2445) Standarized REST error responses
* [gaia-lite] Added example to Swagger specification for /keys/seed.
* [x/stake] Refactor REST utils
* Gaia CLI (`gaiacli`)
* [cli] #2060 removed `--select` from `block` command

View File

@ -55,15 +55,11 @@ func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (*ctypes.ResultBroadc
}
if !res.CheckTx.IsOK() {
return res, errors.Errorf("checkTx failed: (%d) %s",
res.CheckTx.Code,
res.CheckTx.Log)
return res, errors.Errorf(res.CheckTx.Log)
}
if !res.DeliverTx.IsOK() {
return res, errors.Errorf("deliverTx failed: (%d) %s",
res.DeliverTx.Code,
res.DeliverTx.Log)
return res, errors.Errorf(res.DeliverTx.Log)
}
return res, err

View File

@ -168,7 +168,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
resp := result.Response
if !resp.IsOK() {
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
return res, errors.Errorf(resp.Log)
}
// data from trusted node or subspace query doesn't need verification

View File

@ -7,7 +7,7 @@ import (
"strings"
"github.com/bgentry/speakeasy"
isatty "github.com/mattn/go-isatty"
"github.com/mattn/go-isatty"
"github.com/pkg/errors"
)
@ -44,13 +44,8 @@ func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) {
// GetSeed will request a seed phrase from stdin and trims off
// leading/trailing spaces
func GetSeed(prompt string, buf *bufio.Reader) (seed string, err error) {
if inputIsTty() {
fmt.Println(prompt)
}
seed, err = readLineFromBuf(buf)
seed = strings.TrimSpace(seed)
return
func GetSeed(prompt string, buf *bufio.Reader) (string, error) {
return GetString(prompt, buf)
}
// GetCheckPassword will prompt for a password twice to verify they
@ -133,5 +128,6 @@ func readLineFromBuf(buf *bufio.Reader) (string, error) {
// PrintPrefixed prints a string with > prefixed for use in prompts.
func PrintPrefixed(msg string) {
fmt.Printf("> %s\n", msg)
msg = fmt.Sprintf("> %s\n", msg)
fmt.Fprint(os.Stderr, msg)
}

View File

@ -262,6 +262,8 @@ func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
algo := keys.SigningAlgo(algoType)
seed := getSeed(algo)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(seed))
}

78
client/keys/mnemonic.go Normal file
View File

@ -0,0 +1,78 @@
package keys
import (
"crypto/sha256"
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/spf13/cobra"
bip39 "github.com/bartekn/go-bip39"
)
const (
flagUserEntropy = "unsafe-entropy"
mnemonicEntropySize = 256
)
func mnemonicKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "mnemonic",
Short: "Compute the bip39 mnemonic for some input entropy",
Long: "Create a bip39 mnemonic, sometimes called a seed phrase, by reading from the system entropy. To pass your own entropy, use --unsafe-entropy",
RunE: runMnemonicCmd,
}
cmd.Flags().Bool(flagUserEntropy, false, "Prompt the user to supply their own entropy, instead of relying on the system")
return cmd
}
func runMnemonicCmd(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()
userEntropy, _ := flags.GetBool(flagUserEntropy)
var entropySeed []byte
if userEntropy {
// prompt the user to enter some entropy
buf := client.BufferStdin()
inputEntropy, err := client.GetString("> WARNING: Generate at least 256-bits of entropy and enter the results here:", buf)
if err != nil {
return err
}
if len(inputEntropy) < 43 {
return fmt.Errorf("256-bits is 43 characters in Base-64, and 100 in Base-6. You entered %v, and probably want more", len(inputEntropy))
}
conf, err := client.GetConfirmation(
fmt.Sprintf("> Input length: %d", len(inputEntropy)),
buf)
if err != nil {
return err
}
if !conf {
return nil
}
// hash input entropy to get entropy seed
hashedEntropy := sha256.Sum256([]byte(inputEntropy))
entropySeed = hashedEntropy[:]
printStep()
} else {
// read entropy seed straight from crypto.Rand
var err error
entropySeed, err = bip39.NewEntropy(mnemonicEntropySize)
if err != nil {
return err
}
}
mnemonic, err := bip39.NewMnemonic(entropySeed[:])
if err != nil {
return err
}
fmt.Println(mnemonic)
return nil
}

182
client/keys/new.go Normal file
View File

@ -0,0 +1,182 @@
package keys
import (
"bufio"
"fmt"
"os"
"github.com/bartekn/go-bip39"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
)
const (
flagNewDefault = "default"
flagBIP44Path = "bip44-path"
)
func newKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "new",
Short: "Interactive command to derive a new private key, encrypt it, and save to disk",
Long: `Derive a new private key using an interactive command that will prompt you for each input.
Optionally specify a bip39 mnemonic, a bip39 passphrase to further secure the mnemonic,
and a bip32 HD path to derive a specific account. The key will be stored under the given name
and encrypted with the given password. The only input that is required is the encryption password.`,
Args: cobra.ExactArgs(1),
RunE: runNewCmd,
}
cmd.Flags().Bool(flagNewDefault, false, "Skip the prompts and just use the default values for everything")
cmd.Flags().Bool(client.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device")
cmd.Flags().String(flagBIP44Path, "44'/118'/0'/0/0", "BIP44 path from which to derive a private key")
return cmd
}
/*
input
- bip39 mnemonic
- bip39 passphrase
- bip44 path
- local encryption password
output
- armor encrypted private key (saved to file)
*/
// nolint: gocyclo
func runNewCmd(cmd *cobra.Command, args []string) error {
name := args[0]
kb, err := GetKeyBase()
if err != nil {
return err
}
buf := client.BufferStdin()
_, err = kb.Get(name)
if err == nil {
// account exists, ask for user confirmation
if response, err := client.GetConfirmation(
fmt.Sprintf("> override the existing name %s", name), buf); err != nil || !response {
return err
}
}
flags := cmd.Flags()
useDefaults, _ := flags.GetBool(flagNewDefault)
bipFlag := flags.Lookup(flagBIP44Path)
bip44Params, err := getBIP44ParamsAndPath(bipFlag.Value.String(), bipFlag.Changed || useDefaults)
if err != nil {
return err
}
// if we're using ledger, only thing we need is the path.
// generate key and we're done.
if viper.GetBool(client.FlagUseLedger) {
algo := keys.Secp256k1 // SigningAlgo(viper.GetString(flagType))
path := bip44Params.DerivationPath() // ccrypto.DerivationPath{44, 118, account, 0, index}
info, err := kb.CreateLedger(name, path, algo)
if err != nil {
return err
}
printCreate(info, "")
return nil
}
// get the mnemonic
var mnemonic string
if !useDefaults {
mnemonic, err = client.GetString("> Enter your bip39 mnemonic, or hit enter to generate one.", buf)
if err != nil {
return err
}
}
if len(mnemonic) == 0 {
// read entropy seed straight from crypto.Rand and convert to mnemonic
entropySeed, err := bip39.NewEntropy(mnemonicEntropySize)
if err != nil {
return err
}
mnemonic, err = bip39.NewMnemonic(entropySeed[:])
if err != nil {
return err
}
}
// get bip39 passphrase
var bip39Passphrase string
if !useDefaults {
printStep()
printPrefixed("Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed")
bip39Passphrase, err = client.GetString("> Most users should just hit enter to use the default, \"\"", buf)
if err != nil {
return err
}
// if they use one, make them re-enter it
if len(bip39Passphrase) != 0 {
p2, err := client.GetString("Repeat the passphrase:", buf)
if err != nil {
return err
}
if bip39Passphrase != p2 {
return errors.New("passphrases don't match")
}
}
}
// get the encryption password
printStep()
encryptPassword, err := client.GetCheckPassword(
"> Enter a passphrase to encrypt your key to disk:",
"> Repeat the passphrase:", buf)
if err != nil {
return err
}
info, err := kb.Derive(name, mnemonic, bip39Passphrase, encryptPassword, *bip44Params)
if err != nil {
return err
}
_ = info
return nil
}
func getBIP44ParamsAndPath(path string, flagSet bool) (*hd.BIP44Params, error) {
buf := bufio.NewReader(os.Stdin)
bip44Path := path
// if it wasnt set in the flag, give it a chance to overide interactively
if !flagSet {
printStep()
var err error
bip44Path, err = client.GetString(fmt.Sprintf("> Enter your bip44 path. Default is %s\n", path), buf)
if err != nil {
return nil, err
}
if len(bip44Path) == 0 {
bip44Path = path
}
}
bip44params, err := hd.NewParamsFromPath(bip44Path)
if err != nil {
return nil, err
}
return bip44params, nil
}
func printPrefixed(msg string) {
fmt.Printf("> %s\n", msg)
}
func printStep() {
printPrefixed("-------------------------------------")
}

View File

@ -19,6 +19,8 @@ func Commands() *cobra.Command {
needs to sign with a private key.`,
}
cmd.AddCommand(
mnemonicKeyCommand(),
newKeyCommand(),
addKeyCommand(),
listKeysCmd,
showKeysCmd(),

View File

@ -91,5 +91,6 @@ func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}

View File

@ -43,7 +43,7 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
IsCA: true,
}
hosts := strings.Split(host, ",")
for _, h := range hosts {

View File

@ -10,20 +10,20 @@ import (
"testing"
"time"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
p2p "github.com/tendermint/tendermint/p2p"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
p2p "github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
client "github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
tests "github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
version "github.com/cosmos/cosmos-sdk/version"
@ -35,7 +35,7 @@ import (
)
func init() {
cryptoKeys.BcryptSecurityParameter = 1
mintkey.BcryptSecurityParameter = 1
version.Version = os.Getenv("VERSION")
}
@ -480,11 +480,7 @@ func TestPoolParamsQuery(t *testing.T) {
var pool stake.Pool
err = cdc.UnmarshalJSON([]byte(body), &pool)
require.Nil(t, err)
require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset)
require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares)
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
require.Equal(t, initialPool.NextInflation(params), pool.Inflation)
initialPool = initialPool.ProcessProvisions(params) // provisions are added to the pool every hour
require.Equal(t, initialPool.LooseTokens, pool.LooseTokens)
}
@ -496,7 +492,7 @@ func TestValidatorsQuery(t *testing.T) {
require.Equal(t, 1, len(operAddrs))
validators := getValidators(t, port)
require.Equal(t, len(validators), 1)
require.Equal(t, 1, len(validators), fmt.Sprintf("%+v", validators))
// make sure all the validators were found (order unknown because sorted by operator addr)
foundVal := false
@ -713,59 +709,57 @@ func TestUnjail(t *testing.T) {
tests.WaitForHeight(4, port)
require.Equal(t, true, signingInfo.IndexOffset > 0)
require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil)
require.Equal(t, true, signingInfo.SignedBlocksCounter > 0)
require.Equal(t, true, signingInfo.MissedBlocksCounter == 0)
}
func TestProposalsQuery(t *testing.T) {
name, password1 := "test", "1234567890"
name2, password2 := "test2", "1234567890"
addr, seed := CreateAddr(t, "test", password1, GetKeyBase(t))
addr2, seed2 := CreateAddr(t, "test2", password2, GetKeyBase(t))
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr, addr2})
addrs, seeds, names, passwords := CreateAddrs(t, GetKeyBase(t), 2)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addrs[0], addrs[1]})
defer cleanup()
// Addr1 proposes (and deposits) proposals #1 and #2
resultTx := doSubmitProposal(t, port, seed, name, password1, addr, 5)
resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
var proposalID1 int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID1)
tests.WaitForHeight(resultTx.Height+1, port)
resultTx = doSubmitProposal(t, port, seed, name, password1, addr, 5)
resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
var proposalID2 int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID2)
tests.WaitForHeight(resultTx.Height+1, port)
// Addr2 proposes (and deposits) proposals #3
resultTx = doSubmitProposal(t, port, seed2, name2, password2, addr2, 5)
resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5)
var proposalID3 int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID3)
tests.WaitForHeight(resultTx.Height+1, port)
// Addr2 deposits on proposals #2 & #3
resultTx = doDeposit(t, port, seed2, name2, password2, addr2, proposalID2, 5)
resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID2, 5)
tests.WaitForHeight(resultTx.Height+1, port)
resultTx = doDeposit(t, port, seed2, name2, password2, addr2, proposalID3, 5)
resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID3, 5)
tests.WaitForHeight(resultTx.Height+1, port)
// check deposits match proposal and individual deposits
deposits := getDeposits(t, port, proposalID1)
require.Len(t, deposits, 1)
deposit := getDeposit(t, port, proposalID1, addr)
deposit := getDeposit(t, port, proposalID1, addrs[0])
require.Equal(t, deposit, deposits[0])
deposits = getDeposits(t, port, proposalID2)
require.Len(t, deposits, 2)
deposit = getDeposit(t, port, proposalID2, addr)
require.Equal(t, deposit, deposits[0])
deposit = getDeposit(t, port, proposalID2, addr2)
require.Equal(t, deposit, deposits[1])
deposit = getDeposit(t, port, proposalID2, addrs[0])
require.True(t, deposit.Equals(deposits[0]))
deposit = getDeposit(t, port, proposalID2, addrs[1])
require.True(t, deposit.Equals(deposits[1]))
deposits = getDeposits(t, port, proposalID3)
require.Len(t, deposits, 1)
deposit = getDeposit(t, port, proposalID3, addr2)
deposit = getDeposit(t, port, proposalID3, addrs[1])
require.Equal(t, deposit, deposits[0])
// increasing the amount of the deposit should update the existing one
resultTx = doDeposit(t, port, seed, name, password1, addr, proposalID1, 1)
resultTx = doDeposit(t, port, seeds[0], names[0], passwords[0], addrs[0], proposalID1, 1)
tests.WaitForHeight(resultTx.Height+1, port)
deposits = getDeposits(t, port, proposalID1)
@ -782,13 +776,13 @@ func TestProposalsQuery(t *testing.T) {
require.Equal(t, proposalID3, proposals[1].GetProposalID())
// Addr1 votes on proposals #2 & #3
resultTx = doVote(t, port, seed, name, password1, addr, proposalID2)
resultTx = doVote(t, port, seeds[0], names[0], passwords[0], addrs[0], proposalID2)
tests.WaitForHeight(resultTx.Height+1, port)
resultTx = doVote(t, port, seed, name, password1, addr, proposalID3)
resultTx = doVote(t, port, seeds[0], names[0], passwords[0], addrs[0], proposalID3)
tests.WaitForHeight(resultTx.Height+1, port)
// Addr2 votes on proposal #3
resultTx = doVote(t, port, seed2, name2, password2, addr2, proposalID3)
resultTx = doVote(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID3)
tests.WaitForHeight(resultTx.Height+1, port)
// Test query all proposals
@ -798,37 +792,37 @@ func TestProposalsQuery(t *testing.T) {
require.Equal(t, proposalID3, (proposals[2]).GetProposalID())
// Test query deposited by addr1
proposals = getProposalsFilterDepositer(t, port, addr)
proposals = getProposalsFilterDepositer(t, port, addrs[0])
require.Equal(t, proposalID1, (proposals[0]).GetProposalID())
// Test query deposited by addr2
proposals = getProposalsFilterDepositer(t, port, addr2)
proposals = getProposalsFilterDepositer(t, port, addrs[1])
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
require.Equal(t, proposalID3, (proposals[1]).GetProposalID())
// Test query voted by addr1
proposals = getProposalsFilterVoter(t, port, addr)
proposals = getProposalsFilterVoter(t, port, addrs[0])
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
require.Equal(t, proposalID3, (proposals[1]).GetProposalID())
// Test query voted by addr2
proposals = getProposalsFilterVoter(t, port, addr2)
proposals = getProposalsFilterVoter(t, port, addrs[1])
require.Equal(t, proposalID3, (proposals[0]).GetProposalID())
// Test query voted and deposited by addr1
proposals = getProposalsFilterVoterDepositer(t, port, addr, addr)
proposals = getProposalsFilterVoterDepositer(t, port, addrs[0], addrs[0])
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
// Test query votes on Proposal 2
votes := getVotes(t, port, proposalID2)
require.Len(t, votes, 1)
require.Equal(t, addr, votes[0].Voter)
require.Equal(t, addrs[0], votes[0].Voter)
// Test query votes on Proposal 3
votes = getVotes(t, port, proposalID3)
require.Len(t, votes, 2)
require.True(t, addr.String() == votes[0].Voter.String() || addr.String() == votes[1].Voter.String())
require.True(t, addr2.String() == votes[0].Voter.String() || addr2.String() == votes[1].Voter.String())
require.True(t, addrs[0].String() == votes[0].Voter.String() || addrs[0].String() == votes[1].Voter.String())
require.True(t, addrs[1].String() == votes[0].Voter.String() || addrs[1].String() == votes[1].Voter.String())
}
//_____________________________________________________________________________

View File

@ -400,6 +400,7 @@ paths:
description: 16 word Seed
schema:
type: string
example: blossom pool issue kidney elevator blame furnace winter account merry vessel security depend exact travel bargain problem jelly rural net again mask roast chest
/keys/{name}/recover:
post:
summary: Recover a account from a seed

View File

@ -4,16 +4,19 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/tendermint/tendermint/crypto/secp256k1"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/keys"
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
@ -25,6 +28,7 @@ import (
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
abci "github.com/tendermint/tendermint/abci/types"
tmcfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
@ -111,6 +115,69 @@ func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.Acc
return sdk.AccAddress(info.GetPubKey().Address()), seed
}
// Type that combines an Address with the pnemonic of the private key to that address
type AddrSeed struct {
Address sdk.AccAddress
Seed string
Name string
Password string
}
// CreateAddr adds multiple address to the key store and returns the addresses and associated seeds in lexographical order by address.
// It also requires that the keys could be created.
func CreateAddrs(t *testing.T, kb crkeys.Keybase, numAddrs int) (addrs []sdk.AccAddress, seeds, names, passwords []string) {
var (
err error
info crkeys.Info
seed string
)
addrSeeds := AddrSeedSlice{}
for i := 0; i < numAddrs; i++ {
name := fmt.Sprintf("test%d", i)
password := "1234567890"
info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1)
require.NoError(t, err)
addrSeeds = append(addrSeeds, AddrSeed{Address: sdk.AccAddress(info.GetPubKey().Address()), Seed: seed, Name: name, Password: password})
}
sort.Sort(addrSeeds)
for i := range addrSeeds {
addrs = append(addrs, addrSeeds[i].Address)
seeds = append(seeds, addrSeeds[i].Seed)
names = append(names, addrSeeds[i].Name)
passwords = append(passwords, addrSeeds[i].Password)
}
return addrs, seeds, names, passwords
}
// implement `Interface` in sort package.
type AddrSeedSlice []AddrSeed
func (b AddrSeedSlice) Len() int {
return len(b)
}
// Sorts lexographically by Address
func (b AddrSeedSlice) Less(i, j int) bool {
// bytes package already implements Comparable for []byte.
switch bytes.Compare(b[i].Address.Bytes(), b[j].Address.Bytes()) {
case -1:
return true
case 0, 1:
return false
default:
panic("not fail-able with `bytes.Comparable` bounded [-1, 1].")
}
}
func (b AddrSeedSlice) Swap(i, j int) {
b[j], b[i] = b[i], b[j]
}
// InitializeTestLCD starts Tendermint and the LCD in process, listening on
// their respective sockets where nValidators is the total number of validators
// and initAddrs are the accounts to initialize with some steak tokens. It
@ -141,42 +208,43 @@ func InitializeTestLCD(
genesisFile := config.GenesisFile()
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
require.NoError(t, err)
// append initial (proposing) validator
genDoc.Validators[0] = tmtypes.GenesisValidator{
PubKey: privVal.GetPubKey(),
Power: 100, // create enough power to enable 2/3 voting power
Name: "validator-1",
}
require.Nil(t, err)
genDoc.Validators = nil
genDoc.SaveAs(genesisFile)
genTxs := []json.RawMessage{}
// append any additional (non-proposing) validators
for i := 1; i < nValidators; i++ {
genDoc.Validators = append(genDoc.Validators,
tmtypes.GenesisValidator{
PubKey: ed25519.GenPrivKey().PubKey(),
Power: 1,
Name: fmt.Sprintf("validator-%d", i+1),
},
for i := 0; i < nValidators; i++ {
operPrivKey := secp256k1.GenPrivKey()
operAddr := operPrivKey.PubKey().Address()
pubKey := privVal.PubKey
delegation := 100
if i > 0 {
pubKey = ed25519.GenPrivKey().PubKey()
delegation = 1
}
msg := stake.NewMsgCreateValidator(
sdk.ValAddress(operAddr),
pubKey,
sdk.NewCoin("steak", sdk.NewInt(int64(delegation))),
stake.Description{Moniker: fmt.Sprintf("validator-%d", i+1)},
stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
)
}
var appGenTxs []json.RawMessage
for _, gdValidator := range genDoc.Validators {
operAddr := ed25519.GenPrivKey().PubKey().Address()
pk := gdValidator.PubKey
valConsPubKeys = append(valConsPubKeys, pk)
stdSignMsg := txbuilder.StdSignMsg{
ChainID: genDoc.ChainID,
Msgs: []sdk.Msg{msg},
}
sig, err := operPrivKey.Sign(stdSignMsg.Bytes())
require.Nil(t, err)
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{{Signature: sig, PubKey: operPrivKey.PubKey()}}, "")
txBytes, err := cdc.MarshalJSON(tx)
require.Nil(t, err)
genTxs = append(genTxs, txBytes)
valConsPubKeys = append(valConsPubKeys, pubKey)
valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr))
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(operAddr), gdValidator.Name)
require.NoError(t, err)
appGenTxs = append(appGenTxs, appGenTx)
}
genesisState, err := gapp.NewTestGaiaAppGenState(cdc, appGenTxs[:], genDoc.Validators, valOperAddrs)
genesisState, err := gapp.GaiaAppGenState(cdc, genTxs)
require.NoError(t, err)
// add some tokens to init accounts

View File

@ -1,10 +1,10 @@
package lcd
import (
"fmt"
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/version"
)
@ -19,11 +19,11 @@ func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
version, err := cliCtx.Query("/app/version", nil)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query version. Error: %s", err.Error())))
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(version)
}
}

View File

@ -3,9 +3,10 @@ package tx
import (
"encoding/hex"
"fmt"
"github.com/tendermint/tendermint/libs/common"
"net/http"
"github.com/tendermint/tendermint/libs/common"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
@ -142,8 +143,7 @@ func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.H
output, err := queryTx(cdc, cliCtx, hashHexStr)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
utils.PostProcessResponse(w, cdc, output, cliCtx.Indent)

View File

@ -30,7 +30,7 @@ func SearchTxCmd(cdc *codec.Codec) *cobra.Command {
Use: "txs",
Short: "Search for all transactions that match the given tags.",
Long: strings.TrimSpace(`
Search for transactions that match the given tags. By default, transactions must match ALL tags
Search for transactions that match the given tags. By default, transactions must match ALL tags
passed to the --tags option. To match any transaction, use the --any option.
For example:
@ -141,7 +141,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.
return func(w http.ResponseWriter, r *http.Request) {
tag := r.FormValue("tag")
if tag == "" {
w.WriteHeader(400)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("You need to provide at least a tag as a key=value pair to search for. Postfix the key with _bech32 to search bech32-encoded addresses or public keys"))
return
}
@ -151,8 +151,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.
value, err := url.QueryUnescape(keyValue[1])
if err != nil {
w.WriteHeader(400)
w.Write([]byte("Could not decode address: " + err.Error()))
utils.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("could not decode address", err.Error()))
return
}
@ -161,8 +160,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.
prefix := strings.Split(bech32address, "1")[0]
bz, err := sdk.GetFromBech32(bech32address, prefix)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -171,8 +169,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.
txs, err := searchTxs(cliCtx, cdc, []string{tag})
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

View File

@ -1,6 +1,5 @@
package utils
import "C"
import (
"fmt"
"io/ioutil"
@ -27,12 +26,12 @@ const (
// WriteErrorResponse prepares and writes a HTTP error
// given a status code and an error message.
func WriteErrorResponse(w http.ResponseWriter, status int, msg string) {
func WriteErrorResponse(w http.ResponseWriter, status int, err string) {
w.WriteHeader(status)
w.Write([]byte(msg))
w.Write([]byte(err))
}
// WriteGasEstimateResponse prepares and writes an HTTP
// WriteSimulationResponse prepares and writes an HTTP
// response for transactions simulations.
func WriteSimulationResponse(w http.ResponseWriter, gas int64) {
w.WriteHeader(http.StatusOK)

View File

@ -107,7 +107,8 @@ func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg
// SignStdTx appends a signature to a StdTx and returns a copy of a it. If appendSig
// is false, it replaces the signatures already attached with the new signature.
func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, stdTx auth.StdTx, appendSig bool) (auth.StdTx, error) {
// Don't perform online validation or lookups if offline is true.
func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, stdTx auth.StdTx, appendSig bool, offline bool) (auth.StdTx, error) {
var signedStdTx auth.StdTx
keybase, err := keys.GetKeyBase()
@ -122,10 +123,10 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string,
// Check whether the address is a signer
if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) {
fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'", name)
fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'\n", name)
}
if txBldr.AccountNumber == 0 {
if !offline && txBldr.AccountNumber == 0 {
accNum, err := cliCtx.GetAccountNumber(addr)
if err != nil {
return signedStdTx, err
@ -133,7 +134,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string,
txBldr = txBldr.WithAccountNumber(accNum)
}
if txBldr.Sequence == 0 {
if !offline && txBldr.Sequence == 0 {
accSeq, err := cliCtx.GetAccountSequence(addr)
if err != nil {
return signedStdTx, err

View File

@ -2,15 +2,7 @@ package app
import (
"encoding/json"
"io"
"os"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
"fmt"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -18,13 +10,24 @@ 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/mint"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
"io"
"os"
"sort"
)
const (
appName = "GaiaApp"
// DefaultKeyPass contains the default key password for genesis transactions
DefaultKeyPass = "12345678"
)
// default home directories for expected binaries
@ -44,6 +47,7 @@ type GaiaApp struct {
keyStake *sdk.KVStoreKey
tkeyStake *sdk.TransientStoreKey
keySlashing *sdk.KVStoreKey
keyMint *sdk.KVStoreKey
keyDistr *sdk.KVStoreKey
tkeyDistr *sdk.TransientStoreKey
keyGov *sdk.KVStoreKey
@ -57,6 +61,7 @@ type GaiaApp struct {
bankKeeper bank.Keeper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
paramsKeeper params.Keeper
@ -76,6 +81,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
keyAccount: sdk.NewKVStoreKey("acc"),
keyStake: sdk.NewKVStoreKey("stake"),
tkeyStake: sdk.NewTransientStoreKey("transient_stake"),
keyMint: sdk.NewKVStoreKey("mint"),
keyDistr: sdk.NewKVStoreKey("distr"),
tkeyDistr: sdk.NewTransientStoreKey("transient_distr"),
keySlashing: sdk.NewKVStoreKey("slashing"),
@ -108,6 +114,10 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace),
app.RegisterCodespace(stake.DefaultCodespace),
)
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint,
app.paramsKeeper.Subspace(mint.DefaultParamspace),
app.stakeKeeper, app.feeCollectionKeeper,
)
app.distrKeeper = distr.NewKeeper(
app.cdc,
app.keyDistr,
@ -145,11 +155,11 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
AddRoute("stake", stake.NewQuerier(app.stakeKeeper, app.cdc))
// initialize BaseApp
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keyMint, app.keyDistr,
app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
app.SetInitChainer(app.initChainer)
app.SetBeginBlocker(app.BeginBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
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)
app.SetEndBlocker(app.EndBlocker)
@ -182,6 +192,9 @@ func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab
// distribute rewards from previous block
distr.BeginBlocker(ctx, req, app.distrKeeper)
// mint new tokens for this new block
mint.BeginBlocker(ctx, app.mintKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
@ -230,14 +243,46 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
// load the address to pubkey map
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData)
distr.InitGenesis(ctx, app.distrKeeper, genesisState.DistrData)
err = GaiaValidateGenesisState(genesisState)
if err != nil {
panic(err) // TODO find a way to do this w/o panics
}
if len(genesisState.GenTxs) > 0 {
for _, genTx := range genesisState.GenTxs {
var tx auth.StdTx
err = app.cdc.UnmarshalJSON(genTx, &tx)
if err != nil {
panic(err)
}
bz := app.cdc.MustMarshalBinary(tx)
res := app.BaseApp.DeliverTx(bz)
if !res.IsOK() {
panic(res.Log)
}
}
validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
}
app.slashingKeeper.AddValidators(ctx, validators)
// sanity check
if len(req.Validators) > 0 {
if len(req.Validators) != len(validators) {
panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d) ", len(req.Validators), len(validators)))
}
sort.Sort(abci.ValidatorUpdates(req.Validators))
sort.Sort(abci.ValidatorUpdates(validators))
for i, val := range validators {
if !val.Equal(req.Validators[i]) {
panic(fmt.Errorf("validators[%d] != req.Validators[%d] ", i, i))
}
}
}
return abci.ResponseInitChain{
Validators: validators,
}
@ -255,13 +300,14 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
return false
}
app.accountMapper.IterateAccounts(ctx, appendAccount)
genState := GenesisState{
Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
DistrData: distr.WriteGenesis(ctx, app.distrKeeper),
GovData: gov.WriteGenesis(ctx, app.govKeeper),
}
genState := NewGenesisState(
accounts,
stake.WriteGenesis(ctx, app.stakeKeeper),
mint.WriteGenesis(ctx, app.mintKeeper),
distr.WriteGenesis(ctx, app.distrKeeper),
gov.WriteGenesis(ctx, app.govKeeper),
slashing.GenesisState{}, // TODO create write methods
)
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err

View File

@ -4,40 +4,52 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/spf13/pflag"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
)
// DefaultKeyPass contains the default key password for genesis transactions
const DefaultKeyPass = "12345678"
var (
// bonded tokens given to genesis validators/accounts
freeFermionVal = int64(100)
freeFermionsAcc = sdk.NewInt(50)
freeFermionsAcc = sdk.NewInt(150)
)
// State to Unmarshal
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
MintData mint.GenesisState `json:"mint"`
DistrData distr.GenesisState `json:"distr"`
GovData gov.GenesisState `json:"gov"`
SlashingData slashing.GenesisState `json:"slashing"`
GenTxs []json.RawMessage `json:"gentxs"`
}
func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mintData mint.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState {
return GenesisState{
Accounts: accounts,
StakeData: stakeData,
MintData: mintData,
DistrData: distrData,
GovData: govData,
SlashingData: slashingData,
}
}
// GenesisAccount doesn't need pubkey or sequence
@ -70,97 +82,12 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
// get app init parameters for server init command
func GaiaAppInit() server.AppInit {
fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx.String(server.FlagName, "", "validator moniker, required")
fsAppGenTx.String(server.FlagClientHome, DefaultCLIHome,
"home directory for the client, used for key generation")
fsAppGenTx.Bool(server.FlagOWK, false, "overwrite the accounts created")
return server.AppInit{
FlagsAppGenState: fsAppGenState,
FlagsAppGenTx: fsAppGenTx,
AppGenTx: GaiaAppGenTx,
AppGenState: GaiaAppGenStateJSON,
AppGenState: GaiaAppGenStateJSON,
}
}
// simple genesis tx
type GaiaGenTx struct {
Name string `json:"name"`
Address sdk.AccAddress `json:"address"`
PubKey string `json:"pub_key"`
}
// GaiaAppGenTx generates a Gaia genesis transaction.
func GaiaAppGenTx(
cdc *codec.Codec, pk crypto.PubKey, genTxConfig config.GenTx,
) (appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
if genTxConfig.Name == "" {
return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)")
}
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account '%s' (default %s):", genTxConfig.Name, DefaultKeyPass)
keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return appGenTx, cliPrint, validator, err
}
if keyPass == "" {
keyPass = DefaultKeyPass
}
addr, secret, err := server.GenerateSaveCoinKey(
genTxConfig.CliRoot,
genTxConfig.Name,
keyPass,
genTxConfig.Overwrite,
)
if err != nil {
return appGenTx, cliPrint, validator, err
}
mm := map[string]string{"secret": secret}
bz, err := cdc.MarshalJSON(mm)
if err != nil {
return appGenTx, cliPrint, validator, err
}
cliPrint = json.RawMessage(bz)
appGenTx, _, validator, err = GaiaAppGenTxNF(cdc, pk, addr, genTxConfig.Name)
return appGenTx, cliPrint, validator, err
}
// Generate a gaia genesis transaction without flags
func GaiaAppGenTxNF(cdc *codec.Codec, pk crypto.PubKey, addr sdk.AccAddress, name string) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
var bz []byte
gaiaGenTx := GaiaGenTx{
Name: name,
Address: addr,
PubKey: sdk.MustBech32ifyConsPub(pk),
}
bz, err = codec.MarshalJSONIndent(cdc, gaiaGenTx)
if err != nil {
return
}
appGenTx = json.RawMessage(bz)
validator = tmtypes.GenesisValidator{
PubKey: pk,
Power: freeFermionVal,
}
return
}
// Create the core parameters for genesis initialization for gaia
// note that the pubkey input is this machines pubkey
func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
@ -171,71 +98,48 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat
// start with the default staking genesis state
stakeData := stake.DefaultGenesisState()
slashingData := slashing.DefaultGenesisState()
// get genesis flag account information
genaccs := make([]GenesisAccount, len(appGenTxs))
for i, appGenTx := range appGenTxs {
var genTx GaiaGenTx
err = cdc.UnmarshalJSON(appGenTx, &genTx)
for i, genTx := range appGenTxs {
var tx auth.StdTx
err = cdc.UnmarshalJSON(genTx, &tx)
if err != nil {
return
}
msgs := tx.GetMsgs()
if len(msgs) != 1 {
err = errors.New("must provide genesis StdTx with exactly 1 CreateValidator message")
return
}
msg := msgs[0].(stake.MsgCreateValidator)
// create the genesis account, give'm few steaks and a buncha token with there name
genaccs[i] = genesisAccountFromGenTx(genTx)
genaccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply
// add the validator
if len(genTx.Name) > 0 {
stakeData = addValidatorToStakeData(genTx, stakeData)
}
}
// create the final app state
genesisState = GenesisState{
Accounts: genaccs,
StakeData: stakeData,
MintData: mint.DefaultGenesisState(),
DistrData: distr.DefaultGenesisState(),
GovData: gov.DefaultGenesisState(),
SlashingData: slashingData,
GenTxs: appGenTxs,
}
return
}
func addValidatorToStakeData(genTx GaiaGenTx, stakeData stake.GenesisState) stake.GenesisState {
desc := stake.NewDescription(genTx.Name, "", "", "")
validator := stake.NewValidator(
sdk.ValAddress(genTx.Address), sdk.MustGetConsPubKeyBech32(genTx.PubKey), desc,
)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(freeFermionVal)) // increase the supply
// add some new shares to the validator
var issuedDelShares sdk.Dec
validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, sdk.NewInt(freeFermionVal))
stakeData.Validators = append(stakeData.Validators, validator)
// create the self-delegation from the issuedDelShares
delegation := stake.Delegation{
DelegatorAddr: sdk.AccAddress(validator.OperatorAddr),
ValidatorAddr: validator.OperatorAddr,
Shares: issuedDelShares,
Height: 0,
}
stakeData.Bonds = append(stakeData.Bonds, delegation)
return stakeData
}
func genesisAccountFromGenTx(genTx GaiaGenTx) GenesisAccount {
accAuth := auth.NewBaseAccountWithAddress(genTx.Address)
accAuth.Coins = sdk.Coins{
{genTx.Name + "Token", sdk.NewInt(1000)},
{"steak", freeFermionsAcc},
func genesisAccountFromMsgCreateValidator(msg stake.MsgCreateValidator, amount sdk.Int) GenesisAccount {
accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr))
accAuth.Coins = []sdk.Coin{
{msg.Description.Moniker + "Token", sdk.NewInt(1000)},
{"steak", amount},
}
return NewGenesisAccount(&accAuth)
}
@ -249,11 +153,11 @@ func GaiaValidateGenesisState(genesisState GenesisState) (err error) {
if err != nil {
return
}
err = stake.ValidateGenesis(genesisState.StakeData)
if err != nil {
return
// skip stakeData validation as genesis is created from txs
if len(genesisState.GenTxs) > 0 {
return nil
}
return
return stake.ValidateGenesis(genesisState.StakeData)
}
// Ensures that there are no duplicate accounts in the genesis state,
@ -272,7 +176,6 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) {
// GaiaAppGenState but with JSON
func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
// create the final app state
genesisState, err := GaiaAppGenState(cdc, appGenTxs)
if err != nil {
@ -281,3 +184,75 @@ func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appStat
appState, err = codec.MarshalJSONIndent(cdc, genesisState)
return
}
// CollectStdTxs processes and validates application's genesis StdTxs and returns the list of validators,
// appGenTxs, and persistent peers required to generate genesis.json.
func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) (
validators []tmtypes.GenesisValidator, appGenTxs []auth.StdTx, persistentPeers string, err error) {
var fos []os.FileInfo
fos, err = ioutil.ReadDir(genTxsDir)
if err != nil {
return
}
var addresses []string
for _, fo := range fos {
filename := filepath.Join(genTxsDir, fo.Name())
if !fo.IsDir() && (filepath.Ext(filename) != ".json") {
continue
}
// get the genStdTx
var jsonRawTx []byte
jsonRawTx, err = ioutil.ReadFile(filename)
if err != nil {
return
}
var genStdTx auth.StdTx
err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx)
if err != nil {
return
}
appGenTxs = append(appGenTxs, genStdTx)
nodeAddr := genStdTx.GetMemo()
if len(nodeAddr) == 0 {
err = fmt.Errorf("couldn't find node's address in %s", fo.Name())
return
}
msgs := genStdTx.GetMsgs()
if len(msgs) != 1 {
err = errors.New("each genesis transaction must provide a single genesis message")
return
}
// TODO: this could be decoupled from stake.MsgCreateValidator
// TODO: and we likely want to do it for real world Gaia
msg := msgs[0].(stake.MsgCreateValidator)
validators = append(validators, tmtypes.GenesisValidator{
PubKey: msg.PubKey,
Power: freeFermionVal,
Name: msg.Description.Moniker,
})
// exclude itself from persistent peers
if msg.Description.Moniker != moniker {
addresses = append(addresses, nodeAddr)
}
}
sort.Strings(addresses)
persistentPeers = strings.Join(addresses, ",")
return
}
func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount {
accAuth := auth.NewBaseAccountWithAddress(addr)
accAuth.Coins = []sdk.Coin{
{"fooToken", sdk.NewInt(1000)},
{"steak", freeFermionsAcc},
}
return NewGenesisAccount(&accAuth)
}

View File

@ -25,25 +25,24 @@ var (
emptyPubkey crypto.PubKey
)
func makeGenesisState(genTxs []GaiaGenTx) GenesisState {
func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState {
// start with the default staking genesis state
stakeData := stake.DefaultGenesisState()
genAccs := make([]GenesisAccount, len(genTxs))
// get genesis flag account information
genaccs := make([]GenesisAccount, len(genTxs))
for i, genTx := range genTxs {
genaccs[i] = genesisAccountFromGenTx(genTx)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply
msgs := genTx.GetMsgs()
require.Equal(t, 1, len(msgs))
msg := msgs[0].(stake.MsgCreateValidator)
// add the validator
if len(genTx.Name) > 0 {
stakeData = addValidatorToStakeData(genTx, stakeData)
}
// get genesis flag account information
genAccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply
}
// create the final app state
return GenesisState{
Accounts: genaccs,
Accounts: genAccs,
StakeData: stakeData,
GovData: gov.DefaultGenesisState(),
}
@ -75,17 +74,23 @@ func TestGaiaAppGenState(t *testing.T) {
// TODO correct: genesis account created, canididates created, pool token variance
}
func makeMsg(name string, pk crypto.PubKey) auth.StdTx {
desc := stake.NewDescription(name, "", "", "")
comm := stakeTypes.CommissionMsg{}
msg := stake.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin("steak", 50), desc, comm)
return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "")
}
func TestGaiaGenesisValidation(t *testing.T) {
genTxs := make([]GaiaGenTx, 2)
addr := pk1.Address()
genTxs := make([]auth.StdTx, 2)
// Test duplicate accounts fails
genTxs[0] = GaiaGenTx{"", sdk.AccAddress(addr), ""}
genTxs[1] = GaiaGenTx{"", sdk.AccAddress(addr), ""}
genesisState := makeGenesisState(genTxs)
genTxs[0] = makeMsg("test-0", pk1)
genTxs[1] = makeMsg("test-1", pk1)
genesisState := makeGenesisState(t, genTxs)
err := GaiaValidateGenesisState(genesisState)
require.NotNil(t, err)
// Test bonded + jailed validator fails
genesisState = makeGenesisState(genTxs[:1])
genesisState = makeGenesisState(t, genTxs)
val1 := stakeTypes.NewValidator(addr1, pk1, stakeTypes.Description{Moniker: "test #2"})
val1.Jailed = true
val1.Status = sdk.Bonded
@ -94,7 +99,7 @@ func TestGaiaGenesisValidation(t *testing.T) {
require.NotNil(t, err)
// Test duplicate validator fails
val1.Jailed = false
genesisState = makeGenesisState(genTxs[:1])
genesisState = makeGenesisState(t, genTxs)
val2 := stakeTypes.NewValidator(addr1, pk1, stakeTypes.Description{Moniker: "test #3"})
genesisState.StakeData.Validators = append(genesisState.StakeData.Validators, val1)
genesisState.StakeData.Validators = append(genesisState.StakeData.Validators, val2)

View File

@ -18,6 +18,7 @@ import (
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/slashing"
slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation"
@ -79,13 +80,16 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
stakeGenesis.Pool.LooseTokens = sdk.NewDec(int64(100*250) + (numInitiallyBonded * 100))
stakeGenesis.Validators = validators
stakeGenesis.Bonds = delegations
// No inflation, for now
stakeGenesis.Params.InflationMax = sdk.NewDec(0)
stakeGenesis.Params.InflationMin = sdk.NewDec(0)
mintGenesis := mint.DefaultGenesisState()
mintGenesis.Params.InflationMax = sdk.NewDec(0)
mintGenesis.Params.InflationMin = sdk.NewDec(0)
genesis := GenesisState{
Accounts: genesisAccounts,
StakeData: stakeGenesis,
MintData: mintGenesis,
DistrData: distr.DefaultGenesisWithValidators(valAddrs),
SlashingData: slashingGenesis,
GovData: govGenesis,

View File

@ -1,80 +0,0 @@
package app
import (
"encoding/json"
"errors"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
tmtypes "github.com/tendermint/tendermint/types"
)
// NewTestGaiaAppGenState creates the core parameters for a test genesis
// initialization given a set of genesis txs, TM validators and their respective
// operating addresses.
func NewTestGaiaAppGenState(
cdc *codec.Codec, appGenTxs []json.RawMessage, tmVals []tmtypes.GenesisValidator, valOperAddrs []sdk.ValAddress,
) (GenesisState, error) {
switch {
case len(appGenTxs) == 0:
return GenesisState{}, errors.New("must provide at least genesis transaction")
case len(tmVals) != len(valOperAddrs):
return GenesisState{}, errors.New("number of TM validators does not match number of operator addresses")
}
// start with the default staking genesis state
stakeData := stake.DefaultGenesisState()
// get genesis account information
genAccs := make([]GenesisAccount, len(appGenTxs))
for i, appGenTx := range appGenTxs {
var genTx GaiaGenTx
if err := cdc.UnmarshalJSON(appGenTx, &genTx); err != nil {
return GenesisState{}, err
}
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc))
// create the genesis account for the given genesis tx
genAccs[i] = genesisAccountFromGenTx(genTx)
}
for i, tmVal := range tmVals {
var issuedDelShares sdk.Dec
// increase total supply by validator's power
power := sdk.NewInt(tmVal.Power)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(power))
// add the validator
desc := stake.NewDescription(tmVal.Name, "", "", "")
validator := stake.NewValidator(valOperAddrs[i], tmVal.PubKey, desc)
validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, power)
stakeData.Validators = append(stakeData.Validators, validator)
// create the self-delegation from the issuedDelShares
selfDel := stake.Delegation{
DelegatorAddr: sdk.AccAddress(validator.OperatorAddr),
ValidatorAddr: validator.OperatorAddr,
Shares: issuedDelShares,
Height: 0,
}
stakeData.Bonds = append(stakeData.Bonds, selfDel)
}
return GenesisState{
Accounts: genAccs,
StakeData: stakeData,
DistrData: distr.DefaultGenesisState(),
SlashingData: slashing.DefaultGenesisState(),
GovData: gov.DefaultGenesisState(),
}, nil
}

View File

@ -231,7 +231,6 @@ func TestGaiaCLICreateValidator(t *testing.T) {
defaultParams := stake.DefaultParams()
initialPool := stake.InitialPool()
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(100)) // Delegate tx on GaiaAppGenState
initialPool = initialPool.ProcessProvisions(defaultParams) // provisions are added to the pool every hour
// create validator
cvStr := fmt.Sprintf("gaiacli tx create-validator %v", flags)
@ -290,8 +289,6 @@ func TestGaiaCLICreateValidator(t *testing.T) {
require.True(t, defaultParams.Equal(params))
pool := executeGetPool(t, fmt.Sprintf("gaiacli query pool --output=json %v", flags))
require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset)
require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares)
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
}
@ -311,7 +308,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
proposalsQuery := tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
proposalsQuery, _ := tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
require.Equal(t, "No matching proposals found", proposalsQuery)
// submit a test proposal
@ -346,7 +343,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, int64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus())
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
require.Equal(t, " 1 - Test", proposalsQuery)
depositStr := fmt.Sprintf("gaiacli tx deposit %v", flags)
@ -400,10 +397,10 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, int64(1), votes[0].ProposalID)
require.Equal(t, gov.OptionYes, votes[0].Option)
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "")
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "")
require.Equal(t, "No matching proposals found", proposalsQuery)
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=VotingPeriod %v", flags), "")
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=VotingPeriod %v", flags), "")
require.Equal(t, " 1 - Test", proposalsQuery)
// submit a second test proposal
@ -417,7 +414,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
executeWrite(t, spStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --latest=1 %v", flags), "")
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --latest=1 %v", flags), "")
require.Equal(t, " 2 - Apples", proposalsQuery)
}
@ -628,10 +625,10 @@ func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (b
}
func executeInit(t *testing.T, cmdStr string) (chainID string) {
out := tests.ExecuteT(t, cmdStr, app.DefaultKeyPass)
_, stderr := tests.ExecuteT(t, cmdStr, app.DefaultKeyPass)
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(out), &initRes)
err := json.Unmarshal([]byte(stderr), &initRes)
require.NoError(t, err)
err = json.Unmarshal(initRes["chain_id"], &chainID)
@ -641,7 +638,7 @@ func executeInit(t *testing.T, cmdStr string) (chainID string) {
}
func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.AccAddress, crypto.PubKey) {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var ko keys.KeyOutput
keys.UnmarshalJSON([]byte(out), &ko)
@ -655,7 +652,7 @@ func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.AccAddress, crypto.PubKe
}
func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(out), &initRes)
require.NoError(t, err, "out %v, err %v", out, err)
@ -672,7 +669,7 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
// stake
func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var validator stake.Validator
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &validator)
@ -681,7 +678,7 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
}
func executeGetPool(t *testing.T, cmdStr string) stake.Pool {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var pool stake.Pool
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &pool)
@ -690,7 +687,7 @@ func executeGetPool(t *testing.T, cmdStr string) stake.Pool {
}
func executeGetParams(t *testing.T, cmdStr string) stake.Params {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var params stake.Params
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &params)
@ -702,7 +699,7 @@ func executeGetParams(t *testing.T, cmdStr string) stake.Params {
// gov
func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var proposal gov.Proposal
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &proposal)
@ -711,7 +708,7 @@ func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal {
}
func executeGetVote(t *testing.T, cmdStr string) gov.Vote {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var vote gov.Vote
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &vote)
@ -720,7 +717,7 @@ func executeGetVote(t *testing.T, cmdStr string) gov.Vote {
}
func executeGetVotes(t *testing.T, cmdStr string) []gov.Vote {
out := tests.ExecuteT(t, cmdStr, "")
out, _ := tests.ExecuteT(t, cmdStr, "")
var votes []gov.Vote
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &votes)

View File

@ -32,6 +32,7 @@ func main() {
appInit := app.GaiaAppInit()
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc))
server.AddCommands(ctx, cdc, rootCmd, appInit,
newApp, exportAppStateAndTMValidators)

120
cmd/gaia/init/gentx.go Normal file
View File

@ -0,0 +1,120 @@
package init
import (
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/stake/client/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
tmcli "github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
"io/ioutil"
"os"
"path/filepath"
)
const (
defaultAmount = "100steak"
defaultCommissionRate = "0.1"
defaultCommissionMaxRate = "0.2"
defaultCommissionMaxChangeRate = "0.01"
)
// GenTxCmd builds the gaiad gentx command.
// nolint: errcheck
func GenTxCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "gentx",
Short: "Generate a genesis tx carrying a self delegation",
Long: fmt.Sprintf(`This command is an alias of the 'gaiad tx create-validator' command'.
It creates a genesis piece carrying a self delegation with the
following delegation and commission default parameters:
delegation amount: %s
commission rate: %s
commission max rate: %s
commission max change rate: %s
`, defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate),
RunE: func(cmd *cobra.Command, args []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(tmcli.HomeFlag))
nodeID, valPubKey, err := InitializeNodeValidatorFiles(ctx.Config)
if err != nil {
return err
}
ip, err := server.ExternalIP()
if err != nil {
return err
}
// Run gaiad tx create-validator
prepareFlagsForTxCreateValidator(config, nodeID, ip, valPubKey)
createValidatorCmd := cli.GetCmdCreateValidator(cdc)
w, err := ioutil.TempFile("", "gentx")
if err != nil {
return err
}
unsignedGenTxFilename := w.Name()
defer os.Remove(unsignedGenTxFilename)
os.Stdout = w
if err = createValidatorCmd.RunE(nil, args); err != nil {
return err
}
w.Close()
prepareFlagsForTxSign()
signCmd := authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc))
if w, err = prepareOutputFile(config.RootDir, nodeID); err != nil {
return err
}
os.Stdout = w
return signCmd.RunE(nil, []string{unsignedGenTxFilename})
},
}
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id")
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
cmd.MarkFlagRequired(client.FlagName)
return cmd
}
func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip string, valPubKey crypto.PubKey) {
viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home
viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from
viper.Set(cli.FlagNodeID, nodeID) // --node-id
viper.Set(cli.FlagIP, ip) // --ip
viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey
viper.Set(cli.FlagAmount, defaultAmount) // --amount
viper.Set(cli.FlagCommissionRate, defaultCommissionRate)
viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate)
viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate)
viper.Set(cli.FlagGenesisFormat, true) // --genesis-format
viper.Set(cli.FlagMoniker, config.Moniker) // --moniker
if config.Moniker == "" {
viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName))
}
}
func prepareFlagsForTxSign() {
viper.Set("offline", true)
}
func prepareOutputFile(rootDir, nodeID string) (w *os.File, err error) {
writePath := filepath.Join(rootDir, "config", "gentx")
if err = common.EnsureDir(writePath, 0700); err != nil {
return
}
filename := filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID))
return os.Create(filename)
}

View File

@ -2,311 +2,258 @@ package init
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/privval"
"os"
"path"
"path/filepath"
"sort"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
servercfg "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
)
// get cmd to initialize all files for tendermint and application
func GenTxCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command {
cmd := &cobra.Command{
Use: "gen-tx",
Short: "Create genesis transaction file (under [--home]/config/gentx/gentx-[nodeID].json)",
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, args []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
const (
flagWithTxs = "with-txs"
flagOverwrite = "overwrite"
flagClientHome = "home-client"
flagOverwriteKey = "overwrite-key"
flagSkipGenesis = "skip-genesis"
flagMoniker = "moniker"
)
ip := viper.GetString(server.FlagIP)
if len(ip) == 0 {
eip, err := server.ExternalIP()
if err != nil {
return err
}
ip = eip
}
genTxConfig := servercfg.GenTx{
viper.GetString(server.FlagName),
viper.GetString(server.FlagClientHome),
viper.GetBool(server.FlagOWK),
ip,
}
cliPrint, genTxFile, err := gentxWithConfig(cdc, appInit, config, genTxConfig)
if err != nil {
return err
}
toPrint := struct {
AppMessage json.RawMessage `json:"app_message"`
GenTxFile json.RawMessage `json:"gen_tx_file"`
}{
cliPrint,
genTxFile,
}
out, err := codec.MarshalJSONIndent(cdc, toPrint)
if err != nil {
return err
}
fmt.Println(string(out))
return nil
},
}
cmd.Flags().String(server.FlagIP, "", "external facing IP to use if left blank IP will be retrieved from this machine")
cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx)
return cmd
type initConfig struct {
ChainID string
GenTxsDir string
Name string
NodeID string
ClientHome string
WithTxs bool
Overwrite bool
OverwriteKey bool
ValPubKey crypto.PubKey
}
// NOTE: This will update (write) the config file with
// updated name (moniker) for node.
func gentxWithConfig(cdc *codec.Codec, appInit server.AppInit, config *cfg.Config, genTxConfig servercfg.GenTx) (
cliPrint json.RawMessage, genTxFile json.RawMessage, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return
}
nodeID := string(nodeKey.ID())
pubKey := readOrCreatePrivValidator(config)
type printInfo struct {
Moniker string `json:"moniker"`
ChainID string `json:"chain_id"`
NodeID string `json:"node_id"`
AppMessage json.RawMessage `json:"app_message"`
}
appGenTx, cliPrint, validator, err := appInit.AppGenTx(cdc, pubKey, genTxConfig)
// nolint: errcheck
func displayInfo(cdc *codec.Codec, info printInfo) error {
out, err := codec.MarshalJSONIndent(cdc, info)
if err != nil {
return
return err
}
tx := server.GenesisTx{
NodeID: nodeID,
IP: genTxConfig.IP,
Validator: validator,
AppGenTx: appGenTx,
}
bz, err := codec.MarshalJSONIndent(cdc, tx)
if err != nil {
return
}
genTxFile = json.RawMessage(bz)
name := fmt.Sprintf("gentx-%v.json", nodeID)
writePath := filepath.Join(config.RootDir, "config", "gentx")
file := filepath.Join(writePath, name)
err = common.EnsureDir(writePath, 0700)
if err != nil {
return
}
err = common.WriteFile(file, bz, 0644)
if err != nil {
return
}
// Write updated config with moniker
config.Moniker = genTxConfig.Name
configFilePath := filepath.Join(config.RootDir, "config", "config.toml")
cfg.WriteConfigFile(configFilePath, config)
return
fmt.Fprintf(os.Stderr, "%s\n", string(out))
return nil
}
// get cmd to initialize all files for tendermint and application
// nolint: golint
// nolint
func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command {
cmd := &cobra.Command{
Use: "init",
Short: "Initialize genesis config, priv-validator file, and p2p-node file",
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
Short: "Initialize private validator, p2p, genesis, and application configuration files",
Long: `Initialize validators's and node's configuration files.
Note that only node's configuration files will be written if the flag --skip-genesis is
enabled, and the genesis file will not be generated.
`,
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
initConfig := server.InitConfig{
viper.GetString(server.FlagChainID),
viper.GetBool(server.FlagWithTxs),
filepath.Join(config.RootDir, "config", "gentx"),
viper.GetBool(server.FlagOverwrite),
name := viper.GetString(client.FlagName)
chainID := viper.GetString(client.FlagChainID)
if chainID == "" {
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
}
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config)
if err != nil {
return err
}
chainID, nodeID, appMessage, err := initWithConfig(cdc, appInit, config, initConfig)
if err != nil {
return err
if viper.GetString(flagMoniker) != "" {
config.Moniker = viper.GetString(flagMoniker)
}
if config.Moniker == "" && name != "" {
config.Moniker = name
}
toPrint := printInfo{
ChainID: chainID,
Moniker: config.Moniker,
NodeID: nodeID,
}
if viper.GetBool(flagSkipGenesis) {
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
return displayInfo(cdc, toPrint)
}
initCfg := initConfig{
ChainID: chainID,
GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"),
Name: name,
NodeID: nodeID,
ClientHome: viper.GetString(flagClientHome),
WithTxs: viper.GetBool(flagWithTxs),
Overwrite: viper.GetBool(flagOverwrite),
OverwriteKey: viper.GetBool(flagOverwriteKey),
ValPubKey: valPubKey,
}
appMessage, err := initWithConfig(cdc, config, initCfg)
// print out some key information
toPrint := struct {
ChainID string `json:"chain_id"`
NodeID string `json:"node_id"`
AppMessage json.RawMessage `json:"app_message"`
}{
chainID,
nodeID,
appMessage,
}
out, err := codec.MarshalJSONIndent(cdc, toPrint)
if err != nil {
return err
}
fmt.Println(string(out))
return nil
toPrint.AppMessage = appMessage
return displayInfo(cdc, toPrint)
},
}
cmd.Flags().BoolP(server.FlagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().String(server.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().Bool(server.FlagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/")
cmd.Flags().AddFlagSet(appInit.FlagsAppGenState)
cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx) // need to add this flagset for when no GenTx's provided
cmd.AddCommand(GenTxCmd(ctx, cdc, appInit))
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().Bool(flagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/")
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
cmd.Flags().String(flagMoniker, "", "overrides --name flag and set the validator's moniker to a different value; ignored if it runs without the --with-txs flag")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().Bool(flagOverwriteKey, false, "overwrite client's key")
cmd.Flags().Bool(flagSkipGenesis, false, "do not create genesis.json")
return cmd
}
func initWithConfig(cdc *codec.Codec, appInit server.AppInit, config *cfg.Config, initConfig server.InitConfig) (
chainID string, nodeID string, appMessage json.RawMessage, err error) {
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return
}
nodeID = string(nodeKey.ID())
pubKey := readOrCreatePrivValidator(config)
if initConfig.ChainID == "" {
initConfig.ChainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
}
chainID = initConfig.ChainID
valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile())
return
}
func initWithConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig) (
appMessage json.RawMessage, err error) {
genFile := config.GenesisFile()
if !initConfig.Overwrite && common.FileExists(genFile) {
if !initCfg.Overwrite && common.FileExists(genFile) {
err = fmt.Errorf("genesis.json file already exists: %v", genFile)
return
}
// process genesis transactions, or otherwise create one for defaults
var appGenTxs []json.RawMessage
var validators []types.GenesisValidator
// process genesis transactions, else create default genesis.json
var appGenTxs []auth.StdTx
var persistentPeers string
var genTxs []json.RawMessage
var appState json.RawMessage
var jsonRawTx json.RawMessage
chainID := initCfg.ChainID
if initConfig.GenTxs {
validators, appGenTxs, persistentPeers, err = processGenTxs(initConfig.GenTxsDir, cdc)
if initCfg.WithTxs {
_, appGenTxs, persistentPeers, err = app.CollectStdTxs(config.Moniker, initCfg.GenTxsDir, cdc)
if err != nil {
return
}
genTxs = make([]json.RawMessage, len(appGenTxs))
config.P2P.PersistentPeers = persistentPeers
configFilePath := filepath.Join(config.RootDir, "config", "config.toml")
cfg.WriteConfigFile(configFilePath, config)
for i, stdTx := range appGenTxs {
jsonRawTx, err = cdc.MarshalJSON(stdTx)
if err != nil {
return
}
genTxs[i] = jsonRawTx
}
} else {
genTxConfig := servercfg.GenTx{
viper.GetString(server.FlagName),
viper.GetString(server.FlagClientHome),
viper.GetBool(server.FlagOWK),
"127.0.0.1",
var ip, keyPass, secret string
var addr sdk.AccAddress
var signedTx auth.StdTx
if initCfg.Name == "" {
err = errors.New("must specify validator's moniker (--name)")
return
}
// Write updated config with moniker
config.Moniker = genTxConfig.Name
configFilePath := filepath.Join(config.RootDir, "config", "config.toml")
cfg.WriteConfigFile(configFilePath, config)
appGenTx, am, validator, err := appInit.AppGenTx(cdc, pubKey, genTxConfig)
appMessage = am
if err != nil {
return "", "", nil, err
}
validators = []types.GenesisValidator{validator}
appGenTxs = []json.RawMessage{appGenTx}
}
appState, err := appInit.AppGenState(cdc, appGenTxs)
if err != nil {
return
}
err = writeGenesisFile(cdc, genFile, initConfig.ChainID, validators, appState)
if err != nil {
return
}
return
}
// append a genesis-piece
func processGenTxs(genTxsDir string, cdc *codec.Codec) (
validators []types.GenesisValidator, appGenTxs []json.RawMessage, persistentPeers string, err error) {
var fos []os.FileInfo
fos, err = ioutil.ReadDir(genTxsDir)
if err != nil {
return
}
genTxs := make(map[string]server.GenesisTx)
var nodeIDs []string
for _, fo := range fos {
filename := path.Join(genTxsDir, fo.Name())
if !fo.IsDir() && (path.Ext(filename) != ".json") {
continue
}
// get the genTx
var bz []byte
bz, err = ioutil.ReadFile(filename)
config.Moniker = initCfg.Name
ip, err = server.ExternalIP()
if err != nil {
return
}
var genTx server.GenesisTx
err = cdc.UnmarshalJSON(bz, &genTx)
memo := fmt.Sprintf("%s@%s:26656", initCfg.NodeID, ip)
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account %q (default: %q):", initCfg.Name, app.DefaultKeyPass)
keyPass, err = client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return
}
if keyPass == "" {
keyPass = app.DefaultKeyPass
}
addr, secret, err = server.GenerateSaveCoinKey(initCfg.ClientHome, initCfg.Name, keyPass, initCfg.OverwriteKey)
if err != nil {
return
}
appMessage, err = json.Marshal(map[string]string{"secret": secret})
if err != nil {
return
}
genTxs[genTx.NodeID] = genTx
nodeIDs = append(nodeIDs, genTx.NodeID)
}
sort.Strings(nodeIDs)
for _, nodeID := range nodeIDs {
genTx := genTxs[nodeID]
// combine some stuff
validators = append(validators, genTx.Validator)
appGenTxs = append(appGenTxs, genTx.AppGenTx)
// Add a persistent peer
comma := ","
if len(persistentPeers) == 0 {
comma = ""
msg := stake.NewMsgCreateValidator(
sdk.ValAddress(addr),
initCfg.ValPubKey,
sdk.NewInt64Coin("steak", 100),
stake.NewDescription(config.Moniker, "", "", ""),
stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
)
txBldr := authtx.NewTxBuilderFromCLI().WithCodec(cdc).WithMemo(memo).WithChainID(chainID)
signedTx, err = txBldr.SignStdTx(
initCfg.Name, keyPass, auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo), false,
)
if err != nil {
return
}
persistentPeers += fmt.Sprintf("%s%s@%s:26656", comma, genTx.NodeID, genTx.IP)
jsonRawTx, err = cdc.MarshalJSON(signedTx)
if err != nil {
return
}
genTxs = []json.RawMessage{jsonRawTx}
}
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
appState, err = app.GaiaAppGenStateJSON(cdc, genTxs)
if err != nil {
return
}
err = WriteGenesisFile(genFile, chainID, nil, appState)
return
}
// read of create the private key file for this config
func readOrCreatePrivValidator(tmConfig *cfg.Config) crypto.PubKey {
// private validator
privValFile := tmConfig.PrivValidatorFile()
var privValidator *privval.FilePV
if common.FileExists(privValFile) {
privValidator = privval.LoadFilePV(privValFile)
} else {
privValidator = privval.GenFilePV(privValFile)
privValidator.Save()
}
return privValidator.GetPubKey()
}
// writeGenesisFile creates and writes the genesis configuration to disk. An
// WriteGenesisFile creates and writes the genesis configuration to disk. An
// error is returned if building or writing the configuration to file fails.
// nolint: unparam
func writeGenesisFile(cdc *codec.Codec, genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error {
func WriteGenesisFile(genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error {
genDoc := types.GenesisDoc{
ChainID: chainID,
Validators: validators,
@ -319,3 +266,16 @@ func writeGenesisFile(cdc *codec.Codec, genesisFile, chainID string, validators
return genDoc.SaveAs(genesisFile)
}
// read of create the private key file for this config
func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey {
// private validator
var privValidator *privval.FilePV
if common.FileExists(privValFile) {
privValidator = privval.LoadFilePV(privValFile)
} else {
privValidator = privval.GenFilePV(privValFile)
privValidator.Save()
}
return privValidator.GetPubKey()
}

View File

@ -2,47 +2,64 @@ package init
import (
"bytes"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/tendermint/tendermint/libs/cli"
"io"
"io/ioutil"
"os"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/mock"
"github.com/stretchr/testify/require"
abciServer "github.com/tendermint/tendermint/abci/server"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/libs/log"
"github.com/spf13/viper"
)
func TestInitCmd(t *testing.T) {
defer server.SetupViper(t)()
defer setupClientHome(t)()
logger := log.NewNopLogger()
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := server.NewContext(cfg, logger)
cdc := codec.New()
cdc := app.MakeCodec()
appInit := server.AppInit{
AppGenState: mock.AppGenState,
AppGenTx: mock.AppGenTx,
}
cmd := InitCmd(ctx, cdc, appInit)
err = cmd.RunE(nil, nil)
require.NoError(t, err)
}
func setupClientHome(t *testing.T) func() {
clientDir, err := ioutil.TempDir("", "mock-sdk-cmd")
require.Nil(t, err)
viper.Set(flagClientHome, clientDir)
viper.Set(flagOverwriteKey, true)
return func() {
if err := os.RemoveAll(clientDir); err != nil {
// TODO: Handle with #870
panic(err)
}
}
}
func TestEmptyState(t *testing.T) {
defer server.SetupViper(t)()
defer setupClientHome(t)()
logger := log.NewNopLogger()
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := server.NewContext(cfg, logger)
cdc := codec.New()
cdc := app.MakeCodec()
appInit := server.AppInit{
AppGenTx: mock.AppGenTx,
AppGenState: mock.AppGenStateEmpty,
}
cmd := InitCmd(ctx, cdc, appInit)
@ -80,15 +97,17 @@ func TestStartStandAlone(t *testing.T) {
defer func() {
os.RemoveAll(home)
}()
viper.Set(cli.HomeFlag, home)
viper.Set(client.FlagName, "moniker")
defer setupClientHome(t)()
logger := log.NewNopLogger()
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := server.NewContext(cfg, logger)
cdc := codec.New()
cdc := app.MakeCodec()
appInit := server.AppInit{
AppGenState: mock.AppGenState,
AppGenTx: mock.AppGenTx,
}
initCmd := InitCmd(ctx, cdc, appInit)
err = initCmd.RunE(nil, nil)
@ -109,3 +128,19 @@ func TestStartStandAlone(t *testing.T) {
svr.Stop()
}
}
func TestInitNodeValidatorFiles(t *testing.T) {
home, err := ioutil.TempDir("", "mock-sdk-cmd")
require.Nil(t, err)
defer func() {
os.RemoveAll(home)
}()
viper.Set(cli.HomeFlag, home)
viper.Set(client.FlagName, "moniker")
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
nodeID, valPubKey, err := InitializeNodeValidatorFiles(cfg)
require.Nil(t, err)
require.NotEqual(t, "", nodeID)
require.NotEqual(t, 0, len(valPubKey.Bytes()))
}

View File

@ -1,20 +1,24 @@
package init
import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake"
"net"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
gc "github.com/cosmos/cosmos-sdk/server/config"
"os"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
)
@ -46,8 +50,7 @@ Example:
`,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
err := testnetWithConfig(config, cdc, appInit)
return err
return testnetWithConfig(config, cdc, appInit)
},
}
cmd.Flags().Int(nValidators, 4,
@ -70,6 +73,12 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
outDir := viper.GetString(outputDir)
numValidators := viper.GetInt(nValidators)
// Generate genesis.json and config.toml
chainID := "chain-" + cmn.RandStr(6)
monikers := make([]string, numValidators)
nodeIDs := make([]string, numValidators)
valPubKeys := make([]crypto.PubKey, numValidators)
// Generate private key, node ID, initial transaction
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
@ -92,60 +101,101 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
return err
}
monikers = append(monikers, nodeDirName)
config.Moniker = nodeDirName
ip, err := getIP(i)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
genTxConfig := gc.GenTx{
nodeDirName,
clientDir,
true,
ip,
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass)
keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return err
}
if keyPass == "" {
keyPass = app.DefaultKeyPass
}
// Run `init gen-tx` and generate initial transactions
cliPrint, genTxFile, err := gentxWithConfig(cdc, appInit, config, genTxConfig)
addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
info := map[string]string{"secret": secret}
cliPrint, err := json.Marshal(info)
if err != nil {
return err
}
// Save private key seed words
name := fmt.Sprintf("%v.json", "key_seed")
err = writeFile(name, clientDir, cliPrint)
err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint)
if err != nil {
return err
}
msg := stake.NewMsgCreateValidator(
sdk.ValAddress(addr),
valPubKeys[i],
sdk.NewInt64Coin("steak", 100),
stake.NewDescription(nodeDirName, "", "", ""),
stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
)
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo)
signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
txBytes, err := cdc.MarshalJSON(signedTx)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
// Gather gentxs folder
name = fmt.Sprintf("%v.json", nodeDirName)
err = writeFile(name, gentxsDir, genTxFile)
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
}
// Generate genesis.json and config.toml
chainID := "chain-" + cmn.RandStr(6)
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
initConfig := server.InitConfig{
chainID,
true,
gentxsDir,
true,
}
moniker := monikers[i]
config.Moniker = nodeDirName
config.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
// Run `init` and generate genesis.json and config.toml
_, _, _, err := initWithConfig(cdc, appInit, config, initConfig)
if err != nil {
initCfg := initConfig{
ChainID: chainID,
GenTxsDir: gentxsDir,
Name: moniker,
WithTxs: true,
Overwrite: true,
OverwriteKey: false,
NodeID: nodeID,
ValPubKey: valPubKey,
}
if _, err := initWithConfig(cdc, config, initCfg); err != nil {
return err
}
}

View File

@ -1,35 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bcrypt
import "encoding/base64"
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
var bcEncoding = base64.NewEncoding(alphabet)
func base64Encode(src []byte) []byte {
n := bcEncoding.EncodedLen(len(src))
dst := make([]byte, n)
bcEncoding.Encode(dst, src)
for dst[n-1] == '=' {
n--
}
return dst[:n]
}
func base64Decode(src []byte) ([]byte, error) {
numOfEquals := 4 - (len(src) % 4)
for i := 0; i < numOfEquals; i++ {
src = append(src, '=')
}
dst := make([]byte, bcEncoding.DecodedLen(len(src)))
n, err := bcEncoding.Decode(dst, src)
if err != nil {
return nil, err
}
return dst[:n], nil
}

View File

@ -1,297 +0,0 @@
package bcrypt
// MODIFIED BY TENDERMINT TO EXPOSE NONCE
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
// The code is a port of Provos and Mazières's C implementation.
import (
"crypto/subtle"
"errors"
"fmt"
"strconv"
"golang.org/x/crypto/blowfish"
)
const (
// the minimum allowable cost as passed in to GenerateFromPassword
MinCost int = 4
// the maximum allowable cost as passed in to GenerateFromPassword
MaxCost int = 31
// the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
DefaultCost int = 10
)
// The error returned from CompareHashAndPassword when a password and hash do
// not match.
var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
// The error returned from CompareHashAndPassword when a hash is too short to
// be a bcrypt hash.
var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
// The error returned from CompareHashAndPassword when a hash was created with
// a bcrypt algorithm newer than this implementation.
type HashVersionTooNewError byte
func (hv HashVersionTooNewError) Error() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
}
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
type InvalidHashPrefixError byte
// Format error
func (ih InvalidHashPrefixError) Error() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
}
// Invalid bcrypt cost
type InvalidCostError int
func (ic InvalidCostError) Error() string {
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) // nolint: unconvert
}
const (
majorVersion = '2'
minorVersion = 'a'
maxSaltSize = 16
maxCryptedHashSize = 23
encodedSaltSize = 22
encodedHashSize = 31
minHashSize = 59
)
// magicCipherData is an IV for the 64 Blowfish encryption calls in
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
var magicCipherData = []byte{
0x4f, 0x72, 0x70, 0x68,
0x65, 0x61, 0x6e, 0x42,
0x65, 0x68, 0x6f, 0x6c,
0x64, 0x65, 0x72, 0x53,
0x63, 0x72, 0x79, 0x44,
0x6f, 0x75, 0x62, 0x74,
}
type hashed struct {
hash []byte
salt []byte
cost int // allowed range is MinCost to MaxCost
major byte
minor byte
}
// GenerateFromPassword returns the bcrypt hash of the password at the given
// cost. If the cost given is less than MinCost, the cost will be set to
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
// to compare the returned hashed password with its cleartext version.
func GenerateFromPassword(salt []byte, password []byte, cost int) ([]byte, error) {
if len(salt) != maxSaltSize {
return nil, fmt.Errorf("salt len must be %v", maxSaltSize)
}
p, err := newFromPassword(salt, password, cost)
if err != nil {
return nil, err
}
return p.Hash(), nil
}
// CompareHashAndPassword compares a bcrypt hashed password with its possible
// plaintext equivalent. Returns nil on success, or an error on failure.
func CompareHashAndPassword(hashedPassword, password []byte) error {
p, err := newFromHash(hashedPassword)
if err != nil {
return err
}
otherHash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return err
}
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
return nil
}
return ErrMismatchedHashAndPassword
}
// Cost returns the hashing cost used to create the given hashed
// password. When, in the future, the hashing cost of a password system needs
// to be increased in order to adjust for greater computational power, this
// function allows one to establish which passwords need to be updated.
func Cost(hashedPassword []byte) (int, error) {
p, err := newFromHash(hashedPassword)
if err != nil {
return 0, err
}
return p.cost, nil
}
func newFromPassword(salt []byte, password []byte, cost int) (*hashed, error) {
if cost < MinCost {
cost = DefaultCost
}
p := new(hashed)
p.major = majorVersion
p.minor = minorVersion
err := checkCost(cost)
if err != nil {
return nil, err
}
p.cost = cost
p.salt = base64Encode(salt)
hash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return nil, err
}
p.hash = hash
return p, err
}
func newFromHash(hashedSecret []byte) (*hashed, error) {
if len(hashedSecret) < minHashSize {
return nil, ErrHashTooShort
}
p := new(hashed)
n, err := p.decodeVersion(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
n, err = p.decodeCost(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
// The "+2" is here because we'll have to append at most 2 '=' to the salt
// when base64 decoding it in expensiveBlowfishSetup().
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
copy(p.salt, hashedSecret[:encodedSaltSize])
hashedSecret = hashedSecret[encodedSaltSize:]
p.hash = make([]byte, len(hashedSecret))
copy(p.hash, hashedSecret)
return p, nil
}
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
cipherData := make([]byte, len(magicCipherData))
copy(cipherData, magicCipherData)
c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
if err != nil {
return nil, err
}
for i := 0; i < 24; i += 8 {
for j := 0; j < 64; j++ {
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
}
}
// Bug compatibility with C bcrypt implementations. We only encode 23 of
// the 24 bytes encrypted.
hsh := base64Encode(cipherData[:maxCryptedHashSize])
return hsh, nil
}
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
csalt, err := base64Decode(salt)
if err != nil {
return nil, err
}
// Bug compatibility with C bcrypt implementations. They use the trailing
// NULL in the key string during expansion.
ckey := append(key, 0)
c, err := blowfish.NewSaltedCipher(ckey, csalt)
if err != nil {
return nil, err
}
var i, rounds uint64
rounds = 1 << cost
for i = 0; i < rounds; i++ {
blowfish.ExpandKey(ckey, c)
blowfish.ExpandKey(csalt, c)
}
return c, nil
}
func (p *hashed) Hash() []byte {
arr := make([]byte, 60)
arr[0] = '$'
arr[1] = p.major
n := 2
if p.minor != 0 {
arr[2] = p.minor
n = 3
}
arr[n] = '$'
n++
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
n += 2
arr[n] = '$'
n++
copy(arr[n:], p.salt)
n += encodedSaltSize
copy(arr[n:], p.hash)
n += encodedHashSize
return arr[:n]
}
func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
if sbytes[0] != '$' {
return -1, InvalidHashPrefixError(sbytes[0])
}
if sbytes[1] > majorVersion {
return -1, HashVersionTooNewError(sbytes[1])
}
p.major = sbytes[1]
n := 3
if sbytes[2] != '$' {
p.minor = sbytes[2]
n++
}
return n, nil
}
// sbytes should begin where decodeVersion left off.
func (p *hashed) decodeCost(sbytes []byte) (int, error) {
cost, err := strconv.Atoi(string(sbytes[0:2]))
if err != nil {
return -1, err
}
err = checkCost(cost)
if err != nil {
return -1, err
}
p.cost = cost
return 3, nil
}
func (p *hashed) String() string {
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
}
func checkCost(cost int) error {
if cost < MinCost || cost > MaxCost {
return InvalidCostError(cost)
}
return nil
}

View File

@ -1,66 +0,0 @@
package bip39
import (
"strings"
"github.com/bartekn/go-bip39"
)
// ValidSentenceLen defines the mnemonic sentence lengths supported by this BIP 39 library.
type ValidSentenceLen uint8
const (
// FundRaiser is the sentence length used during the cosmos fundraiser (12 words).
FundRaiser ValidSentenceLen = 12
// Size of the checksum employed for the fundraiser
FundRaiserChecksumSize = 4
// FreshKey is the sentence length used for newly created keys (24 words).
FreshKey ValidSentenceLen = 24
// Size of the checksum employed for new keys
FreshKeyChecksumSize = 8
)
// NewMnemonic will return a string consisting of the mnemonic words for
// the given sentence length.
func NewMnemonic(len ValidSentenceLen) (words []string, err error) {
// len = (entropySize + checksum) / 11
var entropySize int
switch len {
case FundRaiser:
// entropySize = 128
entropySize = int(len)*11 - FundRaiserChecksumSize
case FreshKey:
// entropySize = 256
entropySize = int(len)*11 - FreshKeyChecksumSize
}
var entropy []byte
entropy, err = bip39.NewEntropy(entropySize)
if err != nil {
return
}
var mnemonic string
mnemonic, err = bip39.NewMnemonic(entropy)
if err != nil {
return
}
words = strings.Split(mnemonic, " ")
return
}
// MnemonicToSeed creates a BIP 39 seed from the passed mnemonic (with an empty BIP 39 password).
// This method does not validate the mnemonics checksum.
func MnemonicToSeed(mne string) (seed []byte) {
// we do not checksum here...
seed = bip39.NewSeed(mne, "")
return
}
// MnemonicToSeedWithErrChecking returns the same seed as MnemonicToSeed.
// It creates a BIP 39 seed from the passed mnemonic (with an empty BIP 39 password).
//
// Different from MnemonicToSeed it validates the checksum.
// For details on the checksum see the BIP 39 spec.
func MnemonicToSeedWithErrChecking(mne string) (seed []byte, err error) {
seed, err = bip39.NewSeedWithErrorChecking(mne, "")
return
}

View File

@ -1,15 +0,0 @@
package bip39
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestWordCodec_NewMnemonic(t *testing.T) {
_, err := NewMnemonic(FundRaiser)
require.NoError(t, err, "unexpected error generating fundraiser mnemonic")
_, err = NewMnemonic(FreshKey)
require.NoError(t, err, "unexpected error generating new 24-word mnemonic")
}

View File

@ -7,9 +7,10 @@ import (
"io/ioutil"
"testing"
"github.com/bartekn/go-bip39"
"github.com/stretchr/testify/require"
"github.com/cosmos/go-bip39"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/secp256k1"
)

View File

@ -22,7 +22,6 @@ import (
"strings"
"github.com/btcsuite/btcd/btcec"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
// BIP44Prefix is the parts of the BIP32 HD path that are fixed by what we used during the fundraiser.
@ -55,6 +54,77 @@ func NewParams(purpose, coinType, account uint32, change bool, addressIdx uint32
}
}
// Parse the BIP44 path and unmarshal into the struct.
// nolint: gocyclo
func NewParamsFromPath(path string) (*BIP44Params, error) {
spl := strings.Split(path, "/")
if len(spl) != 5 {
return nil, fmt.Errorf("path length is wrong. Expected 5, got %d", len(spl))
}
if spl[0] != "44'" {
return nil, fmt.Errorf("first field in path must be 44', got %v", spl[0])
}
if !isHardened(spl[1]) || !isHardened(spl[2]) {
return nil,
fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %v and %v", spl[1], spl[2])
}
if isHardened(spl[3]) || isHardened(spl[4]) {
return nil,
fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %v and %v", spl[3], spl[4])
}
purpose, err := hardenedInt(spl[0])
if err != nil {
return nil, err
}
coinType, err := hardenedInt(spl[1])
if err != nil {
return nil, err
}
account, err := hardenedInt(spl[2])
if err != nil {
return nil, err
}
change, err := hardenedInt(spl[3])
if err != nil {
return nil, err
}
if !(change == 0 || change == 1) {
return nil, fmt.Errorf("change field can only be 0 or 1")
}
addressIdx, err := hardenedInt(spl[4])
if err != nil {
return nil, err
}
return &BIP44Params{
purpose: purpose,
coinType: coinType,
account: account,
change: change > 0,
addressIdx: addressIdx,
}, nil
}
func hardenedInt(field string) (uint32, error) {
field = strings.TrimSuffix(field, "'")
i, err := strconv.Atoi(field)
if err != nil {
return 0, err
}
if i < 0 {
return 0, fmt.Errorf("fields must not be negative. got %d", i)
}
return uint32(i), nil
}
func isHardened(field string) bool {
return strings.HasSuffix(field, "'")
}
// NewFundraiserParams creates a BIP 44 parameter object from the params:
// m / 44' / 118' / account' / 0 / address_index
// The fixed parameters (purpose', coin_type', and change) are determined by what was used in the fundraiser.
@ -62,6 +132,21 @@ func NewFundraiserParams(account uint32, addressIdx uint32) *BIP44Params {
return NewParams(44, 118, account, false, addressIdx)
}
// Return the BIP44 fields as an array.
func (p BIP44Params) DerivationPath() []uint32 {
change := uint32(0)
if p.change {
change = 1
}
return []uint32{
p.purpose,
p.coinType,
p.account,
change,
p.addressIdx,
}
}
func (p BIP44Params) String() string {
var changeStr string
if p.change {
@ -128,10 +213,15 @@ func derivePrivateKey(privKeyBytes [32]byte, chainCode [32]byte, index uint32, h
data = append([]byte{byte(0)}, privKeyBytes[:]...)
} else {
// this can't return an error:
pubkey := secp256k1.PrivKeySecp256k1(privKeyBytes).PubKey()
_, ecPub := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes[:])
pubkeyBytes := ecPub.SerializeCompressed()
data = pubkeyBytes
/* By using btcec, we can remove the dependency on tendermint/crypto/secp256k1
pubkey := secp256k1.PrivKeySecp256k1(privKeyBytes).PubKey()
public := pubkey.(secp256k1.PubKeySecp256k1)
data = public[:]
*/
}
data = append(data, uint32ToBytes(index)...)
data2, chainCode2 := i64(chainCode[:], data)

View File

@ -3,9 +3,20 @@ package hd
import (
"encoding/hex"
"fmt"
"github.com/cosmos/cosmos-sdk/crypto/keys/bip39"
"testing"
"github.com/stretchr/testify/assert"
"github.com/cosmos/go-bip39"
)
var defaultBIP39Passphrase = ""
// return bip39 seed with empty passphrase
func mnemonicToSeed(mnemonic string) []byte {
return bip39.NewSeed(mnemonic, defaultBIP39Passphrase)
}
//nolint
func ExampleStringifyPathParams() {
path := NewParams(44, 0, 0, false, 0)
@ -13,10 +24,57 @@ func ExampleStringifyPathParams() {
// Output: 44'/0'/0'/0/0
}
func TestParamsFromPath(t *testing.T) {
goodCases := []struct {
params *BIP44Params
path string
}{
{&BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"},
{&BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
{&BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
{&BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
{&BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
{&BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"},
{&BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"},
}
for i, c := range goodCases {
params, err := NewParamsFromPath(c.path)
errStr := fmt.Sprintf("%d %v", i, c)
assert.NoError(t, err, errStr)
assert.EqualValues(t, c.params, params, errStr)
assert.Equal(t, c.path, c.params.String())
}
badCases := []struct {
path string
}{
{"43'/0'/0'/0/0"}, // doesnt start with 44
{"44'/1'/0'/0/0/5"}, // too many fields
{"44'/0'/1'/0"}, // too few fields
{"44'/0'/0'/2/0"}, // change field can only be 0/1
{"44/0'/0'/0/0"}, // first field needs '
{"44'/0/0'/0/0"}, // second field needs '
{"44'/0'/0/0/0"}, // third field needs '
{"44'/0'/0'/0'/0"}, // fourth field must not have '
{"44'/0'/0'/0/0'"}, // fifth field must not have '
{"44'/-1'/0'/0/0"}, // no negatives
{"44'/0'/0'/-1/0"}, // no negatives
}
for i, c := range badCases {
params, err := NewParamsFromPath(c.path)
errStr := fmt.Sprintf("%d %v", i, c)
assert.Nil(t, params, errStr)
assert.Error(t, err, errStr)
}
}
//nolint
func ExampleSomeBIP32TestVecs() {
seed := bip39.MnemonicToSeed("barrel original fuel morning among eternal " +
seed := mnemonicToSeed("barrel original fuel morning among eternal " +
"filter ball stove pluck matrix mechanic")
master, ch := ComputeMastersFromSeed(seed)
fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)")
@ -35,14 +93,14 @@ func ExampleSomeBIP32TestVecs() {
fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html")
fmt.Println()
seed = bip39.MnemonicToSeed(
seed = mnemonicToSeed(
"advice process birth april short trust crater change bacon monkey medal garment " +
"gorilla ranch hour rival razor call lunar mention taste vacant woman sister")
master, ch = ComputeMastersFromSeed(seed)
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
fmt.Println(hex.EncodeToString(priv[:]))
seed = bip39.MnemonicToSeed("idea naive region square margin day captain habit " +
seed = mnemonicToSeed("idea naive region square margin day captain habit " +
"gun second farm pact pulse someone armed")
master, ch = ComputeMastersFromSeed(seed)
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
@ -53,7 +111,7 @@ func ExampleSomeBIP32TestVecs() {
fmt.Println()
// bip32 path: m/0/7
seed = bip39.MnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
master, ch = ComputeMastersFromSeed(seed)
priv, _ = DerivePrivateKeyForPath(master, ch, "0/7")
fmt.Println(hex.EncodeToString(priv[:]))

View File

@ -6,11 +6,15 @@ import (
"os"
"strings"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/bip39"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
"github.com/cosmos/go-bip39"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
"github.com/cosmos/cosmos-sdk/types"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/crypto/secp256k1"
@ -46,6 +50,14 @@ const (
infoSuffix = "info"
)
const (
// used for deriving seed from mnemonic
defaultBIP39Passphrase = ""
// bits of entropy to draw when creating a mnemonic
defaultEntropySize = 256
)
var (
// ErrUnsupportedSigningAlgo is raised when the caller tries to use a
// different signing scheme than secp256k1.
@ -85,12 +97,17 @@ func (kb dbKeybase) CreateMnemonic(name string, language Language, passwd string
}
// default number of words (24):
mnemonicS, err := bip39.NewMnemonic(bip39.FreshKey)
// this generates a mnemonic directly from the number of words by reading system entropy.
entropy, err := bip39.NewEntropy(defaultEntropySize)
if err != nil {
return
}
mnemonic = strings.Join(mnemonicS, " ")
seed := bip39.MnemonicToSeed(mnemonic)
mnemonic, err = bip39.NewMnemonic(entropy)
if err != nil {
return
}
seed := bip39.NewSeed(mnemonic, defaultBIP39Passphrase)
info, err = kb.persistDerivedKey(seed, passwd, name, hd.FullFundraiserPath)
return
}
@ -102,7 +119,7 @@ func (kb dbKeybase) CreateKey(name, mnemonic, passwd string) (info Info, err err
err = fmt.Errorf("recovering only works with 12 word (fundraiser) or 24 word mnemonics, got: %v words", len(words))
return
}
seed, err := bip39.MnemonicToSeedWithErrChecking(mnemonic)
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, defaultBIP39Passphrase)
if err != nil {
return
}
@ -119,7 +136,7 @@ func (kb dbKeybase) CreateFundraiserKey(name, mnemonic, passwd string) (info Inf
err = fmt.Errorf("recovering only works with 12 word (fundraiser), got: %v words", len(words))
return
}
seed, err := bip39.MnemonicToSeedWithErrChecking(mnemonic)
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, defaultBIP39Passphrase)
if err != nil {
return
}
@ -127,12 +144,12 @@ func (kb dbKeybase) CreateFundraiserKey(name, mnemonic, passwd string) (info Inf
return
}
func (kb dbKeybase) Derive(name, mnemonic, passwd string, params hd.BIP44Params) (info Info, err error) {
seed, err := bip39.MnemonicToSeedWithErrChecking(mnemonic)
func (kb dbKeybase) Derive(name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params) (info Info, err error) {
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
if err != nil {
return
}
info, err = kb.persistDerivedKey(seed, passwd, name, params.String())
info, err = kb.persistDerivedKey(seed, encryptPasswd, name, params.String())
return
}
@ -229,7 +246,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
err = fmt.Errorf("private key not available")
return
}
priv, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
priv, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, nil, err
}
@ -279,7 +296,7 @@ func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcr
err = fmt.Errorf("private key not available")
return nil, err
}
priv, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
priv, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, err
}
@ -296,7 +313,7 @@ func (kb dbKeybase) Export(name string) (armor string, err error) {
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
return armorInfoBytes(bz), nil
return mintkey.ArmorInfoBytes(bz), nil
}
// ExportPubKey returns public keys in ASCII armored format.
@ -311,7 +328,7 @@ func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
if err != nil {
return
}
return armorPubKeyBytes(info.GetPubKey().Bytes()), nil
return mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes()), nil
}
func (kb dbKeybase) Import(name string, armor string) (err error) {
@ -319,7 +336,7 @@ func (kb dbKeybase) Import(name string, armor string) (err error) {
if len(bz) > 0 {
return errors.New("Cannot overwrite data for name " + name)
}
infoBytes, err := unarmorInfoBytes(armor)
infoBytes, err := mintkey.UnarmorInfoBytes(armor)
if err != nil {
return
}
@ -335,7 +352,7 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
if len(bz) > 0 {
return errors.New("Cannot overwrite data for name " + name)
}
pubBytes, err := unarmorPubKeyBytes(armor)
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor)
if err != nil {
return
}
@ -360,7 +377,7 @@ func (kb dbKeybase) Delete(name, passphrase string) error {
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
_, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
_, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return err
}
@ -394,7 +411,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
key, err := unarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
key, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
if err != nil {
return err
}
@ -411,7 +428,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
func (kb dbKeybase) writeLocalKey(priv tmcrypto.PrivKey, name, passphrase string) Info {
// encrypt private key using passphrase
privArmor := encryptArmorPrivKey(priv, passphrase)
privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase)
// make Info
pub := priv.PubKey()
info := newLocalInfo(name, pub, privArmor)

View File

@ -4,9 +4,12 @@ import (
"fmt"
"testing"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
@ -15,7 +18,7 @@ import (
)
func init() {
BcryptSecurityParameter = 1
mintkey.BcryptSecurityParameter = 1
}
// TestKeyManagement makes sure we can manipulate these keys well
@ -342,7 +345,7 @@ func TestSeedPhrase(t *testing.T) {
// let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, 0)
newInfo, err := cstore.Derive(n2, mnemonic, p2, params)
newInfo, err := cstore.Derive(n2, mnemonic, defaultBIP39Passphrase, p2, params)
require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName())
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())

View File

@ -1,16 +1,17 @@
package keys
package mintkey
import (
"encoding/hex"
"fmt"
cmn "github.com/tendermint/tendermint/libs/common"
"golang.org/x/crypto/bcrypt"
"github.com/cosmos/cosmos-sdk/crypto/keys/bcrypt"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/armor"
"github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/crypto/xsalsa20symmetric"
cmn "github.com/tendermint/tendermint/libs/common"
)
const (
@ -34,11 +35,16 @@ const (
// TODO: Consider increasing default
var BcryptSecurityParameter = 12
func armorInfoBytes(bz []byte) string {
//-----------------------------------------------------------------
// add armor
// Armor the InfoBytes
func ArmorInfoBytes(bz []byte) string {
return armorBytes(bz, blockTypeKeyInfo)
}
func armorPubKeyBytes(bz []byte) string {
// Armor the PubKeyBytes
func ArmorPubKeyBytes(bz []byte) string {
return armorBytes(bz, blockTypePubKey)
}
@ -50,11 +56,16 @@ func armorBytes(bz []byte, blockType string) string {
return armor.EncodeArmor(blockType, header, bz)
}
func unarmorInfoBytes(armorStr string) (bz []byte, err error) {
//-----------------------------------------------------------------
// remove armor
// Unarmor the InfoBytes
func UnarmorInfoBytes(armorStr string) (bz []byte, err error) {
return unarmorBytes(armorStr, blockTypeKeyInfo)
}
func unarmorPubKeyBytes(armorStr string) (bz []byte, err error) {
// Unarmor the PubKeyBytes
func UnarmorPubKeyBytes(armorStr string) (bz []byte, err error) {
return unarmorBytes(armorStr, blockTypePubKey)
}
@ -74,7 +85,11 @@ func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
return
}
func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
//-----------------------------------------------------------------
// encrypt/decrypt with armor
// Encrypt and armor the private key.
func EncryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
header := map[string]string{
"kdf": "bcrypt",
@ -84,7 +99,22 @@ func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
return armorStr
}
func unarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) {
// encrypt the given privKey with the passphrase using a randomly
// generated salt and the xsalsa20 cipher. returns the salt and the
// encrypted priv key.
func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) {
saltBytes = crypto.CRandBytes(16)
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BcryptSecurityParameter)
if err != nil {
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
}
key = crypto.Sha256(key) // get 32 bytes
privKeyBytes := privKey.Bytes()
return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key)
}
// Unarmor and decrypt the private key.
func UnarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) {
var privKey crypto.PrivKey
blockType, header, encBytes, err := armor.DecodeArmor(armorStr)
if err != nil {
@ -107,17 +137,6 @@ func unarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey,
return privKey, err
}
func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) {
saltBytes = crypto.CRandBytes(16)
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BcryptSecurityParameter)
if err != nil {
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
}
key = crypto.Sha256(key) // Get 32 bytes
privKeyBytes := privKey.Bytes()
return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key)
}
func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BcryptSecurityParameter)
if err != nil {

View File

@ -26,8 +26,12 @@ type Keybase interface {
CreateKey(name, mnemonic, passwd string) (info Info, err error)
// CreateFundraiserKey takes a mnemonic and derives, a password
CreateFundraiserKey(name, mnemonic, passwd string) (info Info, err error)
// Derive derives a key from the passed mnemonic using a BIP44 path.
Derive(name, mnemonic, passwd string, params hd.BIP44Params) (Info, error)
// Compute a BIP39 seed from th mnemonic and bip39Passwd.
// Derive private key from the seed using the BIP44 params.
// Encrypt the key to disk using encryptPasswd.
// See https://github.com/cosmos/cosmos-sdk/issues/2095
Derive(name, mnemonic, bip39Passwd,
encryptPasswd string, params hd.BIP44Params) (Info, error)
// Create, store, and return a new Ledger key reference
CreateLedger(name string, path ccrypto.DerivationPath, algo SigningAlgo) (info Info, err error)

View File

@ -1,6 +1,7 @@
module.exports = {
title: "Cosmos Network",
title: "Cosmos Documentation",
description: "Documentation for the Cosmos Network.",
ga: "UA-51029217-2",
dest: "./dist/docs",
base: "/docs/",
markdown: {
@ -67,6 +68,14 @@ module.exports = {
"/sdk/sdk-by-examples/simple-governance/running-the-application"
]
},
{
title: "Light Client",
collapsable: false,
children: [
"/light/",
"/light/getting_started"
]
},
{
title: "Lotion JS",
collapsable: false,

View File

@ -112,4 +112,4 @@ much as possible with its [counterpart in the Tendermint Core repo](https://gith
4. Compile gaiacli
```
make install
```
```

View File

@ -1,13 +1,8 @@
# Post-0.25/GoS Pre-Release
## Staking/Slashing/Stability
## Staking / Slashing - Stability
- Other slashing issues blocking for launch - [#1256](https://github.com/cosmos/cosmos-sdk/issues/1256)
- Miscellaneous minor staking issues
- [List here](https://github.com/cosmos/cosmos-sdk/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Astaking+label%3Aprelaunch)
- Need to figure out scope of work here to estimate time
- @rigelrozanski to start next
- Consider "tombstone" / "prison" - double-sign and you can never validate again - https://github.com/cosmos/cosmos-sdk/issues/2363
- [Prelaunch Issues](https://github.com/cosmos/cosmos-sdk/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Astaking+label%3Aprelaunch-2.0)
## Multisig
@ -16,24 +11,29 @@
## ABCI Changes
- Need to update for new ABCI changes when/if they land - error string, tags are list of lists
- CheckEvidence/DeliverEvidence
- CheckTx/DeliverTx ordering semantics
- ABCI Error string update (only on the SDK side)
- Need to verify correct proposer reward semantics
- CheckEvidence/DeliverEvidence, CheckTx/DeliverTx ordering semantics
## Gas
- Charge for transaction size
- Decide what "one gas" corresponds to (standard hardware benchmarks?)
- More benchmarking
- Consider charging based on maximum depth of IAVL tree iteration
- Write specification and explainer document for Gas in Cosmos
* Charge for transaction size
* Decide what "one gas" corresponds to (standard hardware benchmarks?)
* Consider charging based on maximum depth of IAVL tree iteration
- Test out gas estimation in CLI and LCD and ensure the UX works
## LCD
- Bianje working on implementation of ICS standards
- Additional PR incoming for ICS 22 and ICS 23
- Decide what ought to be ICS-standardized and what ought not to
- Bianje working with Voyager team (@fedekunze) to complete implementation and documentation.
## Documentation
- gaiad / gaiacli
- LCD
- Each module
- Tags [#1780](https://github.com/cosmos/cosmos-sdk/issues/1780)
# Lower priority
## Governance v2
@ -41,6 +41,6 @@
- Circuit breaker - https://github.com/cosmos/cosmos-sdk/issues/926
- Parameter change proposals (roughly the same implementation as circuit breaker)
## Documentation
## Staking / Slashing - Stability
- gaiad / gaiacli / gaialite documentation!
- Consider "tombstone" / "prison" - double-sign and you can never validate again - https://github.com/cosmos/cosmos-sdk/issues/2363

View File

@ -18,9 +18,9 @@ There are three main pieces to consider:
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).
First, you need to [install the software](../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).
Then, you can start [running a full-node](../getting-started/join-testnet.md).
### Command-Line interface
@ -28,7 +28,7 @@ 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):
To generate a new key (default secp256k1 elliptic curve):
```bash
gaiacli keys add <your_key_name>
@ -62,13 +62,13 @@ gaiacli account <YOUR_ADDRESS>
Here is the command to send coins via the CLI:
```bash
gaiacli tx send --amount=10faucetToken --chain-id=<name_of_testnet_chain> --name=<key_name> --to=<destination_address>
gaiacli send --amount=10faucetToken --chain-id=<name_of_testnet_chain> --from=<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.
- `--from`: Name of the key of the sending account.
- `--to`: Address of the recipient.
#### Help
@ -88,24 +88,25 @@ The Rest Server acts as an intermediary between the front-end and the full-node.
To start the Rest server:
```bash
gaiacli rest-server --trust-node=false --node=<full_node_address:full_node_port>
gaiacli advanced rest-server --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>.
- `--trust-node`: A boolean. If `true`, light-client verification is disabled. If `false`, it is disabled. For service providers, this should be set to `true`. By default, it set to `true`.
- `--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:26657`.
- `--laddr`: This flag allows you to specify the address and port for the Rest Server (default `1317`). 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)
[`/bank/balance/{account}`](https://cosmos.network/rpc/#/ICS20/get_bank_balances__address_)
## 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 Rest API documents all the available endpoints that you can use to interract with your full node. It can be found [here](https://cosmos.network/rpc/).
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.
The API is divided into ICS standards for each category of endpoints. For example, the [ICS20](https://cosmos.network/rpc/#/ICS20/) 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.
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://cosmos.network/rpc/#/ICS20/post_bank_accounts__address__transfers)), [sign](https://cosmos.network/rpc/#/ICS20/post_tx_sign) and [broadcast](https://cosmos.network/rpc/#/ICS20/post_tx_broadcast) them with different API endpoints. This allows service providers to use their own signing mechanism for instance.

View File

@ -15,7 +15,7 @@ These instructions are for setting up a brand new full node from scratch.
First, initialize the node and create the necessary config files:
```bash
gaiad init --name <your_custom_name>
gaiad init --skip-genesis --name <your_custom_name>
```
::: warning Note

View File

@ -1,961 +0,0 @@
# Cosmos Hub (Gaia-Lite) LCD API
This document describes the API that is exposed by the specific Light Client Daemon (LCD) implementation of the Cosmos Hub (Gaia). Those APIs are exposed by a REST server and can easily be accessed over HTTP/WS (websocket)
connections.
The complete API is comprised of the sub-APIs of different modules. The modules in the Cosmos Hub (Gaia-Lite) API are:
- ICS0 ([TendermintAPI](api.md#ics0---tendermintapi))
- ICS1 ([KeyAPI](api.md#ics1---keyapi))
- ICS20 ([TokenAPI](api.md#ics20---tokenapi))
- ICS21 ([StakingAPI](api.md#ics21---stakingapi))
- ICS22 ([GovernanceAPI](api.md#ics22---governanceapi))
- ICS23 ([SlashingAPI](api.md#ics23---slashingapi))
Error messages my change and should be only used for display purposes. Error messages should not be
used for determining the error type.
## ICS0 - TendermintAPI
Exposes the same functionality as the Tendermint RPC from a full node. It aims to have a very similar API.
### POST /txs
- **URL**: `/txs`
- Query Parameters:
- `?return={sync|async|block}`:
- `return=sync`: Waits for the transaction to pass `CheckTx`
- `return=async`: Returns the request immediately after it is received by the server
- `return=block`: waits for for the transaction to be committed in a block
- POST Body:
```json
{
"transaction": "string",
"return": "string",
}
```
- Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"code":0,
"hash":"0D33F2F03A5234F38706E43004489E061AC40A2E",
"data":"",
"log":""
}
}
```
## ICS1 - KeyAPI
This API exposes all functionality needed for key creation, signing and management.
### GET /keys
- **URL**: `/keys`
- **Functionality**: Gets a list of all the keys.
- Returns on success:
```json
{
"rest api":"1.0",
"code":200,
"error":"",
"result":{
"account":[
{
"name":"monkey",
"address":"cosmos1fedh326uxqlxs8ph9ej7cf854gz7fd5zlym5pd",
"pub_key":"cosmospub1zcjduc3q8s8ha96ry4xc5xvjp9tr9w9p0e5lk5y0rpjs5epsfxs4wmf72x3shvus0t"
},
{
"name":"test",
"address":"cosmos1thlqhjqw78zvcy0ua4ldj9gnazqzavyw4eske2",
"pub_key":"cosmospub1zcjduc3qyx6hlf825jcnj39adpkaxjer95q7yvy25yhfj3dmqy2ctev0rxmse9cuak"
}
],
"block_height":5241
}
}
```
### POST /keys
- **URL**: `/keys`
- **Functionality**: Create a new key.
- POST Body:
```json
{
"name": "string",
"password": "string",
"seed": "string",
}
```
Returns on success:
```json
{
"rest api":"1.0",
"code":200,
"error":"",
"result":{
"seed":"crime carpet recycle erase simple prepare moral dentist fee cause pitch trigger when velvet animal abandon"
}
}
```
### GET /keys/{name}
- **URL** : `/keys/{name}`
- **Functionality**: Get the information for the specified key.
- Returns on success:
```json
{
"rest api":"1.0",
"code":200,
"error":"",
"result":{
"name":"test",
"address":"cosmos1thlqhjqw78zvcy0ua4ldj9gnazqzavyw4eske2",
"pub_key":"cosmospub1zcjduc3qyx6hlf825jcnj39adpkaxjer95q7yvy25yhfj3dmqy2ctev0rxmse9cuak"
}
}
```
### PUT /keys/{name}
- **URL** : `/keys/{name}`
- **Functionality**: Change the encryption password for the specified key.
- PUT Body:
```json
{
"old_password": "string",
"new_password": "string",
}
```
- Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{}
}
```
### DELETE /keys/{name}
- **URL**: `/keys/{name}`
- **Functionality**: Delete the specified key.
- DELETE Body:
```json
{
"password": "string",
}
```
- Returns on success:
```json
{
"rest api":"1.0",
"code":200,
"error":"",
"result":{}
}
```
### POST /keys/{name}/recover
- **URL**: `/keys/{name}/recover`
- **Functionality**: Recover your key from seed and persist it encrypted with the password.
- POST Body:
```json
{
"password": "string",
"seed": "string",
}
```
- Returns on success:
```json
{
"rest api":"1.0",
"code":200,
"error":"",
"result":{
"address":"BD607C37147656A507A5A521AA9446EB72B2C907"
}
}
```
### GET /auth/accounts/{address}
- **URL**: `/auth/accounts/{address}`
- **Functionality**: Query the information of an account .
- Returns on success:
```json
{
"rest api":"1.0",
"code":200,
"error":"",
"result":{
"address": "82A57F8575BDFA22F5164C75361A21D2B0E11089",
"public_key": "PubKeyEd25519{A0EEEED3C9CE1A6988DEBFE347635834A1C0EBA0B4BB1125896A7072D22E650D}",
"coins": [
{"atom": 300},
{"photon": 15}
],
"account_number": 1,
"sequence": 7
}
}
```
### POST /auth/tx/sign
- **URL**: `/auth/tx/sign`
- **Functionality**: Sign a transaction without broadcasting it.
- Returns on success:
```json
{
"rest api": "1.0",
"code": 200,
"error": "",
"result": {
"type": "auth/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/Send",
"value": {
"inputs": [
{
"address": "cosmos1ql4ekxkujf3xllk8h5ldhhgh4ylpu7kwec6q3d",
"coins": [
{
"denom": "steak",
"amount": "1"
}
]
}
],
"outputs": [
{
"address": "cosmos1dhyqhg4px33ed3erqymls0hc7q2lxw9hhfwklj",
"coins": [
{
"denom": "steak",
"amount": "1"
}
]
}
]
}
}
],
"fee": {
"amount": [
{
"denom": "",
"amount": "0"
}
],
"gas": "2742"
},
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A2A/f2IYnrPUMTMqhwN81oas9jurtfcsvxdeLlNw3gGy"
},
"signature": "MEQCIGVn73y9QLwBa3vmsAD1bs3ygX75Wo+lAFSAUDs431ZPAiBWAf2amyqTCDXE9J87rL9QF9sd5JvVMt7goGSuamPJwg==",
"account_number": "1",
"sequence": "0"
}
],
"memo": ""
}
}
}
```
### POST /auth/tx/broadcast
- **URL**: `/auth/broadcast`
- **Functionality**: Broadcast a transaction.
- Returns on success:
```json
{
"rest api": "1.0",
"code": 200,
"error": "",
"result":
{
"check_tx": {
"log": "Msg 0: ",
"gasWanted": "2742",
"gasUsed": "1002"
},
"deliver_tx": {
"log": "Msg 0: ",
"gasWanted": "2742",
"gasUsed": "2742",
"tags": [
{
"key": "c2VuZGVy",
"value": "Y29zbW9zMXdjNTl6ZXU3MmNjdnp5ZWR6ZGE1N3pzcXh2eXZ2Y3poaHBhdDI4"
},
{
"key": "cmVjaXBpZW50",
"value": "Y29zbW9zMTJ4OTNmY3V2azg3M3o1ejZnejRlNTl2dnlxcXp1eDdzdDcwNWd5"
}
]
},
"hash": "784314784503582AC885BD6FB0D2A5B79FF703A7",
"height": "5"
}
}
```
## ICS20 - TokenAPI
The TokenAPI exposes all functionality needed to query account balances and send transactions.
### GET /bank/balance/{account}
- **URL**: `/bank/balance/{account}`
- **Functionality**: Query the specified account's balance.
- Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result": {
"atom":1000,
"photon":500,
"ether":20
}
}
```
### POST /bank/transfers
- **URL**: `/bank/transfers`
- **Functionality**: Create a transfer in the bank module.
- POST Body:
```json
{
"amount": [
{
"denom": "string",
"amount": 64,
}
],
"name": "string",
"password": "string",
"chain_id": "string",
"account_number": 64,
"sequence": 64,
"gas": 64,
}
```
- Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO:<JSON sign bytes for the transaction>"
}
}
```
## ICS21 - StakingAPI
The StakingAPI exposes all functionality needed for validation and delegation in Proof-of-Stake.
### GET /stake/delegators/{delegatorAddr}
- **URL**: `/stake/delegators/{delegatorAddr}`
- **Functionality**: Get all delegations (delegation, undelegation) from a delegator.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result": {
"atom":1000,
"photon":500,
"ether":20
}
}
```
### GET /stake/delegators/{delegatorAddr}/validators
- **URL**: `/stake/delegators/{delegatorAddr}/validators`
- **Functionality**: Query all validators that a delegator is bonded to.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{}
}
```
### GET /stake/delegators/{delegatorAddr}/validators/{validatorAddr}
- **URL**: `/stake/delegators/{delegatorAddr}/validators/{validatorAddr}`
- **Functionality**: Query a validator that a delegator is bonded to
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{}
}
```
### GET /stake/delegators/{delegatorAddr}/txs
- **URL**: `/stake/delegators/{delegatorAddr}/txs`
- **Functionality**: Get all staking txs (i.e msgs) from a delegator.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
### POST /stake/delegators/{delegatorAddr}/delegations
- **URL**: `/stake/delegators/{delegatorAddr}/delegations`
- **Functionality**: Submit or edit a delegation.
<!--NOTE Should this be a PUT instead of a POST? the code indicates that this is an edit operation-->
- POST Body:
```json
{
"name": "string",
"password": "string",
"chain_id": "string",
"account_number": 64,
"sequence": 64,
"gas": 64,
"delegations": [
{
"delegator_addr": "string",
"validator_addr": "string",
"delegation": {
"denom": "string",
"amount": 1234
}
}
],
"begin_unbondings": [
{
"delegator_addr": "string",
"validator_addr": "string",
"shares": "string",
}
],
"complete_unbondings": [
{
"delegator_addr": "string",
"validator_addr": "string",
}
],
"begin_redelegates": [
{
"delegator_addr": "string",
"validator_src_addr": "string",
"validator_dst_addr": "string",
"shares": "string",
}
],
"complete_redelegates": [
{
"delegator_addr": "string",
"validator_src_addr": "string",
"validator_dst_addr": "string",
}
]
}
```
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
### GET /stake/delegators/{delegatorAddr}/delegations/{validatorAddr}
- **URL**: `/stake/delegators/{delegatorAddr}/delegations/{validatorAddr}`
- **Functionality**: Query the current delegation status between a delegator and a validator.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
### GET /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}
- **URL**: `/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}`
- **Functionality**: Query all unbonding delegations between a delegator and a validator.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
### GET /stake/validators
- **URL**: `/stake/validators`
- **Functionality**: Get all validator candidates.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
### GET /stake/validators/{validatorAddr}
- **URL**: `/stake/validators/{validatorAddr}`
- **Functionality**: Query the information from a single validator.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
### GET /stake/parameters
- **URL**: `/stake/parameters`
- **Functionality**: Get the current value of staking parameters.
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"inflation_rate_change": 1300000000,
"inflation_max": 2000000000,
"inflation_min": 700000000,
"goal_bonded": 6700000000,
"unbonding_time": "72h0m0s",
"max_validators": 100,
"bond_denom": "atom"
}
}
```
### GET /stake/pool
- **URL**: `/stake/pool`
- **Functionality**: Get the current value of the dynamic parameters of the current state (*i.e* `Pool`).
- Returns on success:
```json
{
"rest api":"2.1",
"code":200,
"error":"",
"result":{
"loose_tokens": 0,
"bonded_tokens": 0,
"inflation_last_time": "1970-01-01 01:00:00 +0100 CET",
"inflation": 700000000,
"date_last_commission_reset": 0,
"prev_bonded_shares": 0,
}
}
```
## ICS22 - GovernanceAPI
The GovernanceAPI exposes all functionality needed for casting votes on plain text, software upgrades and parameter change proposals.
### GET /gov/proposals
- **URL**: `/gov/proposals`
- **Functionality**: Query all submited proposals
- Response on Success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result":{
"proposals":[
"TODO"
]
}
}
```
### POST /gov/proposals
- **URL**: `/gov/proposals`
- **Functionality**: Submit a proposal
- POST Body:
```js
{
"base_req": {
// Name of key to use
"name": "string",
// Password for that key
"password": "string",
"chain_id": "string",
"account_number": 64,
"sequence": 64,
"gas": 64
},
// Title of the proposal
"title": "string",
// Description of the proposal
"description": "string",
// PlainTextProposal supported now. SoftwareUpgradeProposal and other types may be supported soon
"proposal_type": "string",
// A cosmos address
"proposer": "string",
"initial_deposit": [
{
"denom": "string",
"amount": 64,
}
]
}
```
- Returns on success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result":{
"TODO": "TODO",
}
}
```
### GET /gov/proposals/{proposal-id}
- **URL**: `/gov/proposals/{proposal-id}`
- **Functionality**: Query a proposal
- Response on Success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result":{
"proposal_id": 1,
"title": "Example title",
"description": "a larger description with the details of the proposal",
"proposal_type": "Text",
"proposal_status": "DepositPeriod",
"tally_result": {
"yes": 0,
"abstain": 0,
"no": 0,
"no_with_veto": 0
},
"submit_block": 5238512,
"total_deposit": {"atom": 50},
"voting_start_block": -1
}
}
```
### POST /gov/proposals/{proposal-id}/deposits
- **URL**: `/gov/proposals/{proposal-id}/deposits`
- **Functionality**: Submit or rise a deposit to a proposal in order to make it active
- POST Body:
```json
{
"base_req": {
"name": "string",
"password": "string",
"chain_id": "string",
"account_number": 0,
"sequence": 0,
"gas": "simulate"
},
"depositer": "string",
"amount": 0,
}
```
- Returns on success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result":{
"TODO": "TODO",
}
}
```
### GET /gov/proposals/{proposal-id}/deposits/{address}
- **URL**: `/gov/proposals/{proposal-id}/deposits/{address}`
- **Functionality**: Query a validator's deposit to submit a proposal
- Returns on success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result":{
"amount": {"atom": 150},
"depositer": "cosmos1fedh326uxqlxs8ph9ej7cf854gz7fd5zlym5pd",
"proposal-id": 16
}
}
```
### GET /gov/proposals/{proposal-id}/tally
- **URL**: `/gov/proposals/{proposal-id}/tally`
- **Functionality**: Get the tally of a given proposal.
- Returns on success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result": {
"yes": 0,
"abstain": 0,
"no": 0,
"no_with_veto": 0
}
}
```
### GET /gov/proposals/{proposal-id}/votes
- **URL**: `/gov/proposals/{proposal-id}/votes`
- **Functionality**: Query all votes from a specific proposal
- Returns on success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result": [
{
"proposal-id": 1,
"voter": "cosmos1fedh326uxqlxs8ph9ej7cf854gz7fd5zlym5pd",
"option": "no_with_veto"
},
{
"proposal-id": 1,
"voter": "cosmos1849m9wncrqp6v4tkss6a3j8uzvuv0cp7f75lrq",
"option": "yes"
},
]
}
```
### POST /gov/proposals/{proposal-id}/votes
- **URL**: `/gov/proposals/{proposal-id}/votes`
- **Functionality**: Vote for a specific proposal
- POST Body:
```js
{
"base_req": {
"name": "string",
"password": "string",
"chain_id": "string",
"account_number": 0,
"sequence": 0,
"gas": "simulate"
},
// A cosmos address
"voter": "string",
// Value of the vote option `Yes`, `No` `Abstain`, `NoWithVeto`
"option": "string",
}
```
- Returns on success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result":{
"TODO": "TODO",
}
}
```
### GET /gov/proposals/{proposal-id}/votes/{address}
- **URL** : `/gov/proposals/{proposal-id}/votes/{address}`
- **Functionality**: Get the current `Option` submited by an address
- Returns on success:
```json
{
"rest api":"2.2",
"code":200,
"error":"",
"result":{
"proposal-id": 1,
"voter": "cosmos1fedh326uxqlxs8ph9ej7cf854gz7fd5zlym5pd",
"option": "no_with_veto"
}
}
```
## ICS23 - SlashingAPI
The SlashingAPI exposes all functionalities needed to slash (*i.e* penalize) validators and delegators in Proof-of-Stake. The penalization is a fine of the staking coin and jail time, defined by governance parameters. During the jail period, the penalized validator is "jailed".
### GET /slashing/validator/{validatorAddr}/signing-info
- **URL**: `/slashing/validator/{validatorAddr}/signing-info`
- **Functionality**: Query the information from a single validator.
- Returns on success:
```json
{
"rest api":"2.3",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
### POST /slashing/validators/{validatorAddr}/unjail
- **URL**: `/slashing/validators/{validatorAddr}/unjail`
- **Functionality**: Submit a message to unjail a validator after it has been penalized.
- POST Body:
```js
{
// Name of key to use
"name": "string",
// Password for that key
"password": "string",
"chain_id": "string",
"account_number": 64,
"sequence": 64,
"gas": 64,
}
```
- Returns on success:
```json
{
"rest api":"2.3",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```

View File

@ -1,203 +0,0 @@
# Load Balancing Module - WIP
The LCD will be an important bridge between service providers and cosmos blockchain network. Suppose
a service provider wants to monitor token information for millions of accounts. Then it has to keep
sending a large mount of requests to LCD to query token information. As a result, LCD will send huge
requests to full node to get token information and necessary proof which will cost full node much
computing and bandwidth resource. Too many requests to a single full node may result in some bad
situations:
```text
1. The full node crash possibility increases.
2. The reply delay increases.
3. The system reliability will decrease.
4. As the full node may belong to other people or associates, they may deny too frequent access from a single client.
```
It is very urgent to solve this problems. Here we consider to import load balancing into LCD. By the
help of load balancing, LCD can distribute millions of requests to a set of full nodes. Thus the
load of each full node won't be too heavy and the unavailable full nodes will be wiped out of query
list. In addition, the system reliability will increase.
## Design
This module need combine with client to realize the real load balancing. It can embed the
[HTTP Client](https://github.com/tendermint/tendermint/rpc/lib/client/httpclient.go). In other
wordswe realise the new httpclient based on `HTTP`.
```go
type HTTPLoadBalancer struct {
rpcs map[string]*rpcclient.JSONRPCClient
*WSEvents
}
```
## The Diagram of LCD RPC WorkFlow with LoadBalance
![The Diagram of LCD RPC WorkFlow](pics/loadbalanceDiagram.png)
In the above sequence diagram, application calls the `Request()`, and LCD finally call the
`HTTP.Request()` through the SecureClient `Wrapper`. In every `HTTP.Request()`, `Getclient()`
selects the current working rpcclient by the load balancing algorithm,then run the
`JSONRPCClient.Call()` to request from the Full Node, finally `UpdateClient()` updates the weight of
the current rpcclient according to the status that is returned by the full node. The `GetAddr()`
and `UpdateAddrWeight()` are realized in the load balancing module.
There are some abilities to do:
* Add the Remote Address
* Delete the Remote Address
* Update the weights of the addresses
## Load balancing Strategies
We can design some strategies like nginx to combine the different load balancing algorithms to get
the final remote. We can also get the status of the remote server to add or delete the addresses and
update weights of the addresses.
In a wordit can make the entire LCD work more effective in actual conditions.
We are working this module independently in this [Github Repository](https://github.com/MrXJC/GoLoadBalance).
## Interface And Type
### Balancer
This interface `Balancer`is the core of the package. Every load balancing algorithm should realize
it,and it defined two interfaces.
* `init` initialize the balancer, assigns the variables which `DoBalance` needs.
* `DoBalance` load balance the full node addresses according to the current situation.
```go
package balance
type Balancer interface {
init(NodeAddrs)
DoBalance(NodeAddrs) (*NodeAddr,int,error)
}
```
### NodeAddr
* host: ip address
* port: the number of port
* weight: the weight of this full node address,default:1
This NodeAddr is the base struct of the address.
```go
type NodeAddr struct{
host string
port int
weight int
}
func (p *NodeAddr) GetHost() string
func (p *NodeAddr) GetPort() int
func (p *NodeAddr) GetWeight() int
func (p *NodeAddr) updateWeight(weight int)
```
The `weight` is the important factor that schedules which full node the LCD calls. The weight can be
changed by the information from the full node. So we have the function `updateWegiht`.
### NodeAddrs
>in `balance/types.go`
`NodeAddrs` is the list of the full node address. This is the member variable in the
BalanceManager(`BalancerMgr`).
```go
type NodeAddrs []*NodeAddr
```
## Load Balancing Algorithm
### Random
>in `balance/random.go`
Random algorithm selects a remote address randomly to process the request. The probability of them
being selected is the same.
### RandomWeight
>in `balance/random.go`
RandomWeight Algorithm also selects a remote address randomly to process the request. But the higher
the weight, the greater the probability.
### RoundRobin
>in `balance/roundrobin.go`
RoundRobin Algorithm selects a remote address orderly. Every remote address have the same
probability to be selected.
### RoundRobinWeight
>in `balance/roundrobin.go`
RoundRobinWeight Algorthm selects a remote address orderly. But every remote address have different
probability to be selected which are determined by their weight.
### Hash
//TODO
## Load Balancing Manager
### BalanceMgr
>in `balance/manager.go`
* addrs: the set of the remote full node addresses
* balancers: map the string of balancer name to the specific balancer
* change: record whether the machine reinitialize after the `addrs` changes
`BalanceMgr` is the manager of many balancer. It is the access of load balancing. Its main function
is to maintain the `NodeAddrs` and to call the specific load balancing algorithm above.
```go
type BalanceMgr struct{
addrs NodeAddrs
balancers map[string]Balancer
change map[string]bool
}
func (p *BalanceMgr) RegisterBalancer(name string,balancer Balancer)
func (p *BalanceMgr) updateBalancer(name string)
func (p *BalanceMgr) AddNodeAddr(addr *NodeAddr)
func (p *BalanceMgr) DeleteNodeAddr(i int)
func (p *BalanceMgr) UpdateWeightNodeAddr(i int,weight int)
func (p *BalanceMgr) GetAddr(name string)(*NodeAddr,int,error) {
// if addrs change,update the balancer which we use.
if p.change[name]{
p.updateBalancer(name)
}
// get the balancer by name
balancer := p.balancers[name]
// use the load balancing algorithm
addr,index,err := balancer.DoBalance(p.addrs)
return addr,index,err
}
```
* `RegisterBalancer`: register the basic balancer implementing the `Balancer` interface and initialize them.
* `updateBalancer`: update the specific balancer after the `addrs` change.
* `AddNodeAddr`: add the remote address and set all the values of the `change` to true.
* `DeleteNodeAddr`: delete the remote address and set all the values of the `change` to true.
* `UpdateWeightNodeAddr`: update the weight of the remote address and set all the values of the `change` to true.
* `GetAddr`:select the address by the balancer the `name` decides.

View File

@ -1,16 +0,0 @@
# TODO
This document is a place to gather all points for future development.
## API
* finalise ICS0 - TendermintAPI
* make sure that the explorer and voyager can use it
* add ICS21 - StakingAPI
* add ICS22 - GovernanceAPI
* split Gaia Light into reusable components that other zones can leverage
* it should be possible to register extra standards on the light client
* the setup should be similar to how the app is currently started
* implement Gaia light and the general light client in Rust
* export the API as a C interface
* write thin wrappers around the C interface in JS, Swift and Kotlin/Java

View File

@ -1,6 +1,7 @@
# Getting Started
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 |
@ -9,12 +10,12 @@ To start a REST server, we need to specify the following parameters:
| trust-node | bool | "false" | true | Whether this LCD is connected to a trusted full node |
| trust-store | DIRECTORY | "$HOME/.lcd" | false | directory for save checkpoints and validator sets |
Sample command:
For example::
```bash
gaiacli rest-server --chain-id=test \
--laddr=tcp://localhost:1317 \
--node tcp://localhost:46657 \
--node tcp://localhost:26657 \
--trust-node=false
```
@ -23,7 +24,7 @@ The server listens on HTTPS by default. You can set the SSL certificate to be us
```bash
gaiacli rest-server --chain-id=test \
--laddr=tcp://localhost:1317 \
--node tcp://localhost:46657 \
--node tcp://localhost:26657 \
--trust-node=false \
--certfile=mycert.pem --keyfile=mykey.key
```
@ -31,26 +32,4 @@ gaiacli rest-server --chain-id=test \
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
make transaction faster and more reliable in the following cases.
### Create an account
![deposit](pics/create-account.png)
First you need to get a new seed phrase :[get-seed](api.md#keysseed---get)
After having new seed, you could generate a new account with it : [keys](api.md#keys---post)
### Transfer a token
![transfer](pics/transfer-tokens.png)
The first step is to build an asset transfer transaction. Here we can post all necessary parameters
to /create_transfer to get the unsigned transaction byte array. Refer to this link for detailed
operation: [build transaction](api.md#create_transfer---post)
Then sign the returned transaction byte array with users' private key. Finally broadcast the signed
transaction. Refer to this link for how to broadcast the signed transaction: [broadcast transaction](api.md#create_transfer---post)
For more information about the Gaia-Lite RPC, see the [swagger documentation](https://cosmos.network/rpc/)

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,30 +1,20 @@
# Cosmos-Sdk Light Client
# Overview
**See the Cosmos SDK lite Client RPC documentation [here](https://cosmos.network/rpc/)**
## Introduction
A light client allows clients, such as mobile phones, to receive proofs of the state of the
blockchain from any full node. Light clients do not have to trust any full node, since they are able
A lite client allows clients, such as mobile phones, to receive proofs of the state of the
blockchain from any full node. lite clients do not have to trust any full node, since they are able
to verify any proof they receive and hence full nodes cannot lie about the state of the network.
A light client can provide the same security as a full node with the minimal requirements on
bandwidth, computing and storage resource. Besides, it can also provide modular functionality
A lite client can provide the same security as a full node with the minimal requirements on
bandwidth, computing and storage resource. As well, it can also provide modular functionality
according to users' configuration. These fantastic features allow developers to build fully secure,
efficient and usable mobile apps, websites or any other applications without deploying or
maintaining any full blockchain nodes.
LCD will be used in the Cosmos Hub, the first Hub in the Cosmos network.
## Contents
1. [**Overview**](##Overview)
2. [**Get Started**](getting_started.md)
3. [**API**](api.md)
4. [**Specifications**](specification.md)
4. [**Update API docs To Swagger-UI**](update_API_docs.md)
## Overview
### What is a Light Client
### What is a lite Client
The LCD is split into two separate components. The first component is generic for any Tendermint
based application. It handles the security and connectivity aspects of following the header chain
@ -54,7 +44,7 @@ that offers stability guarantees around the zone API.
### Comparision
A full node of ABCI is different from its light client in the following ways:
A full node of ABCI is different from its lite client in the following ways:
|| Full Node | LCD | Description|
|-| ------------- | ----- | -------------- |
@ -71,22 +61,22 @@ A full node of ABCI is different from its light client in the following ways:
According to the above table, LCD can meet all users' functionality and security requirements, but
only requires little resource on bandwidth, computing, storage and power.
## How does LCD achieve high security?
## Achieving Security
### Trusted validator set
### Trusted Validator Set
The base design philosophy of lcd follows the two rules:
The base design philosophy of the LCD follows two rules:
1. **Doesn't trust any blockchain nodes, including validator nodes and other full nodes**
2. **Only trusts the whole validator set**
The original trusted validator set should be prepositioned into its trust store, usually this
validator set comes from genesis file. During running time, if LCD detects different validator set,
it will verify it and save new validated validator set to trust store.
validator set comes from genesis file. During runtime, if LCD detects a different validator set,
it will verify it and save new validated validator set to the trust store.
![validator-set-change](pics/validatorSetChange.png)
### Trust propagation
### Trust Propagation
From the above section, we come to know how to get trusted validator set and how lcd keeps track of
validator set evolution. Validator set is the foundation of trust, and the trust can propagate to
@ -97,5 +87,4 @@ follows:
In general, by trusted validator set, LCD can verify each block commit which contains all pre-commit
data and block header data. Then the block hash, data hash and appHash are trusted. Based on this
and merkle proof, all transactions data and ABCI states can be verified too. Detailed implementation
will be posted on technical specification.
and merkle proof, all transactions data and ABCI states can be verified too.

View File

@ -207,150 +207,3 @@ For instance:
* Update to 10000,tooMuchChangeErr
* Update to 7525, Success
* Update to 10000, Success
## Load Balancing
To improve LCD reliability and TPS, we recommend to connect LCD to more than one fullnode. But the
complexity will increase a lot. So load balancing module will be imported as the adapter. Please
refer to this link for detailed description: [load balancer](load_balancer.md)
## ICS1 (KeyAPI)
### [/keys - GET](api.md#keys---get)
Load the key store:
```go
db, err := dbm.NewGoLevelDB(KeyDBName, filepath.Join(rootDir, "keys"))
if err != nil {
return nil, err
}
keybase = client.GetKeyBase(db)
```
Iterate through the key store.
```go
var res []Info
iter := kb.db.Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
// key := iter.Key()
info, err := readInfo(iter.Value())
if err != nil {
return nil, err
}
res = append(res, info)
}
return res, nil
```
Encode the addresses and public keys in bech32.
```go
bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.PubKey.Address().Bytes()))
if err != nil {
return KeyOutput{}, err
}
bechPubKey, err := sdk.Bech32ifyAccPub(info.PubKey)
if err != nil {
return KeyOutput{}, err
}
return KeyOutput{
Name: info.Name,
Address: bechAccount,
PubKey: bechPubKey,
}, nil
```
### [/keys/recover - POST](api.md#keys/recover---get)
1. Load the key store.
2. Parameter checking. Name, password and seed should not be empty.
3. Check for keys with the same name.
4. Build the key from the name, password and seed.
5. Persist the key to key store.
### [/keys/create - GET](api.md#keys/create---get)**
1. Load the key store.
2. Create a new key in the key store.
3. Save the key to disk.
4. Return the seed.
### [/keys/{name} - GET](api.md#keysname---get)
1. Load the key store.
2. Iterate the whole key store to find the key by name.
3. Encode address and public key in bech32.
### [/keys/{name} - PUT](api.md#keysname---put)
1. Load the key store.
2. Iterate the whole key store to find the key by name.
3. Verify if that the old-password matches the current key password.
4. Re-persist the key with the new password.
### [/keys/{name} - DELETE](api.md#keysname---delete)
1. Load the key store.
2. Iterate the whole key store to find the key by name.
3. Verify that the specified password matches the current key password.
4. Delete the key from the key store.
## ICS20 (TokenAPI)
### [/bank/balance/{account}](api.md#bankbalanceaccount---get)
1. Decode the address from bech32 to hex.
2. Send a query request to a full node. Ask for proof if required by Gaia Light.
3. Verify the proof against the root of trust.
### [/bank/create_transfer](api.md#bankcreate_transfer---post)
1. Check the parameters.
2. Build the transaction with the specified parameters.
3. Serialise the transaction and return the JSON encoded sign bytes.
## ICS21 (StakingAPI)
### [/stake/delegators/{delegatorAddr}](api.md#stakedelegatorsdelegatorAddr---get)
TODO
### [/stake/delegators/{delegatorAddr}/validators](api.md#stakedelegatorsdelegatorAddrvalidators---get)
TODO
### [/stake/delegators/{delegatorAddr}/validators/{validatorAddr}](api.md#stakedelegatorsdelegatorAddrvalidatorsvalidatorAddr---get)
TODO
### [/stake/delegators/{delegatorAddr}/txs](api.md#stakedelegatorsdelegatorAddrtxs---get)
TODO
### [/stake/delegators/{delegatorAddr}/delegations](api.md#stakedelegatorsdelegatorAddrdelegations---post)
TODO
### [/stake/delegators/{delegatorAddr}/delegations/{validatorAddr}](api.md#stakedelegatorsdelegatorAddrdelegationsvalidatorAddr---get)
TODO
### [/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}](api.md#stakedelegatorsdelegatorAddrunbonding_delegationsvalidatorAddr---get)
TODO
### [/stake/validators](api.md#stakevalidators---get)
TODO
### [/stake/validators/{validatorAddr}](api.md#stakevalidatorsvalidatorAddr---get)
TODO

View File

@ -15,7 +15,7 @@ pool which validator holds individually
(`ValidatorDistribution.ProvisionsRewardPool`).
```
func AllocateFees(feesCollected sdk.Coins, feePool FeePool, proposer ValidatorDistribution,
func AllocateTokens(feesCollected sdk.Coins, feePool FeePool, proposer ValidatorDistribution,
sumPowerPrecommitValidators, totalBondedTokens, communityTax,
proposerCommissionRate sdk.Dec)

View File

@ -0,0 +1,28 @@
# Begin-Block
## Inflation
Inflation occurs at the beginning of each block.
### NextInflation
The target annual inflation rate is recalculated for each provisions cycle. The
inflation is also subject to a rate change (positive or negative) depending on
the distance from the desired ratio (67%). The maximum rate change possible is
defined to be 13% per year, however the annual inflation is capped as between
7% and 20%.
NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) {
inflationRateChangePerYear = (1 - bondedRatio/params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear/hrsPerYr
// increase the new annual inflation for this next cycle
inflation += inflationRateChange
if inflation > params.InflationMax {
inflation = params.InflationMax
}
if inflation < params.InflationMin {
inflation = params.InflationMin
}
return inflation

31
docs/spec/mint/state.md Normal file
View File

@ -0,0 +1,31 @@
## State
### Minter
The minter is a space for holding current inflation information.
- Minter: `0x00 -> amino(minter)`
```golang
type Minter struct {
InflationLastTime time.Time // block time which the last inflation was processed
Inflation sdk.Dec // current annual inflation rate
}
```
### Params
Minting params are held in the global params store.
- Params: `mint/params -> amino(params)`
```golang
type Params struct {
MintDenom string // type of coin to mint
InflationRateChange sdk.Dec // maximum annual change in inflation rate
InflationMax sdk.Dec // maximum inflation rate
InflationMin sdk.Dec // minimum inflation rate
GoalBonded sdk.Dec // goal of percent bonded atoms
}
```

View File

@ -93,25 +93,27 @@ for val in block.Validators:
index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW
signInfo.IndexOffset++
previous = SigningBitArray.Get(val.Address, index)
previous = MissedBlockBitArray.Get(val.Address, index)
// update counter if array has changed
if previous and val in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, false)
signInfo.SignedBlocksCounter--
else if !previous and val not in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, true)
signInfo.SignedBlocksCounter++
if !previous and val in block.AbsentValidators:
MissedBlockBitArray.Set(val.Address, index, true)
signInfo.MissedBlocksCounter++
else if previous and val not in block.AbsentValidators:
MissedBlockBitArray.Set(val.Address, index, false)
signInfo.MissedBlocksCounter--
// else previous == val not in block.AbsentValidators, no change
// validator must be active for at least SIGNED_BLOCKS_WINDOW
// before they can be automatically unbonded for failing to be
// included in 50% of the recent LastCommits
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
minSigned = SIGNED_BLOCKS_WINDOW / 2
if height > minHeight AND signInfo.SignedBlocksCounter < minSigned:
maxMissed = SIGNED_BLOCKS_WINDOW / 2
if height > minHeight AND signInfo.MissedBlocksCounter > maxMissed:
signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION
signInfo.IndexOffset = 0
signInfo.MissedBlocksCounter = 0
clearMissedBlockBitArray()
slash & unbond the validator
SigningInfo.Set(val.Address, signInfo)

View File

@ -12,6 +12,17 @@ and `SlashedSoFar` of `0`:
```
onValidatorBonded(address sdk.ValAddress)
signingInfo, found = getValidatorSigningInfo(address)
if !found {
signingInfo = ValidatorSigningInfo {
StartHeight : CurrentHeight,
IndexOffset : 0,
JailedUntil : time.Unix(0, 0),
MissedBloskCounter : 0
}
setValidatorSigningInfo(signingInfo)
}
slashingPeriod = SlashingPeriod{
ValidatorAddr : address,
StartHeight : CurrentHeight,

View File

@ -17,18 +17,18 @@ Information about validator activity is tracked in a `ValidatorSigningInfo`.
It is indexed in the store as follows:
- SigningInfo: ` 0x01 | ValTendermintAddr -> amino(valSigningInfo)`
- SigningBitArray: ` 0x02 | ValTendermintAddr | LittleEndianUint64(signArrayIndex) -> VarInt(didSign)`
- MissedBlocksBitArray: ` 0x02 | ValTendermintAddr | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)`
The first map allows us to easily lookup the recent signing info for a
validator, according to the Tendermint validator address. The second map acts as
a bit-array of size `SIGNED_BLOCKS_WINDOW` that tells us if the validator signed for a given index in the bit-array.
a bit-array of size `SIGNED_BLOCKS_WINDOW` that tells us if the validator missed the block for a given index in the bit-array.
The index in the bit-array is given as little endian uint64.
The result is a `varint` that takes on `0` or `1`, where `0` indicates the
validator did not sign the corresponding block, and `1` indicates they did.
validator did not miss (did sign) the corresponding block, and `1` indicates they missed the block (did not sign).
Note that the SigningBitArray is not explicitly initialized up-front. Keys are
Note that the MissedBlocksBitArray is not explicitly initialized up-front. Keys are
added as we progress through the first `SIGNED_BLOCKS_WINDOW` blocks for a newly
bonded validator.
@ -40,7 +40,7 @@ type ValidatorSigningInfo struct {
IndexOffset int64 // Offset into the signed block bit array
JailedUntilHeight int64 // Block height until which the validator is jailed,
// or sentinel value of 0 for not jailed
SignedBlocksCounter int64 // Running counter of signed blocks
MissedBlocksCounter int64 // Running counter of missed blocks
}
```
@ -49,7 +49,7 @@ Where:
* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power).
* `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not).
* `JailedUntil` is set whenever the candidate is jailed due to downtime
* `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always.
* `MissedBlocksCounter` is a counter kept to avoid unnecessary array reads. `MissedBlocksBitArray.Sum() == MissedBlocksCounter` always.
## Slashing Period

View File

@ -25,10 +25,6 @@ handleMsgUnjail(tx TxUnjail)
if block time < info.JailedUntil
fail with "Validator still jailed, cannot unjail until period has expired"
// Update the start height so the validator won't be immediately unbonded again
info.StartHeight = BlockHeight
setValidatorSigningInfo(info)
validator.Jailed = false
setValidator(validator)

View File

@ -3,15 +3,19 @@
## Unbonding Validator Queue
For all unbonding validators that have finished their unbonding period, this switches their validator.Status
from sdk.Unbonding to sdk.Unbonded
from sdk.Unbonding to sdk.Unbonded if they still have any delegation left. Otherwise, it deletes it from state.
```golang
validatorQueue(currTime time.Time):
// unbonding validators are in ordered queue from oldest to newest
for all unbondingValidators whose CompleteTime < currTime:
validator = GetValidator(unbondingValidator.ValidatorAddr)
validator.Status = sdk.Bonded
SetValidator(unbondingValidator)
if validator.DelegatorShares == 0 {
RemoveValidator(unbondingValidator)
} else {
validator.Status = sdk.Unbonded
SetValidator(unbondingValidator)
}
return
```
@ -61,4 +65,4 @@ redelegationQueue(currTime time.Time):
for all redelegations whose CompleteTime < currTime:
removeRedelegation(redelegation)
return
```
```

View File

@ -12,10 +12,6 @@ inflation information, etc.
type Pool struct {
LooseTokens int64 // tokens not associated with any bonded validator
BondedTokens int64 // reserve of bonded tokens
InflationLastTime int64 // block which the last inflation was processed // TODO make time
Inflation sdk.Dec // current annual inflation rate
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily)
}
```
@ -28,11 +24,6 @@ overall functioning of the stake module.
```golang
type Params struct {
InflationRateChange sdk.Dec // maximum annual change in inflation rate
InflationMax sdk.Dec // maximum inflation rate
InflationMin sdk.Dec // minimum inflation rate
GoalBonded sdk.Dec // Goal of percent bonded atoms
MaxValidators uint16 // maximum number of validators
BondDenom string // bondable coin denomination
}

View File

@ -180,7 +180,7 @@ startUnbonding(tx TxStartUnbonding):
validator = updateValidator(validator)
if validator.DelegatorShares == 0 {
if validator.Status == Unbonded && validator.DelegatorShares == 0 {
removeValidator(validator.Operator)
return
@ -189,8 +189,7 @@ startUnbonding(tx TxStartUnbonding):
### TxRedelegation
The redelegation command allows delegators to instantly switch validators. Once
the unbonding period has passed, the redelegation must be completed with
txRedelegationComplete.
the unbonding period has passed, the redelegation is automatically completed in the EndBlocker.
```golang
type TxRedelegate struct {

View File

@ -2,6 +2,7 @@ package app
import (
"encoding/json"
"os"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
@ -21,6 +22,12 @@ const (
appName = "BasecoinApp"
)
// default home directories for expected binaries
var (
DefaultCLIHome = os.ExpandEnv("$HOME/.basecli")
DefaultNodeHome = os.ExpandEnv("$HOME/.basecoind")
)
// BasecoinApp implements an extended ABCI application. It contains a BaseApp,
// a codec for serialization, KVStore keys for multistore state management, and
// various mappers and keepers to manage getting, setting, and serializing the

View File

@ -6,6 +6,7 @@ import (
"os"
"testing"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
"github.com/stretchr/testify/require"
@ -13,10 +14,11 @@ import (
var (
basecoindHome = ""
basecliHome = ""
)
func init() {
basecoindHome = getTestingHomeDir()
basecoindHome, basecliHome = getTestingHomeDirs()
}
func TestInitStartSequence(t *testing.T) {
@ -32,8 +34,8 @@ func executeInit(t *testing.T) {
chainID string
initRes map[string]json.RawMessage
)
out := tests.ExecuteT(t, fmt.Sprintf("basecoind --home=%s init", basecoindHome), "")
err := json.Unmarshal([]byte(out), &initRes)
_, stderr := tests.ExecuteT(t, fmt.Sprintf("basecoind --home=%s --home-client=%s init --name=test", basecoindHome, basecliHome), app.DefaultKeyPass)
err := json.Unmarshal([]byte(stderr), &initRes)
require.NoError(t, err)
err = json.Unmarshal(initRes["chain_id"], &chainID)
require.NoError(t, err)
@ -45,8 +47,9 @@ func executeStart(t *testing.T, servAddr, port string) {
tests.WaitForTMStart(port)
}
func getTestingHomeDir() string {
func getTestingHomeDirs() (string, string) {
tmpDir := os.TempDir()
basecoindHome := fmt.Sprintf("%s%s.test_basecoind", tmpDir, string(os.PathSeparator))
return basecoindHome
basecliHome := fmt.Sprintf("%s%s.test_basecli", tmpDir, string(os.PathSeparator))
return basecoindHome, basecliHome
}

View File

@ -1,8 +1,6 @@
package main
import (
"os"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/lcd"
@ -86,7 +84,7 @@ func main() {
)
// prepare and add flags
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
executor := cli.PrepareMainCmd(rootCmd, "BC", app.DefaultCLIHome)
err := executor.Execute()
if err != nil {
// Note: Handle with #870

View File

@ -2,23 +2,32 @@ package main
import (
"encoding/json"
"fmt"
"github.com/tendermint/tendermint/p2p"
"io"
"os"
"github.com/cosmos/cosmos-sdk/baseapp"
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
"github.com/spf13/viper"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
)
const (
flagClientHome = "home-client"
)
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
@ -30,7 +39,7 @@ func main() {
}
appInit := server.DefaultAppInit
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit))
server.AddCommands(ctx, cdc, rootCmd, appInit,
@ -47,6 +56,69 @@ func main() {
}
}
// get cmd to initialize all files for tendermint and application
// nolint: errcheck
func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command {
cmd := &cobra.Command{
Use: "init",
Short: "Initialize genesis config, priv-validator file, and p2p-node file",
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
chainID := viper.GetString(client.FlagChainID)
if chainID == "" {
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
}
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
nodeID := string(nodeKey.ID())
pk := gaiaInit.ReadOrCreatePrivValidator(config.PrivValidatorFile())
genTx, appMessage, validator, err := server.SimpleAppGenTx(cdc, pk)
if err != nil {
return err
}
appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx})
if err != nil {
return err
}
appStateJSON, err := cdc.MarshalJSON(appState)
if err != nil {
return err
}
toPrint := struct {
ChainID string `json:"chain_id"`
NodeID string `json:"noide_id"`
AppMessage json.RawMessage `json:"app_message"`
}{
chainID,
nodeID,
appMessage,
}
out, err := codec.MarshalJSONIndent(cdc, toPrint)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "%s\n", string(out))
return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON)
},
}
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().String(client.FlagName, "", "validator's moniker")
cmd.MarkFlagRequired(client.FlagName)
return cmd
}
func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Application {
return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning")))
}

View File

@ -2,6 +2,7 @@ package app
import (
"encoding/json"
"os"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
@ -27,6 +28,12 @@ const (
appName = "DemocoinApp"
)
// default home directories for expected binaries
var (
DefaultCLIHome = os.ExpandEnv("$HOME/.democli")
DefaultNodeHome = os.ExpandEnv("$HOME/.democoind")
)
// Extended ABCI application
type DemocoinApp struct {
*bam.BaseApp

View File

@ -3,6 +3,7 @@ package clitest
import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"os"
"testing"
@ -13,10 +14,11 @@ import (
var (
democoindHome = ""
democliHome = ""
)
func init() {
democoindHome = getTestingHomeDir()
democoindHome, democliHome = getTestingHomeDirs()
}
func TestInitStartSequence(t *testing.T) {
@ -32,8 +34,8 @@ func executeInit(t *testing.T) {
chainID string
initRes map[string]json.RawMessage
)
out := tests.ExecuteT(t, fmt.Sprintf("democoind --home=%s init", democoindHome), "")
err := json.Unmarshal([]byte(out), &initRes)
_, stderr := tests.ExecuteT(t, fmt.Sprintf("democoind --home=%s --home-client=%s init --name=test", democoindHome, democliHome), app.DefaultKeyPass)
err := json.Unmarshal([]byte(stderr), &initRes)
require.NoError(t, err)
err = json.Unmarshal(initRes["chain_id"], &chainID)
require.NoError(t, err)
@ -45,8 +47,9 @@ func executeStart(t *testing.T, servAddr, port string) {
tests.WaitForTMStart(port)
}
func getTestingHomeDir() string {
func getTestingHomeDirs() (string, string) {
tmpDir := os.TempDir()
democoindHome := fmt.Sprintf("%s%s.test_democoind", tmpDir, string(os.PathSeparator))
return democoindHome
democliHome := fmt.Sprintf("%s%s.test_democli", tmpDir, string(os.PathSeparator))
return democoindHome, democliHome
}

View File

@ -1,8 +1,6 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/libs/cli"
@ -91,7 +89,7 @@ func main() {
)
// prepare and add flags
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.democli"))
executor := cli.PrepareMainCmd(rootCmd, "BC", app.DefaultCLIHome)
err := executor.Execute()
if err != nil {
// handle with #870

View File

@ -2,6 +2,11 @@ package main
import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"io"
"os"
@ -19,10 +24,13 @@ import (
"github.com/cosmos/cosmos-sdk/server"
)
const (
flagClientHome = "home-client"
)
// init parameters
var CoolAppInit = server.AppInit{
AppGenState: CoolAppGenState,
AppGenTx: server.SimpleAppGenTx,
}
// coolGenAppParams sets up the app_state and appends the cool app state
@ -52,6 +60,69 @@ func CoolAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState js
return
}
// get cmd to initialize all files for tendermint and application
// nolint: errcheck
func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command {
cmd := &cobra.Command{
Use: "init",
Short: "Initialize genesis config, priv-validator file, and p2p-node file",
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
chainID := viper.GetString(client.FlagChainID)
if chainID == "" {
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
}
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
nodeID := string(nodeKey.ID())
pk := gaiaInit.ReadOrCreatePrivValidator(config.PrivValidatorFile())
genTx, appMessage, validator, err := server.SimpleAppGenTx(cdc, pk)
if err != nil {
return err
}
appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx})
if err != nil {
return err
}
appStateJSON, err := cdc.MarshalJSON(appState)
if err != nil {
return err
}
toPrint := struct {
ChainID string `json:"chain_id"`
NodeID string `json:"noide_id"`
AppMessage json.RawMessage `json:"app_message"`
}{
chainID,
nodeID,
appMessage,
}
out, err := codec.MarshalJSONIndent(cdc, toPrint)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "%s\n", string(out))
return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON)
},
}
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().String(client.FlagName, "", "validator's moniker")
cmd.MarkFlagRequired(client.FlagName)
return cmd
}
func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application {
return app.NewDemocoinApp(logger, db)
}
@ -71,7 +142,7 @@ func main() {
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, CoolAppInit))
rootCmd.AddCommand(InitCmd(ctx, cdc, CoolAppInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, CoolAppInit))
server.AddCommands(ctx, cdc, rootCmd, CoolAppInit,

View File

@ -35,16 +35,3 @@ func (c *Config) MinimumFees() sdk.Coins {
// DefaultConfig returns server's default configuration.
func DefaultConfig() *Config { return &Config{BaseConfig{MinFees: defaultMinimumFees}} }
//_____________________________________________________________________
// Configuration structure for command functions that share configuration.
// For example: init, init gen-tx and testnet commands need similar input and run the same code
// Storage for init gen-tx command input parameters
type GenTx struct {
Name string
CliRoot string
Overwrite bool
IP string
}

View File

@ -2,111 +2,59 @@ package server
import (
"encoding/json"
"errors"
"fmt"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tendermint/libs/db"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/types"
clkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/codec"
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
tmtypes "github.com/tendermint/tendermint/types"
)
//Parameter names, for init gen-tx command
var (
FlagName = "name"
FlagClientHome = "home-client"
FlagOWK = "owk"
)
//parameter names, init command
var (
FlagOverwrite = "overwrite"
FlagWithTxs = "with-txs"
FlagIP = "ip"
FlagChainID = "chain-id"
)
// genesis piece structure for creating combined genesis
type GenesisTx struct {
NodeID string `json:"node_id"`
IP string `json:"ip"`
Validator tmtypes.GenesisValidator `json:"validator"`
AppGenTx json.RawMessage `json:"app_gen_tx"`
}
// Storage for init command input parameters
type InitConfig struct {
ChainID string
GenTxs bool
GenTxsDir string
Overwrite bool
}
//________________________________________________________________________________________
//_____________________________________________________________________
// Core functionality passed from the application to the server init command
type AppInit struct {
// flags required for application init functions
FlagsAppGenState *pflag.FlagSet
FlagsAppGenTx *pflag.FlagSet
// create the application genesis tx
AppGenTx func(cdc *codec.Codec, pk crypto.PubKey, genTxConfig serverconfig.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error)
// AppGenState creates the core parameters initialization. It takes in a
// pubkey meant to represent the pubkey of the validator of this machine.
AppGenState func(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error)
AppGenState func(cdc *codec.Codec, appGenTx []json.RawMessage) (appState json.RawMessage, err error)
}
// SimpleGenTx is a simple genesis tx
type SimpleGenTx struct {
Addr sdk.AccAddress `json:"addr"`
}
//_____________________________________________________________________
// simple default application init
var DefaultAppInit = AppInit{
AppGenTx: SimpleAppGenTx,
AppGenState: SimpleAppGenState,
}
// simple genesis tx
type SimpleGenTx struct {
Addr sdk.AccAddress `json:"addr"`
}
// Generate a genesis transaction
func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey, genTxConfig serverconfig.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) {
var addr sdk.AccAddress
var secret string
addr, secret, err = GenerateCoinKey()
if err != nil {
return
}
var bz []byte
simpleGenTx := SimpleGenTx{addr}
simpleGenTx := SimpleGenTx{Addr: addr}
bz, err = cdc.MarshalJSON(simpleGenTx)
if err != nil {
return
}
appGenTx = json.RawMessage(bz)
mm := map[string]string{"secret": secret}
bz, err = cdc.MarshalJSON(mm)
if err != nil {
return
}
cliPrint = json.RawMessage(bz)
validator = tmtypes.GenesisValidator{
PubKey: pk,
Power: 10,
@ -122,8 +70,8 @@ func SimpleAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState
return
}
var genTx SimpleGenTx
err = cdc.UnmarshalJSON(appGenTxs[0], &genTx)
var tx SimpleGenTx
err = cdc.UnmarshalJSON(appGenTxs[0], &tx)
if err != nil {
return
}
@ -138,7 +86,7 @@ func SimpleAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState
}
]
}]
}`, genTx.Addr))
}`, tx.Addr))
return
}
@ -176,7 +124,8 @@ func GenerateSaveCoinKey(clientRoot, keyName, keyPass string, overwrite bool) (s
if !overwrite {
_, err := keybase.Get(keyName)
if err == nil {
return sdk.AccAddress([]byte{}), "", errors.New("key already exists, overwrite is disabled")
return sdk.AccAddress([]byte{}), "", fmt.Errorf(
"key already exists, overwrite is disabled (clientRoot: %s)", clientRoot)
}
}

View File

@ -6,14 +6,11 @@ import (
"path/filepath"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
gc "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -126,14 +123,3 @@ func AppGenStateEmpty(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMes
appState = json.RawMessage(``)
return
}
// Return a validator, not much else
func AppGenTx(_ *codec.Codec, pk crypto.PubKey, genTxConfig gc.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
validator = tmtypes.GenesisValidator{
PubKey: pk,
Power: 10,
}
return
}

View File

@ -2,6 +2,7 @@ package server
import (
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"io/ioutil"
"net"
"os"
@ -42,6 +43,7 @@ func SetupViper(t *testing.T) func() {
rootDir, err := ioutil.TempDir("", "mock-sdk-cmd")
require.Nil(t, err)
viper.Set(cli.HomeFlag, rootDir)
viper.Set(client.FlagName, "moniker")
return func() {
err := os.RemoveAll(rootDir)
if err != nil {

108
store/list.go Normal file
View File

@ -0,0 +1,108 @@
package store
import (
"fmt"
"strconv"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Key for the length of the list
func LengthKey() []byte {
return []byte{0x00}
}
// Key for the elements of the list
func ElemKey(index uint64) []byte {
return append([]byte{0x01}, []byte(fmt.Sprintf("%020d", index))...)
}
// List defines an integer indexable mapper
// It panics when the element type cannot be (un/)marshalled by the codec
type List struct {
cdc *codec.Codec
store sdk.KVStore
}
// NewList constructs new List
func NewList(cdc *codec.Codec, store sdk.KVStore) List {
return List{
cdc: cdc,
store: store,
}
}
// Len() returns the length of the list
// The length is only increased by Push() and not decreased
// List dosen't check if an index is in bounds
// The user should check Len() before doing any actions
func (m List) Len() (res uint64) {
bz := m.store.Get(LengthKey())
if bz == nil {
return 0
}
m.cdc.MustUnmarshalBinary(bz, &res)
return
}
// Get() returns the element by its index
func (m List) Get(index uint64, ptr interface{}) error {
bz := m.store.Get(ElemKey(index))
return m.cdc.UnmarshalBinary(bz, ptr)
}
// Set() stores the element to the given position
// Setting element out of range will break length counting
// Use Push() instead of Set() to append a new element
func (m List) Set(index uint64, value interface{}) {
bz := m.cdc.MustMarshalBinary(value)
m.store.Set(ElemKey(index), bz)
}
// Delete() deletes the element in the given position
// Other elements' indices are preserved after deletion
// Panics when the index is out of range
func (m List) Delete(index uint64) {
m.store.Delete(ElemKey(index))
}
// Push() inserts the element to the end of the list
// It will increase the length when it is called
func (m List) Push(value interface{}) {
length := m.Len()
m.Set(length, value)
m.store.Set(LengthKey(), m.cdc.MustMarshalBinary(length+1))
}
// Iterate() is used to iterate over all existing elements in the list
// Return true in the continuation to break
// The second element of the continuation will indicate the position of the element
// Using it with Get() will return the same one with the provided element
// CONTRACT: No writes may happen within a domain while iterating over it.
func (m List) Iterate(ptr interface{}, fn func(uint64) bool) {
iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01})
for ; iter.Valid(); iter.Next() {
v := iter.Value()
m.cdc.MustUnmarshalBinary(v, ptr)
k := iter.Key()
s := string(k[len(k)-20:])
index, err := strconv.ParseUint(s, 10, 64)
if err != nil {
panic(err)
}
if fn(index) {
break
}
}
iter.Close()
}
func subspace(prefix []byte) (start, end []byte) {
end = make([]byte, len(prefix))
copy(end, prefix)
end[len(end)-1]++
return prefix, end
}

Some files were not shown because too many files have changed in this diff Show More