Skip to content
37 changes: 23 additions & 14 deletions aclmapping/bank/mappings.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol"
aclkeeper "github.com/cosmos/cosmos-sdk/x/accesscontrol/keeper"
acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
utils "github.com/sei-protocol/sei-chain/aclmapping/utils"
)
Expand All @@ -23,12 +24,13 @@ func GetBankDepedencyGenerator() aclkeeper.DependencyGeneratorMap {
return dependencyGeneratorMap
}

// TODO:: we can make resource types more granular (e.g KV_PARAM or KV_BANK_BALANCE)
func MsgSendDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sdk.Msg) ([]sdkacltypes.AccessOperation, error) {
msgSend, ok := msg.(*banktypes.MsgSend)
if !ok {
return []sdkacltypes.AccessOperation{}, ErrorInvalidMsgType
}
fromAddrIdentifier := string(banktypes.CreateAccountBalancesPrefixFromBech32(msgSend.FromAddress))
toAddrIdentifier := string(banktypes.CreateAccountBalancesPrefixFromBech32(msgSend.ToAddress))

accessOperations := []sdkacltypes.AccessOperation{
// MsgSend also checks if the coin denom is enabled, but the information is from the params.
Expand All @@ -37,46 +39,53 @@ func MsgSendDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sd
// Checks balance of sender
{
AccessType: sdkacltypes.AccessType_READ,
ResourceType: sdkacltypes.ResourceType_KV,
IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgSend.FromAddress),
ResourceType: sdkacltypes.ResourceType_KV_BANK_BALANCES,
IdentifierTemplate: fromAddrIdentifier,
},
// Reduce the amount from the sender's balance
{
AccessType: sdkacltypes.AccessType_WRITE,
ResourceType: sdkacltypes.ResourceType_KV,
IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgSend.FromAddress),
ResourceType: sdkacltypes.ResourceType_KV_BANK_BALANCES,
IdentifierTemplate: fromAddrIdentifier,
},

// Checks balance for receiver
{
AccessType: sdkacltypes.AccessType_READ,
ResourceType: sdkacltypes.ResourceType_KV,
IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgSend.ToAddress),
ResourceType: sdkacltypes.ResourceType_KV_BANK_BALANCES,
IdentifierTemplate: toAddrIdentifier,
},
{
AccessType: sdkacltypes.AccessType_WRITE,
ResourceType: sdkacltypes.ResourceType_KV,
IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgSend.ToAddress),
ResourceType: sdkacltypes.ResourceType_KV_BANK_BALANCES,
IdentifierTemplate: toAddrIdentifier,
},

// Tries to create the reciever's account if it doesn't exist
{
AccessType: sdkacltypes.AccessType_READ,
ResourceType: sdkacltypes.ResourceType_KV,
IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.AUTH, msgSend.ToAddress),
ResourceType: sdkacltypes.ResourceType_KV_AUTH_ADDRESS_STORE,
IdentifierTemplate: string(authtypes.CreateAddressStoreKeyFromBech32(msgSend.ToAddress)),
},
{
AccessType: sdkacltypes.AccessType_WRITE,
ResourceType: sdkacltypes.ResourceType_KV,
IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.AUTH, msgSend.ToAddress),
ResourceType: sdkacltypes.ResourceType_KV_AUTH_ADDRESS_STORE,
IdentifierTemplate: string(authtypes.CreateAddressStoreKeyFromBech32(msgSend.ToAddress)),
},

// Gets Account Info for the sender
{
AccessType: sdkacltypes.AccessType_READ,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the ToAddress, there is a write access but for the FromAddress there is no write access, only read. Just want to make sure it wasn't missed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeee, I believe it has to exist already based (If it doesn't exist then there's no money to be sent) on the logic we only create accounts for the receiver if it doesn't exist

ResourceType: sdkacltypes.ResourceType_KV_AUTH_ADDRESS_STORE,
IdentifierTemplate: string(authtypes.CreateAddressStoreKeyFromBech32(msgSend.FromAddress)),
},

