Skip to content

Commit

Permalink
Move allow list logic to msg server (#550)
Browse files Browse the repository at this point in the history
## Describe your changes and provide context
Move allow list logic to msg server

## Testing performed to validate your change

- Unit tests
- Local chain functional tests
  • Loading branch information
dssei authored Nov 4, 2024
1 parent 71ec704 commit 4214e66
Show file tree
Hide file tree
Showing 4 changed files with 514 additions and 212 deletions.
374 changes: 374 additions & 0 deletions x/bank/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bank_test

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -121,6 +122,142 @@ func TestSendNotEnoughBalance(t *testing.T) {
require.Equal(t, res2.GetSequence(), origSeq+1)
}

func TestSendReceiverNotInAllowList(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: addr1.String(),
}

genAccs := []authtypes.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
testDenom := "testDenom"
factoryDenom := fmt.Sprintf("factory/%s/%s", addr1.String(), testDenom)

require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin(factoryDenom, 100))))
app.BankKeeper.SetDenomAllowList(ctx, factoryDenom,
types.AllowList{Addresses: []string{addr1.String()}})

app.Commit(context.Background())

res1 := app.AccountKeeper.GetAccount(ctx, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*authtypes.BaseAccount))

origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()

sendMsg := types.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 10)})
header := tmproto.Header{Height: app.LastBlockHeight() + 1}
txGen := simapp.MakeTestEncodingConfig().TxConfig
_, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("%s is not allowed to receive funds", addr2))

simapp.CheckBalance(t, app, addr1, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 100)})
}

func TestSendSenderAndReceiverInAllowList(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: addr1.String(),
}

genAccs := []authtypes.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
testDenom := "testDenom"
factoryDenom := fmt.Sprintf("factory/%s/%s", addr1.String(), testDenom)

require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin(factoryDenom, 100))))
app.BankKeeper.SetDenomAllowList(ctx, factoryDenom,
types.AllowList{Addresses: []string{addr1.String(), addr2.String()}})

app.Commit(context.Background())

res1 := app.AccountKeeper.GetAccount(ctx, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*authtypes.BaseAccount))

origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()

sendMsg := types.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 10)})
header := tmproto.Header{Height: app.LastBlockHeight() + 1}
txGen := simapp.MakeTestEncodingConfig().TxConfig
_, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq}, true, true, priv1)
require.NoError(t, err)

simapp.CheckBalance(t, app, addr1, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 90)})
simapp.CheckBalance(t, app, addr2, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 10)})
}

func TestSendWithEmptyAllowList(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: addr1.String(),
}

genAccs := []authtypes.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
testDenom := "testDenom"
factoryDenom := fmt.Sprintf("factory/%s/%s", addr1.String(), testDenom)

require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin(factoryDenom, 100))))
app.BankKeeper.SetDenomAllowList(ctx, factoryDenom,
types.AllowList{Addresses: []string{}})

app.Commit(context.Background())

res1 := app.AccountKeeper.GetAccount(ctx, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*authtypes.BaseAccount))

origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()

sendMsg := types.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 10)})
header := tmproto.Header{Height: app.LastBlockHeight() + 1}
txGen := simapp.MakeTestEncodingConfig().TxConfig
_, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq}, true, true, priv1)
require.NoError(t, err)

simapp.CheckBalance(t, app, addr1, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 90)})
simapp.CheckBalance(t, app, addr2, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 10)})
}

func TestSendSenderNotInAllowList(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: addr1.String(),
}

genAccs := []authtypes.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
testDenom := "testDenom"
factoryDenom := fmt.Sprintf("factory/%s/%s", addr1.String(), testDenom)

require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin(factoryDenom, 100))))
app.BankKeeper.SetDenomAllowList(ctx, factoryDenom,
types.AllowList{Addresses: []string{addr2.String()}})

app.Commit(context.Background())

res1 := app.AccountKeeper.GetAccount(ctx, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*authtypes.BaseAccount))

origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()

sendMsg := types.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 10)})
header := tmproto.Header{Height: app.LastBlockHeight() + 1}
txGen := simapp.MakeTestEncodingConfig().TxConfig
_, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("%s is not allowed to send funds", addr1))

