Merge remote-tracking branch 'origin/develop' into cwgoes/zero-power-should-not-be-bonded
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,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
@@ -38,7 +38,7 @@ func (msg MsgUnrevoke) GetSignBytes() []byte {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
return sdk.MustSortJSON(b)
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user