// Last Operation should always be a commit
{
ResourceType: sdkacltypes.ResourceType_ANY,
AccessType: sdkacltypes.AccessType_COMMIT,
IdentifierTemplate: utils.DefaultIDTemplate,
},
}

return accessOperations, nil
}
158 changes: 158 additions & 0 deletions aclmapping/bank/mappings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package aclbankmapping

import (
"testing"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol"
acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/bank/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
aclutils "github.com/sei-protocol/sei-chain/aclmapping/utils"
oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types"
"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)


func cacheTxContext(ctx sdk.Context) (sdk.Context, sdk.CacheMultiStore) {
ms := ctx.MultiStore()
msCache := ms.CacheMultiStore()
return ctx.WithMultiStore(msCache), msCache
}

func TestMsgBankSendAclOps(t *testing.T) {
priv1 := secp256k1.GenPrivKey()
addr1 := sdk.AccAddress(priv1.PubKey().Address())
priv2 := secp256k1.GenPrivKey()
addr2 := sdk.AccAddress(priv2.PubKey().Address())
coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}

tests := []struct {
name string
expectedError error
msg *types.MsgSend
dynamicDep bool
}{
{
name: "default send",
msg: types.NewMsgSend(addr1, addr2, coins),
expectedError: nil,
dynamicDep: true,
},
{
name: "dont check synchronous",
msg: types.NewMsgSend(addr1, addr2, coins),
expectedError: nil,
dynamicDep: false,
},
}

acc1 := &authtypes.BaseAccount{
Address: addr1.String(),
}
acc2 := &authtypes.BaseAccount{
Address: addr2.String(),
}
accs := authtypes.GenesisAccounts{acc1, acc2}
balances := []types.Balance{
{
Address: addr1.String(),
Coins: coins,
},
{
Address: addr2.String(),
Coins: coins,
},
}

app := simapp.SetupWithGenesisAccounts(accs, balances...)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

handler := bank.NewHandler(app.BankKeeper)
msgValidator := sdkacltypes.NewMsgValidator(aclutils.StoreKeyToResourceTypePrefixMap)
ctx = ctx.WithMsgValidator(msgValidator)

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
handlerCtx, cms := cacheTxContext(ctx)

_, err := handler(handlerCtx, tc.msg)

depdenencies , _ := MsgSendDependencyGenerator(app.AccessControlKeeper, handlerCtx, tc.msg)

if !tc.dynamicDep {
depdenencies = sdkacltypes.SynchronousAccessOps()
}

if tc.expectedError != nil {
require.EqualError(t, err, tc.expectedError.Error())
} else {
require.NoError(t, err)
}
missing := handlerCtx.MsgValidator().ValidateAccessOperations(depdenencies, cms.GetEvents())
require.Empty(t, missing)
})
}
}

func TestGeneratorInvalidMessageTypes(t *testing.T) {
accs := authtypes.GenesisAccounts{}
balances := []types.Balance{}

app := simapp.SetupWithGenesisAccounts(accs, balances...)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

oracleVote := oracletypes.MsgAggregateExchangeRateVote{
ExchangeRates: "1usei",
Feeder: "test",
Validator: "validator",
}

_, err := MsgSendDependencyGenerator(app.AccessControlKeeper, ctx, &oracleVote)
require.Error(t, err)
}

func TestMsgBeginBankSendGenerator(t *testing.T) {
priv1 := secp256k1.GenPrivKey()
addr1 := sdk.AccAddress(priv1.PubKey().Address())
priv2 := secp256k1.GenPrivKey()
addr2 := sdk.AccAddress(priv2.PubKey().Address())
coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}