simapp.CheckBalance(t, app, addr1, sdk.Coins{sdk.NewInt64Coin(factoryDenom, 100)})
}

func TestMsgMultiSendWithAccounts(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: addr1.String(),
Expand Down Expand Up @@ -337,3 +474,240 @@ func TestMsgMultiSendDependent(t *testing.T) {
}
}
}

func TestMultiSendAllowList(t *testing.T) {
// CoinToAllowList defines a struct to map coins to their allow lists.
type CoinToAllowList struct {
fundAmount sdk.Coin
sendAmount sdk.Coin
allowList types.AllowList
}

type testCase struct {
name string
coinsToAllowList []CoinToAllowList
sender sdk.AccAddress
receiver sdk.AccAddress
accNums []uint64
accSeqs []uint64
privKeys []cryptotypes.PrivKey
expectedSenderBal sdk.Coins
expectedReceiverBal sdk.Coins
expectedError bool
expectedErrorMsg string
}

senderAcc := sdk.AccAddress(priv1.PubKey().Address())
receiverAcc := sdk.AccAddress(priv2.PubKey().Address())
testDenom := fmt.Sprintf("factory/%s/test", senderAcc.String())
testDenom1 := fmt.Sprintf("factory/%s/test1", senderAcc.String())
// Define test cases
testCases := []testCase{

{
name: "sender not allowed to send coins",
coinsToAllowList: []CoinToAllowList{
{
fundAmount: sdk.NewInt64Coin(testDenom, 100),
sendAmount: sdk.NewInt64Coin(testDenom, 20),
allowList: types.AllowList{
Addresses: []string{
receiverAcc.String(),
},
},
},
},
accNums: []uint64{0, 2},
accSeqs: []uint64{0, 0},
sender: senderAcc,
receiver: receiverAcc,
privKeys: []cryptotypes.PrivKey{priv1},
expectedSenderBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 100)),
expectedReceiverBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 0)),
expectedError: true,
expectedErrorMsg: fmt.Sprintf("%s is not allowed to send funds", senderAcc),
},
{
name: "receiver not allowed to receive coins",
coinsToAllowList: []CoinToAllowList{
{
fundAmount: sdk.NewInt64Coin(testDenom, 100),
sendAmount: sdk.NewInt64Coin(testDenom, 20),
allowList: types.AllowList{
Addresses: []string{
senderAcc.String(),
},
},
},
},
accNums: []uint64{0, 2},
accSeqs: []uint64{0, 0},
sender: senderAcc,
receiver: receiverAcc,
privKeys: []cryptotypes.PrivKey{priv1},
expectedSenderBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 100)),
expectedReceiverBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 0)),
expectedError: true,
expectedErrorMsg: fmt.Sprintf("%s is not allowed to receive funds", receiverAcc),
},
{
name: "allow list is empty (no restrictions)",
coinsToAllowList: []CoinToAllowList{
{
fundAmount: sdk.NewInt64Coin(testDenom, 100),
sendAmount: sdk.NewInt64Coin(testDenom, 20),
allowList: types.AllowList{
Addresses: []string{},
},
},
},
accNums: []uint64{0, 2},
accSeqs: []uint64{0, 0},
sender: senderAcc,
receiver: receiverAcc,
expectedSenderBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 80)),
expectedReceiverBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 20)),
privKeys: []cryptotypes.PrivKey{priv1},
expectedError: false,
},
{
name: "both sender and receiver are allowed to send and receive coins",
coinsToAllowList: []CoinToAllowList{
{
fundAmount: sdk.NewInt64Coin(testDenom, 100),
sendAmount: sdk.NewInt64Coin(testDenom, 25),
allowList: types.AllowList{
Addresses: []string{
senderAcc.String(), receiverAcc.String(),
},
},
},
},
accNums: []uint64{0, 2},
accSeqs: []uint64{0, 0},
sender: senderAcc,
receiver: receiverAcc,
privKeys: []cryptotypes.PrivKey{priv1},
expectedSenderBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 75)),
expectedReceiverBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 25)),
expectedError: false,
},
{
name: "both are allowed for first coin, but only sender is allowed for second coin",
coinsToAllowList: []CoinToAllowList{
{
fundAmount: sdk.NewInt64Coin(testDenom, 100),
sendAmount: sdk.NewInt64Coin(testDenom, 20),
allowList: types.AllowList{
Addresses: []string{
senderAcc.String(), receiverAcc.String(),
},
},
},
{
fundAmount: sdk.NewInt64Coin(testDenom1, 200),
sendAmount: sdk.NewInt64Coin(testDenom1, 20),
allowList: types.AllowList{
Addresses: []string{
senderAcc.String(),
},
},
},
},
accNums: []uint64{0, 2},
accSeqs: []uint64{0, 0},
sender: senderAcc,
receiver: receiverAcc,
privKeys: []cryptotypes.PrivKey{priv1},
expectedError: true,
expectedSenderBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 100), sdk.NewInt64Coin(testDenom1, 200)),
expectedReceiverBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 0), sdk.NewInt64Coin(testDenom1, 0)),
expectedErrorMsg: fmt.Sprintf("%s is not allowed to receive funds", receiverAcc),
},
{
name: "both sender and receiver are allowed to send and receive 2 coins",
coinsToAllowList: []CoinToAllowList{
{
fundAmount: sdk.NewInt64Coin(testDenom, 100),
sendAmount: sdk.NewInt64Coin(testDenom, 25),
allowList: types.AllowList{
Addresses: []string{
senderAcc.String(), receiverAcc.String(),
},
},
},
{
fundAmount: sdk.NewInt64Coin(testDenom1, 200),
sendAmount: sdk.NewInt64Coin(testDenom1, 50),
allowList: types.AllowList{
Addresses: []string{
senderAcc.String(), receiverAcc.String(),
},
},
},
},
accNums: []uint64{0, 2},
accSeqs: []uint64{0, 0},
sender: senderAcc,
receiver: receiverAcc,
expectedSenderBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 75), sdk.NewInt64Coin(testDenom1, 150)),
expectedReceiverBal: sdk.NewCoins(sdk.NewInt64Coin(testDenom, 25), sdk.NewInt64Coin(testDenom1, 50)),
privKeys: []cryptotypes.PrivKey{priv1},
expectedError: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Setup genesis accounts
sender := &authtypes.BaseAccount{
Address: tc.sender.String(),
}
receiver := &authtypes.BaseAccount{
Address: tc.receiver.String(),
}

