diff --git a/aclmapping/oracle/mappings.go b/aclmapping/oracle/mappings.go index adce4da9b4..778bc5bf6b 100644 --- a/aclmapping/oracle/mappings.go +++ b/aclmapping/oracle/mappings.go @@ -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" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" utils "github.com/sei-protocol/sei-chain/aclmapping/utils" oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" ) @@ -28,6 +29,7 @@ func MsgVoteDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sd if !ok { return []sdkacltypes.AccessOperation{}, ErrorInvalidMsgType } + valAddr, _ := sdk.ValAddressFromBech32(msgVote.Validator) accessOperations := []sdkacltypes.AccessOperation{ // validate feeder @@ -35,16 +37,17 @@ func MsgVoteDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sd { ResourceType: sdkacltypes.ResourceType_KV_ORACLE_FEEDERS, AccessType: sdkacltypes.AccessType_READ, - IdentifierTemplate: msgVote.Validator, + IdentifierTemplate: string(oracletypes.GetFeederDelegationKey(valAddr)), }, // read validator from staking - READ // validator is bonded check - READ // (both covered by below) { - ResourceType: sdkacltypes.ResourceType_KV_STAKING, + ResourceType: sdkacltypes.ResourceType_KV_STAKING_VALIDATOR, AccessType: sdkacltypes.AccessType_READ, - IdentifierTemplate: msgVote.Validator, + IdentifierTemplate: string(stakingtypes.GetValidatorKey(valAddr)), }, + // get vote target (for all exchange rate tuples) -> blanket read on that prefix - READ { ResourceType: sdkacltypes.ResourceType_KV_ORACLE_VOTE_TARGETS, @@ -55,15 +58,12 @@ func MsgVoteDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sd // set exchange rate vote - WRITE { ResourceType: sdkacltypes.ResourceType_KV_ORACLE_AGGREGATE_VOTES, - AccessType: sdkacltypes.AccessType_READ, - IdentifierTemplate: msgVote.Validator, + AccessType: sdkacltypes.AccessType_WRITE, + IdentifierTemplate: string(oracletypes.GetAggregateExchangeRateVoteKey(valAddr)), }, + // Last Operation should always be a commit - { - ResourceType: sdkacltypes.ResourceType_ANY, - AccessType: sdkacltypes.AccessType_COMMIT, - IdentifierTemplate: utils.DefaultIDTemplate, - }, + *acltypes.CommitAccessOp(), } return accessOperations, nil } diff --git a/aclmapping/oracle/mappings_test.go b/aclmapping/oracle/mappings_test.go index 0e7a6d29de..0e3246475c 100644 --- a/aclmapping/oracle/mappings_test.go +++ b/aclmapping/oracle/mappings_test.go @@ -1,16 +1,129 @@ -package acloraclemapping +package acloraclemapping_test import ( + "fmt" "testing" "time" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol" + oracleacl "github.com/sei-protocol/sei-chain/aclmapping/oracle" + aclutils "github.com/sei-protocol/sei-chain/aclmapping/utils" + utils "github.com/sei-protocol/sei-chain/aclmapping/utils" + "github.com/sei-protocol/sei-chain/app/apptesting" + oraclekeeper "github.com/sei-protocol/sei-chain/x/oracle/keeper" + oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/sei-protocol/sei-chain/app" - oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" ) +type KeeperTestSuite struct { + apptesting.KeeperTestHelper + + queryClient oracletypes.QueryClient + msgServer oracletypes.MsgServer + // defaultDenom is on the suite, as it depends on the creator test address. + defaultDenom string + defaultExchangeRate string + initalBalance sdk.Coins + + validator sdk.ValAddress +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +// Runs before each test case +func (suite *KeeperTestSuite) SetupTest() { + suite.Setup() +} + +// Explicitly only run once during setup +func (suite *KeeperTestSuite) PrepareTest() { + suite.defaultDenom = "usei" + suite.defaultExchangeRate = fmt.Sprintf("%dusei", sdk.NewDec(1700)) + + suite.initalBalance = sdk.Coins{sdk.NewInt64Coin(suite.defaultDenom, 100000000000)} + suite.FundAcc(suite.TestAccs[0], suite.initalBalance) + + suite.queryClient = oracletypes.NewQueryClient(suite.QueryHelper) + suite.msgServer = oraclekeeper.NewMsgServerImpl(suite.App.OracleKeeper) + + // testInput := oraclekeeper.CreateTestInput(suite.T()) + // suite.Ctx = testInput.Ctx + + msgValidator := sdkacltypes.NewMsgValidator(aclutils.StoreKeyToResourceTypePrefixMap) + suite.Ctx = suite.Ctx.WithMsgValidator(msgValidator) + suite.Ctx = suite.Ctx.WithBlockHeight(1) + suite.validator = suite.SetupValidator(stakingtypes.Bonded) + + suite.App.OracleKeeper.SetFeederDelegation(suite.Ctx, suite.validator, suite.TestAccs[0]) + suite.App.OracleKeeper.SetVoteTarget(suite.Ctx, suite.defaultDenom) +} + +func (suite *KeeperTestSuite) TestMsgBurnDependencies() { + suite.PrepareTest() + tests := []struct { + name string + expectedError error + msg *oracletypes.MsgAggregateExchangeRateVote + dynamicDep bool + }{ + { + name: "default vote", + msg: &oracletypes.MsgAggregateExchangeRateVote{ + ExchangeRates: suite.defaultExchangeRate, + Feeder: suite.TestAccs[0].String(), + Validator: suite.validator.String(), + }, + expectedError: nil, + dynamicDep: true, + }, + { + name: "dont check synchronous", + msg: &oracletypes.MsgAggregateExchangeRateVote{ + ExchangeRates: suite.defaultExchangeRate, + Feeder: suite.TestAccs[0].String(), + Validator: suite.validator.String(), + }, + expectedError: nil, + dynamicDep: false, + }, + } + for _, tc := range tests { + suite.Run(fmt.Sprintf("Test Case: %s", tc.name), func() { + handlerCtx, cms := utils.CacheTxContext(suite.Ctx) + _, err := suite.msgServer.AggregateExchangeRateVote( + sdk.WrapSDKContext(handlerCtx), + tc.msg, + ) + depdenencies , _ := oracleacl.MsgVoteDependencyGenerator( + suite.App.AccessControlKeeper, + handlerCtx, + tc.msg, + ) + + if !tc.dynamicDep { + depdenencies = sdkacltypes.SynchronousAccessOps() + } + + if tc.expectedError != nil { + suite.Require().EqualError(err, tc.expectedError.Error()) + } else { + suite.Require().NoError(err) + } + + missing := handlerCtx.MsgValidator().ValidateAccessOperations(depdenencies, cms.GetEvents()) + suite.Require().Empty(missing) + }) + } +} func TestMsgVoteDependencyGenerator(t *testing.T) { tm := time.Now().UTC() valPub := secp256k1.GenPrivKey().PubKey() @@ -23,7 +136,7 @@ func TestMsgVoteDependencyGenerator(t *testing.T) { Validator: "validator", } - accessOps, err := MsgVoteDependencyGenerator(testWrapper.App.AccessControlKeeper, testWrapper.Ctx, &oracleVote) + accessOps, err := oracleacl.MsgVoteDependencyGenerator(testWrapper.App.AccessControlKeeper, testWrapper.Ctx, &oracleVote) require.NoError(t, err) err = acltypes.ValidateAccessOps(accessOps) require.NoError(t, err) diff --git a/aclmapping/utils/test_utils.go b/aclmapping/utils/test_utils.go new file mode 100644 index 0000000000..5b39275708 --- /dev/null +++ b/aclmapping/utils/test_utils.go @@ -0,0 +1,9 @@ +package utils + +import sdk "github.com/cosmos/cosmos-sdk/types" + +func CacheTxContext(ctx sdk.Context) (sdk.Context, sdk.CacheMultiStore) { + ms := ctx.MultiStore() + msCache := ms.CacheMultiStore() + return ctx.WithMultiStore(msCache), msCache +} diff --git a/x/oracle/simulation/operations.go b/x/oracle/simulation/operations.go index 3988c4b164..2028e73625 100644 --- a/x/oracle/simulation/operations.go +++ b/x/oracle/simulation/operations.go @@ -65,7 +65,7 @@ func WeightedOperations( } // SimulateMsgAggregateExchangeRateVote generates a MsgAggregateExchangeRateVote with random values. -//nolint: funlen +// nolint: funlen func SimulateMsgAggregateExchangeRateVote(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, @@ -122,7 +122,7 @@ func SimulateMsgAggregateExchangeRateVote(ak types.AccountKeeper, bk types.BankK } // SimulateMsgDelegateFeedConsent generates a MsgDelegateFeedConsent with random values. -//nolint: funlen +// nolint: funlen func SimulateMsgDelegateFeedConsent(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,