acc1 := &authtypes.BaseAccount{
Address: addr1.String(),
}
acc2 := &authtypes.BaseAccount{
Address: addr2.String(),
}
accs := authtypes.GenesisAccounts{acc1, acc2}
balances := []types.Balance{
{
Address: addr1.String(),
Coins: coins,
},
{
Address: addr2.String(),
Coins: coins,
},
}

app := simapp.SetupWithGenesisAccounts(accs, balances...)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

sendMsg := banktypes.MsgSend{
FromAddress: addr1.String(),
ToAddress: addr2.String(),
Amount: coins,
}

accessOps, err := MsgSendDependencyGenerator(app.AccessControlKeeper, ctx, &sendMsg)
require.NoError(t, err)
err = acltypes.ValidateAccessOps(accessOps)
require.NoError(t, err)
}
2 changes: 1 addition & 1 deletion aclmapping/utils/identifier_templates.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package util
package utils

import (
"fmt"
Expand Down
51 changes: 51 additions & 0 deletions aclmapping/utils/resource_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package utils

import (
aclsdktypes "github.com/cosmos/cosmos-sdk/types/accesscontrol"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
dextypes "github.com/sei-protocol/sei-chain/x/dex/types"
epochtypes "github.com/sei-protocol/sei-chain/x/epoch/types"
oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types"
tokenfactorytypes "github.com/sei-protocol/sei-chain/x/tokenfactory/types"
)

var StoreKeyToResourceTypePrefixMap = aclsdktypes.StoreKeyToResourceTypePrefixMap{
aclsdktypes.ParentNodeKey: {
aclsdktypes.ResourceType_ANY: aclsdktypes.EmptyPrefix,
aclsdktypes.ResourceType_KV: aclsdktypes.EmptyPrefix,
aclsdktypes.ResourceType_Mem: aclsdktypes.EmptyPrefix,
},
dextypes.StoreKey: {
aclsdktypes.ResourceType_KV_DEX: aclsdktypes.EmptyPrefix,
aclsdktypes.ResourceType_DexMem: aclsdktypes.EmptyPrefix,
},
banktypes.StoreKey: {
aclsdktypes.ResourceType_KV_BANK: aclsdktypes.EmptyPrefix,
aclsdktypes.ResourceType_KV_BANK_BALANCES: banktypes.BalancesPrefix,
aclsdktypes.ResourceType_KV_BANK_SUPPLY: banktypes.SupplyKey,
aclsdktypes.ResourceType_KV_BANK_DENOM: banktypes.DenomMetadataPrefix,
},
authtypes.StoreKey: {
aclsdktypes.ResourceType_KV_AUTH: aclsdktypes.EmptyPrefix,
aclsdktypes.ResourceType_KV_AUTH_ADDRESS_STORE: authtypes.AddressStoreKeyPrefix,
},
oracletypes.StoreKey: {
aclsdktypes.ResourceType_KV_ORACLE: aclsdktypes.EmptyPrefix,
aclsdktypes.ResourceType_KV_ORACLE_VOTE_TARGETS: oracletypes.VoteTargetKey,
aclsdktypes.ResourceType_KV_ORACLE_AGGREGATE_VOTES: oracletypes.AggregateExchangeRateVoteKey,
aclsdktypes.ResourceType_KV_ORACLE_FEEDERS: oracletypes.FeederDelegationKey,
},
stakingtypes.StoreKey: {
aclsdktypes.ResourceType_KV_STAKING: aclsdktypes.EmptyPrefix,
aclsdktypes.ResourceType_KV_STAKING_DELEGATION: stakingtypes.DelegationKey,
aclsdktypes.ResourceType_KV_STAKING_VALIDATOR: stakingtypes.ValidatorsKey,
},
tokenfactorytypes.StoreKey: {
aclsdktypes.ResourceType_KV_TOKENFACTORY: aclsdktypes.EmptyPrefix,
},
epochtypes.StoreKey: {
aclsdktypes.ResourceType_KV_EPOCH: aclsdktypes.EmptyPrefix,
},
}
Loading