Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

validate minitoken send #222

Merged
merged 3 commits into from
Jun 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/lcd/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
1 change: 1 addition & 0 deletions types/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{},
Expand Down
5 changes: 0 additions & 5 deletions x/auth/stdtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
9 changes: 9 additions & 0 deletions x/bank/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
124 changes: 124 additions & 0 deletions x/bank/handler_test.go
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 5 additions & 0 deletions x/bank/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion x/bank/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
74 changes: 74 additions & 0 deletions x/bank/mini_token_helper.go
Original file line number Diff line number Diff line change
@@ -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
}