From 986d47186728b018c075704fe0ca294eb5a62d0e Mon Sep 17 00:00:00 2001 From: rickyyangz Date: Mon, 13 Apr 2020 16:47:49 +0800 Subject: [PATCH 1/2] fix GetSigners --- x/auth/stdtx.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index ee252775c..3f974857e 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -43,11 +43,6 @@ func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs } // in the order they appear in tx.GetMsgs(). // Duplicate addresses will be omitted. func (tx StdTx) GetSigners() []sdk.AccAddress { - // shortcut for most cases - if len(tx.GetMsgs()) == 1 { - return tx.GetMsgs()[0].GetSigners() - } - seen := map[string]bool{} var signers []sdk.AccAddress for _, msg := range tx.GetMsgs() { From 50b41a5428d0080bdedbcd0ca65706fbad26fc05 Mon Sep 17 00:00:00 2001 From: erhenglu <42333959+erhenglu@users.noreply.github.com> Date: Mon, 1 Jun 2020 20:22:16 +0800 Subject: [PATCH 2/2] validate minitoken send (#219) * validate minitoken send --- client/lcd/test_helpers.go | 2 +- go.mod | 2 +- go.sum | 4 +- types/upgrade.go | 1 + x/bank/handler.go | 9 +++ x/bank/handler_test.go | 124 ++++++++++++++++++++++++++++++++++++ x/bank/keeper.go | 5 ++ x/bank/keeper_test.go | 2 +- x/bank/mini_token_helper.go | 74 +++++++++++++++++++++ 9 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 x/bank/handler_test.go create mode 100644 x/bank/mini_token_helper.go diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 7aa6960a2..b4277f2a8 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -338,7 +338,7 @@ func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec) (net.Liste return nil, err } - go tmrpc.StartHTTPServer(listener, createHandler(cdc), logger, &tmrpc.Config{}) + go tmrpc.StartHTTPServer(listener, createHandler(cdc), logger, tmrpc.DefaultConfig()) return listener, nil } diff --git a/go.mod b/go.mod index 749ce329b..eb9dc1777 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( replace ( github.com/tendermint/go-amino => github.com/binance-chain/bnc-go-amino v0.14.1-binance.2 - github.com/tendermint/iavl => github.com/binance-chain/bnc-tendermint-iavl v0.12.0-binance.3 + github.com/tendermint/iavl => github.com/binance-chain/bnc-tendermint-iavl v0.12.0-binance.4 github.com/tendermint/tendermint => github.com/binance-chain/bnc-tendermint v0.32.3-binance.1 github.com/zondax/ledger-cosmos-go => github.com/binance-chain/ledger-cosmos-go v0.9.9-binance.3 golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20190823143015-45b1026d81ae diff --git a/go.sum b/go.sum index dc769ca52..b54699b14 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/binance-chain/bnc-go-amino v0.14.1-binance.2 h1:XcbcfisVItk92UKoGbtNT github.com/binance-chain/bnc-go-amino v0.14.1-binance.2/go.mod h1:yaElUUxWtv/TC/ldGtlKAvS1vKwokxgJ1d97I+6is80= github.com/binance-chain/bnc-tendermint v0.32.3-binance.1 h1:LDGvORYLSwsTEQM0W7yrbdgjrAZxQDe18WUTLNuFOEc= github.com/binance-chain/bnc-tendermint v0.32.3-binance.1/go.mod h1:kN5dNxE8voFtDqx2HjbM8sBIH5cUuMtLg0XEHjqzUiY= -github.com/binance-chain/bnc-tendermint-iavl v0.12.0-binance.3 h1:OyTJet9aGz+c2WKpTf5cAvJNiQeqVFYP4AV9cPpro2M= -github.com/binance-chain/bnc-tendermint-iavl v0.12.0-binance.3/go.mod h1:Zmh8GRdNJB8DULIOBar3JCZp6tSpcvM1NGKfE9U2EzA= +github.com/binance-chain/bnc-tendermint-iavl v0.12.0-binance.4 h1:BhaV2iiGWfRC6iB8HHOYJeUDwtQMB2pUA4ah+KCbBhI= +github.com/binance-chain/bnc-tendermint-iavl v0.12.0-binance.4/go.mod h1:Zmh8GRdNJB8DULIOBar3JCZp6tSpcvM1NGKfE9U2EzA= github.com/binance-chain/ledger-cosmos-go v0.9.9-binance.3 h1:FFpFbkzlP2HUyxQCm0eoU6mkfgMNynfqZRbeWqlaLdQ= github.com/binance-chain/ledger-cosmos-go v0.9.9-binance.3/go.mod h1:TULULYTvPuWBxFIZFy6KjJaxJzbHeUderYNB1YhD6N0= github.com/binance-chain/tss v0.1.2 h1:AyTedSG5HG/WAvM9PDPWjTXQ+dvNdHg3x1c+1a584PQ= diff --git a/types/upgrade.go b/types/upgrade.go index 5857a4c1f..21d05dd68 100644 --- a/types/upgrade.go +++ b/types/upgrade.go @@ -5,6 +5,7 @@ import "fmt" var UpgradeMgr = NewUpgradeManager(UpgradeConfig{}) const FixSignBytesOverflow = "FixSignBytesOverflow" // fix json unmarshal overflow when build SignBytes +const BEP8 = "BEP8" // Mini-BEP2 token var MainNetConfig = UpgradeConfig{ HeightMap: map[string]int64{}, diff --git a/x/bank/handler.go b/x/bank/handler.go index 2e9306292..8454f45d7 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -30,6 +30,15 @@ func handleMsgSend(ctx sdk.Context, k Keeper, msg MsgSend) sdk.Result { return err.Result() } } + + if sdk.IsUpgrade(sdk.BEP8) { + am := k.GetAccountKeeper() + for _, in := range msg.Inputs { + if err := checkAndValidateMiniTokenCoins(ctx, am, in.Address, in.Coins); err != nil { + return err.Result() + } + } + } // NOTE: totalIn == totalOut should already have been checked tags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) if err != nil { diff --git a/x/bank/handler_test.go b/x/bank/handler_test.go new file mode 100644 index 000000000..3925dce1b --- /dev/null +++ b/x/bank/handler_test.go @@ -0,0 +1,124 @@ +package bank + +import ( + "github.com/stretchr/testify/require" + "testing" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" +) + +func setup() (sdk.Context, sdk.Handler, BaseKeeper, auth.AccountKeeper) { + ms, authKey := setupMultiStore() + + cdc := codec.New() + auth.RegisterBaseAccount(cdc) + accountCache := getAccountCache(cdc, ms, authKey) + + ctx := sdk.NewContext(ms, abci.Header{}, sdk.RunTxModeDeliver, log.NewNopLogger()).WithAccountCache(accountCache) + accountKeeper := auth.NewAccountKeeper(cdc, authKey, auth.ProtoBaseAccount) + bankKeeper := NewBaseKeeper(accountKeeper) + handler := NewHandler(bankKeeper) + sdk.UpgradeMgr.AddUpgradeHeight(sdk.BEP8, int64(-1)) + return ctx, handler, bankKeeper, accountKeeper +} + +func TestHandleSendToken(t *testing.T) { + ctx, handler, bankKeeper, accountKeeper := setup() + + ctx = ctx.WithValue(baseapp.TxHashKey, "000") + addr := sdk.AccAddress([]byte("addr1")) + addr2 := sdk.AccAddress([]byte("addr2")) + acc := accountKeeper.NewAccountWithAddress(ctx, addr) + acc2 := accountKeeper.NewAccountWithAddress(ctx, addr2) + + accountKeeper.SetAccount(ctx, acc) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + + //transfer BEP2 token Successfully + bankKeeper.SetCoins(ctx, addr, sdk.Coins{sdk.NewCoin("NNB-000", 100)}) + + msg := createSendMsg(acc.GetAddress(), acc2.GetAddress(), sdk.Coins{sdk.NewCoin("NNB-000", 60)}) + sdkResult := handler(ctx, msg) + require.Equal(t, true, sdkResult.Code.IsOK()) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{sdk.NewCoin("NNB-000", 40)})) + require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{sdk.NewCoin("NNB-000", 60)})) +} + +func TestHandleSendMiniToken(t *testing.T) { + ctx, handler, bankKeeper, accountKeeper := setup() + + ctx = ctx.WithValue(baseapp.TxHashKey, "000") + addr := sdk.AccAddress([]byte("addr1")) + addr2 := sdk.AccAddress([]byte("addr2")) + addr3 := sdk.AccAddress([]byte("addr3")) + acc := accountKeeper.NewAccountWithAddress(ctx, addr) + acc2 := accountKeeper.NewAccountWithAddress(ctx, addr2) + + accountKeeper.SetAccount(ctx, acc) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + + //Transfer Mini token + //Fail to Transfer with value < 1e8 + MiniTokenFoo := "foocoin-000M" + bankKeeper.SetCoins(ctx, addr, sdk.Coins{sdk.NewCoin(MiniTokenFoo, 10e8)}) + msg := createSendMsg(acc.GetAddress(), acc2.GetAddress(), sdk.Coins{sdk.NewCoin(MiniTokenFoo, 2)}) + sdkResult := handler(ctx, msg) + require.Equal(t, false, sdkResult.Code.IsOK()) + require.Contains(t, sdkResult.Log, "transfer amount is too small") + + //Success with amount >= 1e8 + msg = createSendMsg(acc.GetAddress(), acc2.GetAddress(), sdk.Coins{sdk.NewCoin(MiniTokenFoo, 1e8)}) + sdkResult = handler(ctx, msg) + require.Equal(t, true, sdkResult.Code.IsOK()) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{sdk.NewCoin(MiniTokenFoo, 9e8)})) + require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{sdk.NewCoin(MiniTokenFoo, 1e8)})) + + //Fail to Multisend + MiniTokenBar := "barcoin-000M" + bankKeeper.SetCoins(ctx, addr2, sdk.Coins{sdk.NewCoin(MiniTokenBar, 10), sdk.NewCoin(MiniTokenFoo, 1e8)}) + + inputs := []Input{ + NewInput(addr, sdk.Coins{sdk.NewCoin(MiniTokenFoo, 3e8)}), + NewInput(addr2, sdk.Coins{sdk.NewCoin(MiniTokenBar, 3), sdk.NewCoin(MiniTokenFoo, 1e8)}), + } + + outputs := []Output{ + NewOutput(addr, sdk.Coins{sdk.NewCoin(MiniTokenBar, 1)}), + NewOutput(addr3, sdk.Coins{sdk.NewCoin(MiniTokenBar, 2), sdk.NewCoin(MiniTokenFoo, 4e8)}), + } + msg = NewMsgSend(inputs, outputs) + sdkResult = handler(ctx, msg) + require.Equal(t, false, sdkResult.Code.IsOK()) + require.Contains(t, sdkResult.Log, "transfer amount is too small") + + // + //Success with all balance + inputs = []Input{ + NewInput(addr, sdk.Coins{sdk.NewCoin(MiniTokenFoo, 3e8)}), + NewInput(addr2, sdk.Coins{sdk.NewCoin(MiniTokenBar, 10), sdk.NewCoin(MiniTokenFoo, 1e8)}), + } + + outputs = []Output{ + NewOutput(addr, sdk.Coins{sdk.NewCoin(MiniTokenBar, 4)}), + NewOutput(addr3, sdk.Coins{sdk.NewCoin(MiniTokenBar, 6), sdk.NewCoin(MiniTokenFoo, 4e8)}), + } + msg = NewMsgSend(inputs, outputs) + sdkResult = handler(ctx, msg) + require.Equal(t, true, sdkResult.Code.IsOK()) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{sdk.NewCoin(MiniTokenBar, 4), sdk.NewCoin(MiniTokenFoo, 6e8)})) + require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{})) + require.True(t, bankKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{sdk.NewCoin(MiniTokenBar, 6), sdk.NewCoin(MiniTokenFoo, 4e8)})) +} + +func createSendMsg(from sdk.AccAddress, to sdk.AccAddress, coins sdk.Coins) sdk.Msg { + input := NewInput(from, coins) + output := NewOutput(to, coins) + msg := NewMsgSend([]Input{input}, []Output{output}) + return msg +} diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 2ba5d8721..6191b63d0 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -14,6 +14,7 @@ type Keeper interface { SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) + GetAccountKeeper() auth.AccountKeeper } var _ Keeper = (*BaseKeeper)(nil) @@ -52,6 +53,10 @@ func (keeper BaseKeeper) SubtractCoins( return subtractCoins(ctx, keeper.am, addr, amt) } +func (keeper BaseKeeper) GetAccountKeeper() auth.AccountKeeper { + return keeper.am +} + // AddCoins adds amt to the coins at the addr. func (keeper BaseKeeper) AddCoins( ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 49d9363bd..a44429dff 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -9,7 +9,7 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - codec "github.com/cosmos/cosmos-sdk/codec" + "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" diff --git a/x/bank/mini_token_helper.go b/x/bank/mini_token_helper.go new file mode 100644 index 000000000..f3914c720 --- /dev/null +++ b/x/bank/mini_token_helper.go @@ -0,0 +1,74 @@ +package bank + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" +) + +const ( + MiniTokenSymbolSuffixLen = 4 // probably enough. if it collides (unlikely) the issuer can just use another tx. + MiniTokenSymbolMSuffix = "M" + MiniTokenMinExecutionAmount int64 = 100000000 // 1 with 8 decimal digits +) + +func checkAndValidateMiniTokenCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, coins sdk.Coins) sdk.Error { + var err sdk.Error + for _, coin := range coins { + if isMiniTokenSymbol(coin.Denom) { + err = validateMiniTokenAmount(ctx, am, addr, coin) + } + if err != nil { + return err + } + } + return nil +} + +func isMiniTokenSymbol(symbol string) bool { + parts, err := splitSuffixedTokenSymbol(symbol) + if err != nil { + return false + } + suffixPart := parts[1] + + return len(suffixPart) == MiniTokenSymbolSuffixLen && strings.HasSuffix(suffixPart, MiniTokenSymbolMSuffix) +} + +func validateMiniTokenAmount(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, coin sdk.Coin) sdk.Error { + if MiniTokenMinExecutionAmount <= coin.Amount { + return nil + } + + coins := getCoins(ctx, am, addr) + balance := coins.AmountOf(coin.Denom) + if balance < coin.Amount { + return sdk.ErrInsufficientCoins("not enough token to send") + } + + useAllBalance := balance == coin.Amount + + if !useAllBalance { + return sdk.ErrInvalidCoins(fmt.Sprintf("transfer amount is too small, the min amount is %d or total account balance", + MiniTokenMinExecutionAmount)) + } + + return nil +} + +func splitSuffixedTokenSymbol(suffixed string) ([]string, error) { + + split := strings.SplitN(suffixed, "-", 2) + + if len(split) != 2 { + return nil, fmt.Errorf("suffixed token symbol must contain a hyphen ('-')") + } + + if strings.Contains(split[1], "-") { + return nil, fmt.Errorf("suffixed token symbol must contain just one hyphen ('-')") + } + + return split, nil +}