Merge remote-tracking branch 'origin/develop' into cwgoes/zero-power-should-not-be-bonded

This commit is contained in:
Christopher Goes
2018-07-07 01:49:46 +02:00
60 changed files with 1199 additions and 582 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
package cli
import (
"errors"
"fmt"
"github.com/spf13/cobra"
@@ -54,7 +55,7 @@ func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecode
// Check if account was found
if res == nil {
return sdk.ErrUnknownAddress("No account with address " + addr +
return errors.New("No account with address " + addr +
" was found in the state.\nAre you sure there has been a transaction involving it?")
}
+11 -17
View File
@@ -110,38 +110,32 @@ func (fee StdFee) Bytes() []byte {
// and the Sequence numbers for each signature (prevent
// inchain replay and enforce tx ordering per account).
type StdSignDoc struct {
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
FeeBytes json.RawMessage `json:"fee_bytes"`
MsgsBytes json.RawMessage `json:"msg_bytes"`
Memo string `json:"memo"`
AccountNumber int64 `json:"account_number"`
ChainID string `json:"chain_id"`
Fee json.RawMessage `json:"fee"`
Memo string `json:"memo"`
Msgs []json.RawMessage `json:"msgs"`
Sequence int64 `json:"sequence"`
}
// StdSignBytes returns the bytes to sign for a transaction.
// TODO: change the API to just take a chainID and StdTx ?
func StdSignBytes(chainID string, accnum int64, sequence int64, fee StdFee, msgs []sdk.Msg, memo string) []byte {
var msgsBytes []json.RawMessage
for _, msg := range msgs {
msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes()))
}
msgBytes, err := msgCdc.MarshalJSON(msgsBytes)
if err != nil {
panic(err)
}
bz, err := msgCdc.MarshalJSON(StdSignDoc{
ChainID: chainID,
AccountNumber: accnum,
Sequence: sequence,
FeeBytes: json.RawMessage(fee.Bytes()),
MsgsBytes: json.RawMessage(msgBytes),
ChainID: chainID,
Fee: json.RawMessage(fee.Bytes()),
Memo: memo,
Msgs: msgsBytes,
Sequence: sequence,
})
if err != nil {
panic(err)
}
return bz
return sdk.MustSortJSON(bz)
}
// StdSignMsg is a convenience structure for passing along
+17 -6
View File
@@ -1,6 +1,7 @@
package auth
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
@@ -10,12 +11,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// func newStdFee() StdFee {
// return NewStdFee(100,
// Coin{"atom", 150},
// )
// }
func TestStdTx(t *testing.T) {
priv := crypto.GenPrivKeyEd25519()
addr := priv.PubKey().Address()
@@ -30,3 +25,19 @@ func TestStdTx(t *testing.T) {
feePayer := FeePayer(tx)
require.Equal(t, addr, feePayer)
}
func TestStdSignBytes(t *testing.T) {
priv := crypto.GenPrivKeyEd25519()
addr := priv.PubKey().Address()
msgs := []sdk.Msg{sdk.NewTestMsg(addr)}
fee := newStdFee()
signMsg := StdSignMsg{
"1234",
3,
6,
fee,
msgs,
"memo",
}
require.Equal(t, fmt.Sprintf("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"5000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\"}", addr), string(signMsg.Bytes()))
}
+14 -10
View File
@@ -5,6 +5,8 @@ import (
"github.com/stretchr/testify/require"
"math/rand"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/mock"
@@ -81,17 +83,19 @@ func getMockApp(t *testing.T) *mock.App {
return mapp
}
// getBenchmarkMockApp initializes a mock application for this module, for purposes of benchmarking
// Any long term API support commitments do not apply to this function.
func getBenchmarkMockApp() (*mock.App, error) {
mapp := mock.NewApp()
func TestBankWithRandomMessages(t *testing.T) {
mapp := getMockApp(t)
setup := func(r *rand.Rand, keys []crypto.PrivKey) {
return
}
RegisterWire(mapp.Cdc)
coinKeeper := NewKeeper(mapp.AccountMapper)
mapp.Router().AddRoute("bank", NewHandler(coinKeeper))
err := mapp.CompleteSetup([]*sdk.KVStoreKey{})
return mapp, err
mapp.RandomizedTesting(
t,
[]mock.TestAndRunTx{TestAndRunSingleInputMsgSend},
[]mock.RandSetup{setup},
[]mock.Invariant{ModuleInvariants},
100, 30, 30,
)
}
func TestMsgSendWithAccounts(t *testing.T) {
+13
View File
@@ -10,6 +10,19 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
)
// getBenchmarkMockApp initializes a mock application for this module, for purposes of benchmarking
// Any long term API support commitments do not apply to this function.
func getBenchmarkMockApp() (*mock.App, error) {
mapp := mock.NewApp()
RegisterWire(mapp.Cdc)
coinKeeper := NewKeeper(mapp.AccountMapper)
mapp.Router().AddRoute("bank", NewHandler(coinKeeper))
err := mapp.CompleteSetup([]*sdk.KVStoreKey{})
return mapp, err
}
func BenchmarkOneBankSendTxPerBlock(b *testing.B) {
benchmarkApp, _ := getBenchmarkMockApp()
+29 -18
View File
@@ -1,25 +1,24 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"errors"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/bank/client"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
flagTo = "to"
flagAmount = "amount"
flagAsync = "async"
)
// SendTxCommand will create a send tx and sign it with the given key
// SendTxCmd will create a send tx and sign it with the given key
func SendTxCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "send",
@@ -33,35 +32,48 @@ func SendTxCmd(cdc *wire.Codec) *cobra.Command {
return err
}
fromAcc, err := ctx.QueryStore(auth.AddressStoreKey(from), ctx.AccountStore)
if err != nil {
return err
}
bech32From := sdk.MustBech32ifyAcc(from)
// Check if account was found
if fromAcc == nil {
return errors.New("No account with address " + bech32From +
" was found in the state.\nAre you sure there has been a transaction involving it?")
}
toStr := viper.GetString(flagTo)
to, err := sdk.GetAccAddressBech32(toStr)
if err != nil {
return err
}
// parse coins
// parse coins trying to be sent
amount := viper.GetString(flagAmount)
coins, err := sdk.ParseCoins(amount)
if err != nil {
return err
}
// ensure account has enough coins
account, err := ctx.Decoder(fromAcc)
if err != nil {
return err
}
if !account.GetCoins().IsGTE(coins) {
return errors.New("Address " + bech32From +
" doesn't have enough coins to pay for this transaction.")
}
// build and sign the transaction, then broadcast to Tendermint
msg := client.BuildMsg(from, to, coins)
if viper.GetBool(flagAsync) {
res, err := ctx.EnsureSignBuildBroadcastAsync(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Println("Async tx sent. tx hash: ", res.Hash.String())
return nil
}
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
@@ -69,7 +81,6 @@ func SendTxCmd(cdc *wire.Codec) *cobra.Command {
cmd.Flags().String(flagTo, "", "Address to send coins")
cmd.Flags().String(flagAmount, "", "Amount of coins to send")
cmd.Flags().Bool(flagAsync, false, "Pass the async flag to send a tx without waiting for the tx to be included in a block")
return cmd
}
+4 -4
View File
@@ -72,7 +72,7 @@ func (msg MsgSend) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// Implements Msg.
@@ -133,7 +133,7 @@ func (msg MsgIssue) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// Implements Msg.
@@ -162,7 +162,7 @@ func (in Input) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return bin
return sdk.MustSortJSON(bin)
}
// ValidateBasic - validate transaction input
@@ -209,7 +209,7 @@ func (out Output) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return bin
return sdk.MustSortJSON(bin)
}
// ValidateBasic - validate transaction output
+2 -2
View File
@@ -187,7 +187,7 @@ func TestMsgSendGetSignBytes(t *testing.T) {
}
res := msg.GetSignBytes()
expected := `{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"denom":"atom","amount":"10"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"denom":"atom","amount":"10"}]}]}`
expected := `{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}`
require.Equal(t, expected, string(res))
}
@@ -257,7 +257,7 @@ func TestMsgIssueGetSignBytes(t *testing.T) {
}
res := msg.GetSignBytes()
expected := `{"banker":"cosmosaccaddr1d9h8qat5e4ehc5","outputs":[{"address":"cosmosaccaddr1d3hkzm3dveex7mfdvfsku6cwsauqd","coins":[{"denom":"atom","amount":"10"}]}]}`
expected := `{"banker":"cosmosaccaddr1d9h8qat5e4ehc5","outputs":[{"address":"cosmosaccaddr1d3hkzm3dveex7mfdvfsku6cwsauqd","coins":[{"amount":"10","denom":"atom"}]}]}`
require.Equal(t, expected, string(res))
}
+150
View File
@@ -0,0 +1,150 @@
package bank
import (
"errors"
"fmt"
"math/big"
"math/rand"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
)
// ModuleInvariants runs all invariants of the bank module.
// Currently runs non-negative balance invariant and TotalCoinsInvariant
func ModuleInvariants(t *testing.T, app *mock.App, log string) {
NonnegativeBalanceInvariant(t, app, log)
TotalCoinsInvariant(t, app, log)
}
// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances
func NonnegativeBalanceInvariant(t *testing.T, app *mock.App, log string) {
ctx := app.NewContext(false, abci.Header{})
accts := mock.GetAllAccounts(app.AccountMapper, ctx)
for _, acc := range accts {
coins := acc.GetCoins()
assert.True(t, coins.IsNotNegative(),
fmt.Sprintf("%s has a negative denomination of %s\n%s",
acc.GetAddress().String(),
coins.String(),
log),
)
}
}
// TotalCoinsInvariant checks that the sum of the coins across all accounts
// is what is expected
func TotalCoinsInvariant(t *testing.T, app *mock.App, log string) {
ctx := app.BaseApp.NewContext(false, abci.Header{})
totalCoins := sdk.Coins{}
chkAccount := func(acc auth.Account) bool {
coins := acc.GetCoins()
totalCoins = totalCoins.Plus(coins)
return false
}
app.AccountMapper.IterateAccounts(ctx, chkAccount)
require.Equal(t, app.TotalCoinsSupply, totalCoins, log)
}
// TestAndRunSingleInputMsgSend tests and runs a single msg send, with one input and one output, where both
// accounts already exist.
func TestAndRunSingleInputMsgSend(t *testing.T, r *rand.Rand, app *mock.App, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
fromKey := keys[r.Intn(len(keys))]
fromAddr := fromKey.PubKey().Address()
toKey := keys[r.Intn(len(keys))]
// Disallow sending money to yourself
for {
if !fromKey.Equals(toKey) {
break
}
toKey = keys[r.Intn(len(keys))]
}
toAddr := toKey.PubKey().Address()
initFromCoins := app.AccountMapper.GetAccount(ctx, fromAddr).GetCoins()
denomIndex := r.Intn(len(initFromCoins))
amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount)
if goErr != nil {
return "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, nil
}
action = fmt.Sprintf("%s is sending %s %s to %s",
fromAddr.String(),
amt.String(),
initFromCoins[denomIndex].Denom,
toAddr.String(),
)
log = fmt.Sprintf("%s\n%s", log, action)
coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}}
var msg = MsgSend{
Inputs: []Input{NewInput(fromAddr, coins)},
Outputs: []Output{NewOutput(toAddr, coins)},
}
sendAndVerifyMsgSend(t, app, msg, ctx, log, []crypto.PrivKey{fromKey})
return action, nil
}
// Sends and verifies the transition of a msg send. This fails if there are repeated inputs or outputs
func sendAndVerifyMsgSend(t *testing.T, app *mock.App, msg MsgSend, ctx sdk.Context, log string, privkeys []crypto.PrivKey) {
initialInputAddrCoins := make([]sdk.Coins, len(msg.Inputs))
initialOutputAddrCoins := make([]sdk.Coins, len(msg.Outputs))
AccountNumbers := make([]int64, len(msg.Inputs))
SequenceNumbers := make([]int64, len(msg.Inputs))
for i := 0; i < len(msg.Inputs); i++ {
acc := app.AccountMapper.GetAccount(ctx, msg.Inputs[i].Address)
AccountNumbers[i] = acc.GetAccountNumber()
SequenceNumbers[i] = acc.GetSequence()
initialInputAddrCoins[i] = acc.GetCoins()
}
for i := 0; i < len(msg.Outputs); i++ {
acc := app.AccountMapper.GetAccount(ctx, msg.Outputs[i].Address)
initialOutputAddrCoins[i] = acc.GetCoins()
}
tx := mock.GenTx([]sdk.Msg{msg},
AccountNumbers,
SequenceNumbers,
privkeys...)
res := app.Deliver(tx)
if !res.IsOK() {
// TODO: Do this in a more 'canonical' way
fmt.Println(res)
fmt.Println(log)
t.FailNow()
}
for i := 0; i < len(msg.Inputs); i++ {
terminalInputCoins := app.AccountMapper.GetAccount(ctx, msg.Inputs[i].Address).GetCoins()
require.Equal(t,
initialInputAddrCoins[i].Minus(msg.Inputs[i].Coins),
terminalInputCoins,
fmt.Sprintf("Input #%d had an incorrect amount of coins\n%s", i, log),
)
}
for i := 0; i < len(msg.Outputs); i++ {
terminalOutputCoins := app.AccountMapper.GetAccount(ctx, msg.Outputs[i].Address).GetCoins()
require.Equal(t,
initialOutputAddrCoins[i].Plus(msg.Outputs[i].Coins),
terminalOutputCoins,
fmt.Sprintf("Output #%d had an incorrect amount of coins\n%s", i, log),
)
}
}
func randPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) {
if !max.GT(sdk.OneInt()) {
return sdk.Int{}, errors.New("max too small")
}
max = max.Sub(sdk.OneInt())
return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil
}
+6 -8
View File
@@ -29,7 +29,7 @@ const (
// submit a proposal tx
func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "submitproposal",
Use: "submit-proposal",
Short: "Submit a proposal along with an initial deposit",
RunE: func(cmd *cobra.Command, args []string) error {
title := viper.GetString(flagTitle)
@@ -63,13 +63,13 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
// proposalID must be returned, and it is a part of response
ctx.PrintResponse = true
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block:%d. Hash:%s.Response:%+v \n", res.Height, res.Hash.String(), res.DeliverTx)
return nil
},
}
@@ -113,11 +113,10 @@ func GetCmdDeposit(cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
@@ -164,11 +163,10 @@ func GetCmdVote(cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
+3 -3
View File
@@ -82,7 +82,7 @@ func (msg MsgSubmitProposal) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// Implements Msg.
@@ -149,7 +149,7 @@ func (msg MsgDeposit) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// Implements Msg.
@@ -213,7 +213,7 @@ func (msg MsgVote) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// Implements Msg.
+2 -2
View File
@@ -109,7 +109,7 @@ key2 DC26002735D3AA9573707CFA6D77C12349E49868
## Transfer coins (addr1:chain1 -> addr2:chain2)
```console
> basecli transfer --name key1 --to $ADDR2 --amount 10mycoin --chain $ID2 --chain-id $ID1 --node $NODE1
> basecli transfer --from key1 --to $ADDR2 --amount 10mycoin --chain $ID2 --chain-id $ID1 --node $NODE1
Password to sign with 'key1':
Committed at block 1022. Hash: E16019DCC4AA08CA70AFCFBC96028ABCC51B6AD0
> basecli account $ADDR1 --node $NODE1
@@ -133,7 +133,7 @@ Committed at block 1022. Hash: E16019DCC4AA08CA70AFCFBC96028ABCC51B6AD0
## Relay IBC packets
```console
> basecli relay --name key2 --from-chain-id $ID1 --from-chain-node $NODE1 --to-chain-id $ID2 --to-chain-node $NODE2 --chain-id $ID2
> basecli relay --from key2 --from-chain-id $ID1 --from-chain-node $NODE1 --to-chain-id $ID2 --to-chain-node $NODE2 --chain-id $ID2
Password to sign with 'key2':
I[04-03|16:18:59.984] Detected IBC packet number=0
I[04-03|16:19:00.869] Relayed IBC packet number=0
+1 -4
View File
@@ -2,7 +2,6 @@ package cli
import (
"encoding/hex"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@@ -43,12 +42,10 @@ func IBCTransferCmd(cdc *wire.Codec) *cobra.Command {
}
// get password
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
+2 -2
View File
@@ -59,7 +59,7 @@ func (p IBCPacket) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// validator the ibc packey
@@ -131,5 +131,5 @@ func (msg IBCReceiveMsg) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
+77 -8
View File
@@ -1,6 +1,7 @@
package mock
import (
"math/rand"
"os"
bam "github.com/cosmos/cosmos-sdk/baseapp"
@@ -15,7 +16,9 @@ import (
const chainID = ""
// App extends an ABCI application.
// App extends an ABCI application, but with most of its parameters exported.
// They are exported for convenience in creating helper functions, as object
// capabilities aren't needed for testing.
type App struct {
*bam.BaseApp
Cdc *wire.Codec // Cdc is public since the codec is passed into the module anyways
@@ -26,7 +29,8 @@ type App struct {
AccountMapper auth.AccountMapper
FeeCollectionKeeper auth.FeeCollectionKeeper
GenesisAccounts []auth.Account
GenesisAccounts []auth.Account
TotalCoinsSupply sdk.Coins
}
// NewApp partially constructs a new app on the memstore for module and genesis
@@ -43,10 +47,11 @@ func NewApp() *App {
// Create your application object
app := &App{
BaseApp: bam.NewBaseApp("mock", cdc, logger, db),
Cdc: cdc,
KeyMain: sdk.NewKVStoreKey("main"),
KeyAccount: sdk.NewKVStoreKey("acc"),
BaseApp: bam.NewBaseApp("mock", cdc, logger, db),
Cdc: cdc,
KeyMain: sdk.NewKVStoreKey("main"),
KeyAccount: sdk.NewKVStoreKey("acc"),
TotalCoinsSupply: sdk.Coins{},
}
// Define the accountMapper
@@ -124,8 +129,8 @@ func SetGenesis(app *App, accs []auth.Account) {
func GenTx(msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKey) auth.StdTx {
// Make the transaction free
fee := auth.StdFee{
sdk.Coins{sdk.NewCoin("foocoin", 0)},
100000,
Amount: sdk.Coins{sdk.NewCoin("foocoin", 0)},
Gas: 100000,
}
sigs := make([]auth.StdSignature, len(priv))
@@ -148,6 +153,70 @@ func GenTx(msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKey)
return auth.NewStdTx(msgs, fee, sigs, memo)
}
// GeneratePrivKeys generates a total n Ed25519 private keys.
func GeneratePrivKeys(n int) (keys []crypto.PrivKey) {
// TODO: Randomize this between ed25519 and secp256k1
keys = make([]crypto.PrivKey, n, n)
for i := 0; i < n; i++ {
keys[i] = crypto.GenPrivKeyEd25519()
}
return
}
// GeneratePrivKeyAddressPairs generates a total of n private key, address
// pairs.
func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.Address) {
keys = make([]crypto.PrivKey, n, n)
addrs = make([]sdk.Address, n, n)
for i := 0; i < n; i++ {
keys[i] = crypto.GenPrivKeyEd25519()
addrs[i] = keys[i].PubKey().Address()
}
return
}
// RandomSetGenesis set genesis accounts with random coin values using the
// provided addresses and coin denominations.
func RandomSetGenesis(r *rand.Rand, app *App, addrs []sdk.Address, denoms []string) {
accts := make([]auth.Account, len(addrs), len(addrs))
randCoinIntervals := []BigInterval{
{sdk.NewIntWithDecimal(1, 0), sdk.NewIntWithDecimal(1, 1)},
{sdk.NewIntWithDecimal(1, 2), sdk.NewIntWithDecimal(1, 3)},
{sdk.NewIntWithDecimal(1, 40), sdk.NewIntWithDecimal(1, 50)},
}
for i := 0; i < len(accts); i++ {
coins := make([]sdk.Coin, len(denoms), len(denoms))
// generate a random coin for each denomination
for j := 0; j < len(denoms); j++ {
coins[j] = sdk.Coin{Denom: denoms[j],
Amount: RandFromBigInterval(r, randCoinIntervals),
}
}
app.TotalCoinsSupply = app.TotalCoinsSupply.Plus(coins)
baseAcc := auth.NewBaseAccountWithAddress(addrs[i])
(&baseAcc).SetCoins(coins)
accts[i] = &baseAcc
}
SetGenesis(app, accts)
}
// GetAllAccounts returns all accounts in the accountMapper.
func GetAllAccounts(mapper auth.AccountMapper, ctx sdk.Context) []auth.Account {
accounts := []auth.Account{}
appendAccount := func(acc auth.Account) (stop bool) {
accounts = append(accounts, acc)
return false
}
mapper.IterateAccounts(ctx, appendAccount)
return accounts
}
// GenSequenceOfTxs generates a set of signed transactions of messages, such
// that they differ only by having the sequence numbers incremented between
// every transaction.
+15
View File
@@ -0,0 +1,15 @@
/*
Package mock provides functions for creating applications for testing.
This module also features randomized testing, so that various modules can test
that their operations are interoperable.
The intended method of using this randomized testing framework is that every
module provides TestAndRunTx methods for each of its desired methods of fuzzing
its own txs, and it also provides the invariants that it assumes to be true.
You then pick and choose from these tx types and invariants. To pick and choose
these, you first build a mock app with the correct keepers. Then you call the
app.RandomizedTesting method with the set of desired txs, invariants, along
with the setups each module requires.
*/
package mock
+95
View File
@@ -0,0 +1,95 @@
package mock
import (
"fmt"
"math/big"
"math/rand"
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
)
// RandomizedTesting tests application by sending random messages.
func (app *App) RandomizedTesting(
t *testing.T, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numKeys int, numBlocks int, blockSize int,
) {
time := time.Now().UnixNano()
app.RandomizedTestingFromSeed(t, time, ops, setups, invariants, numKeys, numBlocks, blockSize)
}
// RandomizedTestingFromSeed tests an application by running the provided
// operations, testing the provided invariants, but using the provided seed.
func (app *App) RandomizedTestingFromSeed(
t *testing.T, seed int64, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numKeys int, numBlocks int, blockSize int,
) {
log := fmt.Sprintf("Starting SingleModuleTest with randomness created with seed %d", int(seed))
keys, addrs := GeneratePrivKeyAddressPairs(numKeys)
r := rand.New(rand.NewSource(seed))
for i := 0; i < len(setups); i++ {
setups[i](r, keys)
}
RandomSetGenesis(r, app, addrs, []string{"foocoin"})
header := abci.Header{Height: 0}
for i := 0; i < numBlocks; i++ {
app.BeginBlock(abci.RequestBeginBlock{})
// Make sure invariants hold at beginning of block and when nothing was
// done.
app.assertAllInvariants(t, invariants, log)
ctx := app.NewContext(false, header)
// TODO: Add modes to simulate "no load", "medium load", and
// "high load" blocks.
for j := 0; j < blockSize; j++ {
logUpdate, err := ops[r.Intn(len(ops))](t, r, app, ctx, keys, log)
log += "\n" + logUpdate
require.Nil(t, err, log)
app.assertAllInvariants(t, invariants, log)
}
app.EndBlock(abci.RequestEndBlock{})
header.Height++
}
}
func (app *App) assertAllInvariants(t *testing.T, tests []Invariant, log string) {
for i := 0; i < len(tests); i++ {
tests[i](t, app, log)
}
}
// BigInterval is a representation of the interval [lo, hi), where
// lo and hi are both of type sdk.Int
type BigInterval struct {
lo sdk.Int
hi sdk.Int
}
// RandFromBigInterval chooses an interval uniformly from the provided list of
// BigIntervals, and then chooses an element from an interval uniformly at random.
func RandFromBigInterval(r *rand.Rand, intervals []BigInterval) sdk.Int {
if len(intervals) == 0 {
return sdk.ZeroInt()
}
interval := intervals[r.Intn(len(intervals))]
lo := interval.lo
hi := interval.hi
diff := hi.Sub(lo)
result := sdk.NewIntFromBigInt(new(big.Int).Rand(r, diff.BigInt()))
result = result.Add(lo)
return result
}
+38
View File
@@ -0,0 +1,38 @@
package mock
import (
"math/rand"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/crypto"
)
type (
// TestAndRunTx produces a fuzzed transaction, and ensures the state
// transition was as expected. It returns a descriptive message "action"
// about what this fuzzed tx actually did, for ease of debugging.
TestAndRunTx func(
t *testing.T, r *rand.Rand, app *App, ctx sdk.Context,
privKeys []crypto.PrivKey, log string,
) (action string, err sdk.Error)
// RandSetup performs the random setup the mock module needs.
RandSetup func(r *rand.Rand, privKeys []crypto.PrivKey)
// An Invariant is a function which tests a particular invariant.
// If the invariant has been broken, the function should halt the
// test and output the log.
Invariant func(t *testing.T, app *App, log string)
)
// PeriodicInvariant returns an Invariant function closure that asserts
// a given invariant if the mock application's last block modulo the given
// period is congruent to the given offset.
func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant {
return func(t *testing.T, app *App, log string) {
if int(app.LastBlockHeight())%period == offset {
invariant(t, app, log)
}
}
}
+1 -5
View File
@@ -1,8 +1,6 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/context"
@@ -29,12 +27,10 @@ func GetCmdUnrevoke(cdc *wire.Codec) *cobra.Command {
msg := slashing.NewMsgUnrevoke(validatorAddr)
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
+1 -1
View File
@@ -38,7 +38,7 @@ func (msg MsgUnrevoke) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check
+3 -2
View File
@@ -31,8 +31,9 @@ func GetCmdQueryValidator(storeName string, cdc *wire.Codec) *cobra.Command {
res, err := ctx.QueryStore(key, storeName)
if err != nil {
return err
} else if len(res) == 0 {
return fmt.Errorf("No validator found with address %s", args[0])
}
validator := types.MustUnmarshalValidator(cdc, addr, res)
switch viper.Get(cli.OutputFlag) {
@@ -118,7 +119,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
return err
}
delAddr, err := sdk.GetValAddressHex(viper.GetString(FlagAddressDelegator))
delAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
if err != nil {
return err
}
+7 -15
View File
@@ -53,12 +53,10 @@ func GetCmdCreateValidator(cdc *wire.Codec) *cobra.Command {
msg := stake.NewMsgCreateValidator(validatorAddr, pk, amount, description)
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
@@ -92,12 +90,11 @@ func GetCmdEditValidator(cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
@@ -132,12 +129,11 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
@@ -197,12 +193,11 @@ func GetCmdBeginRedelegate(storeName string, cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
@@ -282,12 +277,11 @@ func GetCmdCompleteRedelegate(cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
@@ -340,12 +334,11 @@ func GetCmdBeginUnbonding(storeName string, cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
@@ -377,12 +370,11 @@ func GetCmdCompleteUnbonding(cdc *wire.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
+7
View File
@@ -261,6 +261,13 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
if updatedVal.Owner != nil { // updates to validator occurred to be updated
validator = updatedVal
}
// if decreased in power but still bonded, update Tendermint validator
// (if updatedVal is set, the validator has changed bonding status)
stillBonded := oldFound && oldValidator.Status() == sdk.Bonded && updatedVal.Owner == nil
if stillBonded && oldValidator.PoolShares.Bonded().GT(validator.PoolShares.Bonded()) {
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
}
return validator
}
+40
View File
@@ -701,3 +701,43 @@ func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) {
require.Equal(t, validators[0].ABCIValidatorZero(), updates[0])
require.Equal(t, validators[2].ABCIValidator(), updates[1])
}
func TestGetTendermintUpdatesPowerDecrease(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{100, 100}
var validators [2]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
// check initial power
require.Equal(t, sdk.NewRat(100).RoundInt64(), validators[0].GetPower().RoundInt64())
require.Equal(t, sdk.NewRat(100).RoundInt64(), validators[1].GetPower().RoundInt64())
// test multiple value change
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
pool := keeper.GetPool(ctx)
validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewRat(20))
validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewRat(30))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
// power has changed
require.Equal(t, sdk.NewRat(80).RoundInt64(), validators[0].GetPower().RoundInt64())
require.Equal(t, sdk.NewRat(70).RoundInt64(), validators[1].GetPower().RoundInt64())
// Tendermint updates should reflect power change
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
}
+7 -7
View File
@@ -63,7 +63,7 @@ func (msg MsgCreateValidator) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check
@@ -114,7 +114,7 @@ func (msg MsgEditValidator) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check
@@ -166,7 +166,7 @@ func (msg MsgDelegate) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check
@@ -226,7 +226,7 @@ func (msg MsgBeginRedelegate) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check
@@ -286,7 +286,7 @@ func (msg MsgCompleteRedelegate) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check
@@ -338,7 +338,7 @@ func (msg MsgBeginUnbonding) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check
@@ -387,7 +387,7 @@ func (msg MsgCompleteUnbonding) GetSignBytes() []byte {
if err != nil {
panic(err)
}
return b
return sdk.MustSortJSON(b)
}
// quick validity check