Merge PR #3807: Custom MsgMultiSend Handler (x/bank fork)
This commit is contained in:
parent
805e7fbfc2
commit
bf7cbbbdf8
|
@ -19,6 +19,10 @@
|
|||
|
||||
### Gaia
|
||||
|
||||
* [\#3787] Fork the `x/bank` module into the Gaia application with only a
|
||||
modified message handler, where the modified message handler behaves the same as
|
||||
the standard `x/bank` message handler except for `MsgMultiSend` that must burn
|
||||
exactly 9 atoms and transfer 1 atom, and `MsgSend` is disabled.
|
||||
* [\#3789] Update validator creation flow:
|
||||
* Remove `NewMsgCreateValidatorOnBehalfOf` and corresponding business logic
|
||||
* Ensure the validator address equals the delegator address during
|
||||
|
@ -32,6 +36,7 @@ tags.
|
|||
* [\#3751] Disable (temporarily) support for ED25519 account key pairs.
|
||||
|
||||
### Tendermint
|
||||
|
||||
* [\#3804] Update to Tendermint `v0.31.0-dev0`
|
||||
|
||||
<!--------------------------------- FEATURES --------------------------------->
|
||||
|
|
|
@ -11,6 +11,9 @@ import (
|
|||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
// TODO: Remove once transfers are enabled.
|
||||
gaiabank "github.com/cosmos/cosmos-sdk/cmd/gaia/app/x/bank"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -149,8 +152,10 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
|
|||
)
|
||||
|
||||
// register message routes
|
||||
//
|
||||
// TODO: Use standard bank router once transfers are enabled.
|
||||
app.Router().
|
||||
AddRoute(bank.RouterKey, bank.NewHandler(app.bankKeeper)).
|
||||
AddRoute(bank.RouterKey, gaiabank.NewHandler(app.bankKeeper)).
|
||||
AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)).
|
||||
AddRoute(distr.RouterKey, distr.NewHandler(app.distrKeeper)).
|
||||
AddRoute(slashing.RouterKey, slashing.NewHandler(app.slashingKeeper)).
|
||||
|
|
|
@ -11,13 +11,12 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"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"
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
// Package bank contains a forked version of the bank module. It only contains
|
||||
// a modified message handler to support a very limited form of transfers during
|
||||
// mainnet launch -- MsgMultiSend messages.
|
||||
//
|
||||
// NOTE: This fork should be removed entirely once transfers are enabled and
|
||||
// the Gaia router should be reset to using the original bank module handler.
|
||||
package bank
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
var (
|
||||
uatomDenom = "uatom"
|
||||
atomsToUatoms = int64(1000000)
|
||||
|
||||
// BurnedCoinsAccAddr represents the burn account address used for
|
||||
// MsgMultiSend message during the period for which transfers are disabled.
|
||||
// Its Bech32 address is cosmos1x4p90uuy63fqzsheamn48vq88q3eusykf0a69v.
|
||||
BurnedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("bankBurnedCoins")))
|
||||
)
|
||||
|
||||
// NewHandler returns a handler for "bank" type messages.
|
||||
func NewHandler(k bank.Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case bank.MsgSend:
|
||||
return handleMsgSend(ctx, k, msg)
|
||||
|
||||
case bank.MsgMultiSend:
|
||||
return handleMsgMultiSend(ctx, k, msg)
|
||||
|
||||
default:
|
||||
errMsg := "Unrecognized bank Msg type: %s" + msg.Type()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleMsgSend implements a MsgSend message handler. It operates no differently
|
||||
// than the standard bank module MsgSend message handler in that it transfers
|
||||
// an amount from one account to another under the condition of transfers being
|
||||
// enabled.
|
||||
func handleMsgSend(ctx sdk.Context, k bank.Keeper, msg bank.MsgSend) sdk.Result {
|
||||
// No need to modify handleMsgSend as the forked module requires no changes,
|
||||
// so we can just call the standard bank modules handleMsgSend since we know
|
||||
// the message is of type MsgSend.
|
||||
return bank.NewHandler(k)(ctx, msg)
|
||||
}
|
||||
|
||||
// handleMsgMultiSend implements a modified forked version of a MsgMultiSend
|
||||
// message handler. If transfers are disabled, a modified version of MsgMultiSend
|
||||
// is allowed where there must be a single input and only two outputs. The first
|
||||
// of the two outputs must be to a specific burn address defined by
|
||||
// burnedCoinsAccAddr. In addition, the output amounts must be of 9atom and
|
||||
// 1uatom respectively.
|
||||
func handleMsgMultiSend(ctx sdk.Context, k bank.Keeper, msg bank.MsgMultiSend) sdk.Result {
|
||||
// NOTE: totalIn == totalOut should already have been checked
|
||||
if !k.GetSendEnabled(ctx) {
|
||||
if !validateMultiSendTransfersDisabled(msg) {
|
||||
return bank.ErrSendDisabled(k.Codespace()).Result()
|
||||
}
|
||||
}
|
||||
|
||||
tags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func validateMultiSendTransfersDisabled(msg bank.MsgMultiSend) bool {
|
||||
nineAtoms := sdk.Coins{sdk.NewInt64Coin(uatomDenom, 9*atomsToUatoms)}
|
||||
oneAtom := sdk.Coins{sdk.NewInt64Coin(uatomDenom, 1*atomsToUatoms)}
|
||||
|
||||
if len(msg.Inputs) != 1 {
|
||||
return false
|
||||
}
|
||||
if len(msg.Outputs) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if !msg.Outputs[0].Address.Equals(BurnedCoinsAccAddr) {
|
||||
return false
|
||||
}
|
||||
if !msg.Outputs[0].Coins.IsEqual(nineAtoms) {
|
||||
return false
|
||||
}
|
||||
if !msg.Outputs[1].Coins.IsEqual(oneAtom) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
package bank
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
)
|
||||
|
||||
var (
|
||||
addrs = []sdk.AccAddress{
|
||||
sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()),
|
||||
sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()),
|
||||
sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()),
|
||||
}
|
||||
|
||||
initAmt = sdk.NewInt(atomsToUatoms * 100)
|
||||
)
|
||||
|
||||
type testInput struct {
|
||||
ctx sdk.Context
|
||||
accKeeper auth.AccountKeeper
|
||||
bankKeeper bank.Keeper
|
||||
}
|
||||
|
||||
func newTestCodec() *codec.Codec {
|
||||
cdc := codec.New()
|
||||
|
||||
bank.RegisterCodec(cdc)
|
||||
auth.RegisterCodec(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
codec.RegisterCrypto(cdc)
|
||||
|
||||
return cdc
|
||||
}
|
||||
|
||||
func createTestInput(t *testing.T) testInput {
|
||||
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
|
||||
keyParams := sdk.NewKVStoreKey(params.StoreKey)
|
||||
tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
|
||||
|
||||
cdc := newTestCodec()
|
||||
db := dbm.NewMemDB()
|
||||
ms := store.NewCommitMultiStore(db)
|
||||
ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger())
|
||||
|
||||
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
|
||||
ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db)
|
||||
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
|
||||
|
||||
require.NoError(t, ms.LoadLatestVersion())
|
||||
|
||||
paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams)
|
||||
accKeeper := auth.NewAccountKeeper(
|
||||
cdc,
|
||||
keyAcc,
|
||||
paramsKeeper.Subspace(auth.DefaultParamspace),
|
||||
auth.ProtoBaseAccount,
|
||||
)
|
||||
|
||||
bankKeeper := bank.NewBaseKeeper(
|
||||
accKeeper,
|
||||
paramsKeeper.Subspace(bank.DefaultParamspace),
|
||||
bank.DefaultCodespace,
|
||||
)
|
||||
|
||||
for _, addr := range addrs {
|
||||
_, _, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
return testInput{ctx, accKeeper, bankKeeper}
|
||||
}
|
||||
|
||||
func TestHandlerMsgSendTransfersDisabled(t *testing.T) {
|
||||
input := createTestInput(t)
|
||||
input.bankKeeper.SetSendEnabled(input.ctx, false)
|
||||
|
||||
handler := NewHandler(input.bankKeeper)
|
||||
amt := sdk.NewInt(atomsToUatoms * 5)
|
||||
msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, amt)})
|
||||
|
||||
res := handler(input.ctx, msg)
|
||||
require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log)
|
||||
|
||||
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
|
||||
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
|
||||
|
||||
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
|
||||
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
|
||||
}
|
||||
|
||||
func TestHandlerMsgSendTransfersEnabled(t *testing.T) {
|
||||
input := createTestInput(t)
|
||||
input.bankKeeper.SetSendEnabled(input.ctx, true)
|
||||
|
||||
handler := NewHandler(input.bankKeeper)
|
||||
amt := sdk.NewInt(atomsToUatoms * 5)
|
||||
msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, amt)})
|
||||
|
||||
res := handler(input.ctx, msg)
|
||||
require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log)
|
||||
|
||||
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
|
||||
balance := initAmt.Sub(amt)
|
||||
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
|
||||
|
||||
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
|
||||
balance = initAmt.Add(amt)
|
||||
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
|
||||
}
|
||||
|
||||
func TestHandlerMsgMultiSendTransfersDisabledBurn(t *testing.T) {
|
||||
input := createTestInput(t)
|
||||
input.bankKeeper.SetSendEnabled(input.ctx, false)
|
||||
|
||||
handler := NewHandler(input.bankKeeper)
|
||||
totalAmt := sdk.NewInt(10 * atomsToUatoms)
|
||||
burnAmt := sdk.NewInt(9 * atomsToUatoms)
|
||||
transAmt := sdk.NewInt(1 * atomsToUatoms)
|
||||
msg := bank.NewMsgMultiSend(
|
||||
[]bank.Input{
|
||||
bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}),
|
||||
},
|
||||
[]bank.Output{
|
||||
bank.NewOutput(BurnedCoinsAccAddr, sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)}),
|
||||
bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, transAmt)}),
|
||||
},
|
||||
)
|
||||
|
||||
res := handler(input.ctx, msg)
|
||||
require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log)
|
||||
|
||||
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
|
||||
balance := initAmt.Sub(totalAmt)
|
||||
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
|
||||
|
||||
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
|
||||
balance = initAmt.Add(transAmt)
|
||||
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
|
||||
|
||||
burn := input.accKeeper.GetAccount(input.ctx, BurnedCoinsAccAddr)
|
||||
require.Equal(t, burn.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)})
|
||||
}
|
||||
|
||||
func TestHandlerMsgMultiSendTransfersDisabledInvalidBurn(t *testing.T) {
|
||||
input := createTestInput(t)
|
||||
input.bankKeeper.SetSendEnabled(input.ctx, false)
|
||||
|
||||
handler := NewHandler(input.bankKeeper)
|
||||
totalAmt := sdk.NewInt(15 * atomsToUatoms)
|
||||
burnAmt := sdk.NewInt(10 * atomsToUatoms)
|
||||
transAmt := sdk.NewInt(5 * atomsToUatoms)
|
||||
msg := bank.NewMsgMultiSend(
|
||||
[]bank.Input{
|
||||
bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}),
|
||||
},
|
||||
[]bank.Output{
|
||||
bank.NewOutput(BurnedCoinsAccAddr, sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)}),
|
||||
bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, transAmt)}),
|
||||
},
|
||||
)
|
||||
|
||||
res := handler(input.ctx, msg)
|
||||
require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log)
|
||||
|
||||
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
|
||||
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
|
||||
|
||||
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
|
||||
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
|
||||
}
|
||||
|
||||
func TestHandlerMsgMultiSendTransfersEnabled(t *testing.T) {
|
||||
input := createTestInput(t)
|
||||
input.bankKeeper.SetSendEnabled(input.ctx, true)
|
||||
|
||||
handler := NewHandler(input.bankKeeper)
|
||||
totalAmt := sdk.NewInt(10 * atomsToUatoms)
|
||||
outAmt := sdk.NewInt(5 * atomsToUatoms)
|
||||
msg := bank.NewMsgMultiSend(
|
||||
[]bank.Input{
|
||||
bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}),
|
||||
},
|
||||
[]bank.Output{
|
||||
bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, outAmt)}),
|
||||
bank.NewOutput(addrs[2], sdk.Coins{sdk.NewCoin(uatomDenom, outAmt)}),
|
||||
},
|
||||
)
|
||||
|
||||
res := handler(input.ctx, msg)
|
||||
require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log)
|
||||
|
||||
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
|
||||
balance := initAmt.Sub(totalAmt)
|
||||
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
|
||||
|
||||
out1 := input.accKeeper.GetAccount(input.ctx, addrs[1])
|
||||
balance = initAmt.Add(outAmt)
|
||||
require.Equal(t, out1.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
|
||||
|
||||
out2 := input.accKeeper.GetAccount(input.ctx, addrs[2])
|
||||
balance = initAmt.Add(outAmt)
|
||||
require.Equal(t, out2.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
|
||||
}
|
Loading…
Reference in New Issue