genAccs := []authtypes.GenesisAccount{sender, receiver}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

msgs := make([]sdk.Msg, 0)

for _, coinToAllowList := range tc.coinsToAllowList {
app.BankKeeper.SetDenomAllowList(ctx, coinToAllowList.fundAmount.Denom, coinToAllowList.allowList)
require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, sender.GetAddress(), sdk.NewCoins(coinToAllowList.fundAmount)))
multiSendMsg := &types.MsgMultiSend{
Inputs: []types.Input{
types.NewInput(sender.GetAddress(), sdk.Coins{coinToAllowList.sendAmount}),
},
Outputs: []types.Output{
types.NewOutput(receiver.GetAddress(), sdk.Coins{coinToAllowList.sendAmount}),
},
}
msgs = append(msgs, multiSendMsg)
}

app.Commit(context.Background())

header := tmproto.Header{Height: app.LastBlockHeight() + 1}
txGen := simapp.MakeTestEncodingConfig().TxConfig
_, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, msgs, "", tc.accNums, tc.accSeqs, !tc.expectedError, !tc.expectedError, tc.privKeys...)

if tc.expectedError {
require.Error(t, err, "expected an error but got none")
require.Contains(t, err.Error(), tc.expectedErrorMsg)
} else {
require.NoError(t, err, "did not expect an error but got one")
}

// Validate balances
// Sender's balance after send should be as expected
senderBal := app.BankKeeper.GetAllBalances(ctx, tc.sender)
require.Equal(t, tc.expectedSenderBal, senderBal, "sender balance mismatch")

// Receiver's balance after receive should be as expected
receiverBal := app.BankKeeper.GetAllBalances(ctx, tc.receiver)
require.Equal(t, tc.expectedReceiverBal, receiverBal, "receiver balance mismatch")
})
}
}
Loading

0 comments on commit 4214e66

Please sign in to comment.