diff --git a/aclmapping/dex/mappings.go b/aclmapping/dex/mappings.go index 58156eb64e..775f785f5a 100644 --- a/aclmapping/dex/mappings.go +++ b/aclmapping/dex/mappings.go @@ -7,28 +7,107 @@ 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" - dexmoduletypes "github.com/sei-protocol/sei-chain/x/dex/types" + dextypes "github.com/sei-protocol/sei-chain/x/dex/types" ) -var ErrPlaceOrdersGenerator = fmt.Errorf("invalid message received for type DexPlaceOrders") +var ErrPlaceOrdersGenerator = fmt.Errorf("invalid message received for dex module") func GetDexDependencyGenerators() aclkeeper.DependencyGeneratorMap { dependencyGeneratorMap := make(aclkeeper.DependencyGeneratorMap) // dex place orders - placeOrdersKey := acltypes.GenerateMessageKey(&dexmoduletypes.MsgPlaceOrders{}) + placeOrdersKey := acltypes.GenerateMessageKey(&dextypes.MsgPlaceOrders{}) + cancelOrdersKey := acltypes.GenerateMessageKey(&dextypes.MsgCancelOrders{}) dependencyGeneratorMap[placeOrdersKey] = DexPlaceOrdersDependencyGenerator + dependencyGeneratorMap[cancelOrdersKey] = DexCancelOrdersDependencyGenerator return dependencyGeneratorMap } +func GetDexMemReadWrite(contract string) []sdkacltypes.AccessOperation { + if contract == "" { + return []sdkacltypes.AccessOperation{} + } + + return []sdkacltypes.AccessOperation{ + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_DexMem, + IdentifierTemplate: contract, + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_DexMem, + IdentifierTemplate: contract, + }, + } +} + +func GetLongShortOrderBookOps(contractAddr string, priceDenom string, assetDenom string) []sdkacltypes.AccessOperation { + return []sdkacltypes.AccessOperation{ + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV_DEX_CONTRACT_LONGBOOK, + IdentifierTemplate: string(dextypes.OrderBookPrefix(true, contractAddr, priceDenom, assetDenom)), + }, + + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV_DEX_CONTRACT_SHORTBOOK, + IdentifierTemplate: string(dextypes.OrderBookPrefix(false, contractAddr, priceDenom, assetDenom)), + }, + } +} + func DexPlaceOrdersDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sdk.Msg) ([]sdkacltypes.AccessOperation, error) { - placeOrdersMsg, ok := msg.(*dexmoduletypes.MsgPlaceOrders) + placeOrdersMsg, ok := msg.(*dextypes.MsgPlaceOrders) if !ok { return []sdkacltypes.AccessOperation{}, ErrPlaceOrdersGenerator } - // TODO: This is not final, JUST AN EXAMPLE - return []sdkacltypes.AccessOperation{ - {AccessType: sdkacltypes.AccessType_WRITE, ResourceType: sdkacltypes.ResourceType_KV, IdentifierTemplate: placeOrdersMsg.ContractAddr}, - }, nil + + contractAddr := placeOrdersMsg.ContractAddr + + aclOps := GetDexMemReadWrite(contractAddr) + + aclOps = append(aclOps, []sdkacltypes.AccessOperation{ + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV_DEX_NEXT_ORDER_ID, + IdentifierTemplate: string(dextypes.NextOrderIDPrefix(contractAddr)), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV_DEX_NEXT_ORDER_ID, + IdentifierTemplate: string(dextypes.NextOrderIDPrefix(contractAddr)), + }, + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV_DEX_TICK_SIZE, + IdentifierTemplate: string(dextypes.TickSizeKeyPrefix(contractAddr)), + }, + }...) + + // Last Operation should always be a commit + aclOps = append(aclOps, *acltypes.CommitAccessOp()) + return aclOps, nil +} + +func DexCancelOrdersDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sdk.Msg) ([]sdkacltypes.AccessOperation, error) { + cancelOrdersMsg, ok := msg.(*dextypes.MsgCancelOrders) + if !ok { + return []sdkacltypes.AccessOperation{}, ErrPlaceOrdersGenerator + } + contractAddr := cancelOrdersMsg.ContractAddr + + aclOps := GetDexMemReadWrite(contractAddr) + + for _, order := range cancelOrdersMsg.GetCancellations() { + priceDenom := order.GetPriceDenom() + assetDenom := order.GetAssetDenom() + aclOps = append(aclOps, GetLongShortOrderBookOps(contractAddr, priceDenom, assetDenom)...) + } + + // Last Operation should always be a commit + aclOps = append(aclOps, *acltypes.CommitAccessOp()) + return aclOps, nil } diff --git a/aclmapping/dex/mappings_test.go b/aclmapping/dex/mappings_test.go new file mode 100644 index 0000000000..906fd01b2e --- /dev/null +++ b/aclmapping/dex/mappings_test.go @@ -0,0 +1,294 @@ +package acldexmapping_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol" + acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types" + "github.com/k0kubun/pp/v3" + dexacl "github.com/sei-protocol/sei-chain/aclmapping/dex" + aclutils "github.com/sei-protocol/sei-chain/aclmapping/utils" + "github.com/sei-protocol/sei-chain/app" + "github.com/sei-protocol/sei-chain/app/apptesting" + keepertest "github.com/sei-protocol/sei-chain/testutil/keeper" + dexcache "github.com/sei-protocol/sei-chain/x/dex/cache" + dexmsgserver "github.com/sei-protocol/sei-chain/x/dex/keeper/msgserver" + "github.com/sei-protocol/sei-chain/x/dex/types" + dextypes "github.com/sei-protocol/sei-chain/x/dex/types" + dexutils "github.com/sei-protocol/sei-chain/x/dex/utils" + 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 dextypes.QueryClient + msgServer dextypes.MsgServer + defaultDenom string + defaultExchangeRate string + initialBalance sdk.Coins + creator string + contract string + + msgPlaceOrders *dextypes.MsgPlaceOrders + msgCancelOrders *dextypes.MsgCancelOrders +} + +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.initialBalance = sdk.Coins{sdk.NewInt64Coin(suite.defaultDenom, 100000000000)} + suite.initialBalance = sdk.Coins{sdk.NewInt64Coin("stake", 100000000000)} + suite.FundAcc(suite.TestAccs[0], suite.initialBalance) + + suite.queryClient = dextypes.NewQueryClient(suite.QueryHelper) + suite.msgServer = dexmsgserver.NewMsgServerImpl(suite.App.DexKeeper) + + msgValidator := sdkacltypes.NewMsgValidator(aclutils.StoreKeyToResourceTypePrefixMap) + suite.Ctx = suite.Ctx.WithMsgValidator(msgValidator) + + suite.Ctx = suite.Ctx.WithBlockHeight(10) + suite.Ctx = suite.Ctx.WithBlockTime(time.Unix(333, 0)) + + suite.creator = suite.TestAccs[0].String() + suite.contract = "sei14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sh9m79m" + + suite.App.DexKeeper.AddRegisteredPair(suite.Ctx, suite.contract, keepertest.TestPair) + suite.App.DexKeeper.SetTickSizeForPair(suite.Ctx, suite.contract, keepertest.TestPair, *keepertest.TestPair.Ticksize) + + suite.msgPlaceOrders = &types.MsgPlaceOrders{ + Creator: suite.creator, + ContractAddr: suite.contract, + Orders: []*types.Order{ + { + Price: sdk.MustNewDecFromStr("10"), + Quantity: sdk.MustNewDecFromStr("10"), + Data: "", + PositionDirection: types.PositionDirection_LONG, + OrderType: types.OrderType_LIMIT, + PriceDenom: keepertest.TestPriceDenom, + AssetDenom: keepertest.TestAssetDenom, + }, + { + Price: sdk.MustNewDecFromStr("20"), + Quantity: sdk.MustNewDecFromStr("5"), + Data: "", + PositionDirection: types.PositionDirection_SHORT, + OrderType: types.OrderType_MARKET, + PriceDenom: keepertest.TestPriceDenom, + AssetDenom: keepertest.TestAssetDenom, + }, + }, + } + + suite.msgCancelOrders = &types.MsgCancelOrders{ + Creator: suite.creator, + ContractAddr: suite.contract, + Cancellations: []*types.Cancellation{ + { + Id: 1, + Price: sdk.MustNewDecFromStr("10"), + Creator: suite.creator, + PositionDirection: types.PositionDirection_LONG, + PriceDenom: keepertest.TestPriceDenom, + AssetDenom: keepertest.TestAssetDenom, + }, + { + Id: 2, + Creator: suite.creator, + Price: sdk.MustNewDecFromStr("20"), + PositionDirection: types.PositionDirection_SHORT, + PriceDenom: keepertest.TestPriceDenom, + AssetDenom: keepertest.TestAssetDenom, + }, + }, + } +} + + +func (suite *KeeperTestSuite) TestMsgPlaceOrder() { + suite.PrepareTest() + tests := []struct { + name string + expectedError error + msg *dextypes.MsgPlaceOrders + dynamicDep bool + }{ + { + name: "default place order", + msg: suite.msgPlaceOrders, + expectedError: nil, + dynamicDep: true, + }, + { + name: "dont check synchronous", + msg: suite.msgPlaceOrders, + expectedError: nil, + dynamicDep: false, + }, + } + for _, tc := range tests { + suite.Run(fmt.Sprintf("Test Case: %s", tc.name), func() { + goCtx := context.WithValue(suite.Ctx.Context(), dexutils.DexMemStateContextKey, dexcache.NewMemState()) + suite.Ctx = suite.Ctx.WithContext(goCtx) + + handlerCtx, cms := aclutils.CacheTxContext(suite.Ctx) + _, err := suite.msgServer.PlaceOrders( + sdk.WrapSDKContext(handlerCtx), + tc.msg, + ) + + depdenencies , _ := dexacl.DexPlaceOrdersDependencyGenerator( + 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()) + pp.Default.SetColoringEnabled(false) + + suite.Require().Empty(missing) + }) + } +} + +func (suite *KeeperTestSuite) TestMsgCancelOrder() { + suite.PrepareTest() + tests := []struct { + name string + expectedError error + msg *dextypes.MsgCancelOrders + dynamicDep bool + }{ + { + name: "default cancel order", + msg: suite.msgCancelOrders, + expectedError: nil, + dynamicDep: true, + }, + { + name: "dont check synchronous", + msg: suite.msgCancelOrders, + expectedError: nil, + dynamicDep: false, + }, + } + for _, tc := range tests { + suite.Run(fmt.Sprintf("Test Case: %s", tc.name), func() { + goCtx := context.WithValue(suite.Ctx.Context(), dexutils.DexMemStateContextKey, dexcache.NewMemState()) + suite.Ctx = suite.Ctx.WithContext(goCtx) + + _, err := suite.msgServer.PlaceOrders( + sdk.WrapSDKContext(suite.Ctx), + suite.msgPlaceOrders, + ) + + handlerCtx, cms := aclutils.CacheTxContext(suite.Ctx) + _, err = suite.msgServer.CancelOrders( + sdk.WrapSDKContext(handlerCtx), + tc.msg, + ) + + depdenencies , _ := dexacl.DexCancelOrdersDependencyGenerator( + 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 TestGeneratorInvalidMessageTypes(t *testing.T) { + tm := time.Now().UTC() + valPub := secp256k1.GenPrivKey().PubKey() + testWrapper := app.NewTestWrapper(t, tm, valPub) + + oracleVote := oracletypes.MsgAggregateExchangeRateVote{ + ExchangeRates: "1usei", + Feeder: "test", + Validator: "validator", + } + + _, err := dexacl.DexPlaceOrdersDependencyGenerator( + testWrapper.App.AccessControlKeeper, + testWrapper.Ctx, + &oracleVote, + ) + require.Error(t, err) + + _, err = dexacl.DexCancelOrdersDependencyGenerator( + testWrapper.App.AccessControlKeeper, + testWrapper.Ctx, + &oracleVote, + ) + require.Error(t, err) +} + +func (suite *KeeperTestSuite) TestMsgPlaceOrderGenerator() { + suite.PrepareTest() + + accessOps, err := dexacl.DexPlaceOrdersDependencyGenerator( + suite.App.AccessControlKeeper, + suite.Ctx, + suite.msgPlaceOrders, + ) + require.NoError(suite.T(), err) + err = acltypes.ValidateAccessOps(accessOps) + require.NoError(suite.T(), err) +} + +func (suite *KeeperTestSuite) TestMsgCancelOrderGenerator() { + suite.PrepareTest() + accessOps, err := dexacl.DexCancelOrdersDependencyGenerator( + suite.App.AccessControlKeeper, + suite.Ctx, + suite.msgCancelOrders, + ) + require.NoError(suite.T(), err) + err = acltypes.ValidateAccessOps(accessOps) + require.NoError(suite.T(), err) +} + diff --git a/aclmapping/utils/identifier_templates.go b/aclmapping/utils/identifier_templates.go index ba79cecc2a..effbc00445 100644 --- a/aclmapping/utils/identifier_templates.go +++ b/aclmapping/utils/identifier_templates.go @@ -13,6 +13,7 @@ const ( AUTH = "auth" STAKING = "staking" TOKENFACTORY = "tokenfactory" + DEX = "dex" DefaultIDTemplate = "*" ) diff --git a/aclmapping/utils/resource_type.go b/aclmapping/utils/resource_type.go index cb0fe3f2cd..7e90975e59 100644 --- a/aclmapping/utils/resource_type.go +++ b/aclmapping/utils/resource_type.go @@ -14,13 +14,35 @@ import ( var StoreKeyToResourceTypePrefixMap = aclsdktypes.StoreKeyToResourceTypePrefixMap{ aclsdktypes.ParentNodeKey: { - aclsdktypes.ResourceType_ANY: aclsdktypes.EmptyPrefix, - aclsdktypes.ResourceType_KV: aclsdktypes.EmptyPrefix, - aclsdktypes.ResourceType_Mem: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_ANY: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_KV: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_Mem: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_KV_WASM: aclsdktypes.EmptyPrefix, }, dextypes.StoreKey: { - aclsdktypes.ResourceType_KV_DEX: aclsdktypes.EmptyPrefix, - aclsdktypes.ResourceType_DexMem: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_KV_DEX: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_DexMem: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_KV_DEX_CONTRACT_LONGBOOK: dextypes.KeyPrefix(dextypes.LongBookKey), + aclsdktypes.ResourceType_KV_DEX_CONTRACT_SHORTBOOK: dextypes.KeyPrefix(dextypes.ShortBookKey), + // pricedenom and assetdenoms are the prefixes + aclsdktypes.ResourceType_KV_DEX_PAIR_PREFIX: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_KV_DEX_TWAP: dextypes.KeyPrefix(dextypes.TwapKey), + aclsdktypes.ResourceType_KV_DEX_PRICE: dextypes.KeyPrefix(dextypes.PriceKey), + aclsdktypes.ResourceType_KV_DEX_SETTLEMENT_ENTRY: dextypes.KeyPrefix(dextypes.SettlementEntryKey), + aclsdktypes.ResourceType_KV_DEX_REGISTERED_PAIR: dextypes.KeyPrefix(dextypes.RegisteredPairKey), + aclsdktypes.ResourceType_KV_DEX_TICK_SIZE: dextypes.KeyPrefix(dextypes.TickSizeKey), + aclsdktypes.ResourceType_KV_DEX_ORDER: dextypes.KeyPrefix(dextypes.OrderKey), + aclsdktypes.ResourceType_KV_DEX_CANCEL: dextypes.KeyPrefix(dextypes.CancelKey), + aclsdktypes.ResourceType_KV_DEX_ACCOUNT_ACTIVE_ORDERS: dextypes.KeyPrefix(dextypes.AccountActiveOrdersKey), + aclsdktypes.ResourceType_KV_DEX_REGISTERED_PAIR_COUNT: dextypes.KeyPrefix(dextypes.RegisteredPairCount), + aclsdktypes.ResourceType_KV_DEX_ASSET_LIST: dextypes.KeyPrefix(dextypes.AssetListKey), + aclsdktypes.ResourceType_KV_DEX_NEXT_ORDER_ID: dextypes.KeyPrefix(dextypes.NextOrderIDKey), + aclsdktypes.ResourceType_KV_DEX_NEXT_SETTLEMENT_ID: dextypes.KeyPrefix(dextypes.NextSettlementIDKey), + aclsdktypes.ResourceType_KV_DEX_MATCH_RESULT: dextypes.KeyPrefix(dextypes.MatchResultKey), + aclsdktypes.ResourceType_KV_DEX_ORDER_BOOK: dextypes.KeyPrefix(dextypes.NextOrderIDKey), + // SETTLEMENT keys are prefixed with account and order id + aclsdktypes.ResourceType_KV_DEX_SETTLEMENT_ORDER_ID: aclsdktypes.EmptyPrefix, + aclsdktypes.ResourceType_KV_DEX_SETTLEMENT: aclsdktypes.EmptyPrefix, }, banktypes.StoreKey: { aclsdktypes.ResourceType_KV_BANK: aclsdktypes.EmptyPrefix, @@ -45,10 +67,13 @@ var StoreKeyToResourceTypePrefixMap = aclsdktypes.StoreKeyToResourceTypePrefixMa aclsdktypes.ResourceType_KV_DISTRIBUTION_SLASH_EVENT: distributiontypes.ValidatorSlashEventPrefix, }, 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, + 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, + aclsdktypes.ResourceType_KV_ORACLE_PRICE_SNAPSHOT: oracletypes.PriceSnapshotKey, + aclsdktypes.ResourceType_KV_ORACLE_EXCHANGE_RATE: oracletypes.ExchangeRateKey, + aclsdktypes.ResourceType_KV_ORACLE_VOTE_PENALTY_COUNTER: oracletypes.VotePenaltyCounterKey, }, stakingtypes.StoreKey: { aclsdktypes.ResourceType_KV_STAKING: aclsdktypes.EmptyPrefix, diff --git a/aclmapping/utils/resource_type_test.go b/aclmapping/utils/resource_type_test.go new file mode 100644 index 0000000000..fffe91a44e --- /dev/null +++ b/aclmapping/utils/resource_type_test.go @@ -0,0 +1,29 @@ +package utils_test + +import ( + "fmt" + "testing" + + sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol" + aclutils "github.com/sei-protocol/sei-chain/aclmapping/utils" +) + +func TestAllResourcesInTree(t *testing.T) { + storeKeyToResourceMap := aclutils.StoreKeyToResourceTypePrefixMap + resourceTree := sdkacltypes.ResourceTree + + storeKeyAllResourceTypes := make(map[sdkacltypes.ResourceType]bool) + for _, resourceTypeToPrefix := range storeKeyToResourceMap { + for resourceType := range resourceTypeToPrefix { + storeKeyAllResourceTypes[resourceType] = true + } + } + + for resourceType := range resourceTree { + if _, ok := storeKeyAllResourceTypes[resourceType]; !ok { + panic(fmt.Sprintf("Missing resourceType=%s in the storekey to resource type prefix mapping", resourceType)) + } + } + + +} diff --git a/app/app.go b/app/app.go index 02f8c26bad..f4812654df 100644 --- a/app/app.go +++ b/app/app.go @@ -15,6 +15,7 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/sei-protocol/sei-chain/aclmapping" + aclutils "github.com/sei-protocol/sei-chain/aclmapping/utils" appparams "github.com/sei-protocol/sei-chain/app/params" "github.com/sei-protocol/sei-chain/utils" "github.com/sei-protocol/sei-chain/wasmbinding" @@ -964,12 +965,12 @@ func (app *App) ProcessBlockSynchronous(ctx sdk.Context, txs [][]byte) []*abci.E } // Returns a mapping of the accessOperation to the channels -func getChannelsFromSignalMapping(signalMapping acltypes.MessageCompletionSignalMapping) sdkacltypes.MessageAccessOpsChannelMapping { +func GetChannelsFromSignalMapping(signalMapping acltypes.MessageCompletionSignalMapping) sdkacltypes.MessageAccessOpsChannelMapping { channelsMapping := make(sdkacltypes.MessageAccessOpsChannelMapping) for messageIndex, accessOperationsToSignal := range signalMapping { + channelsMapping[messageIndex] = make(sdkacltypes.AccessOpsChannelMapping) for accessOperation, completionSignals := range accessOperationsToSignal { var channels []chan interface{} - channelsMapping[messageIndex] = make(sdkacltypes.AccessOpsChannelMapping) for _, completionSignal := range completionSignals { channels = append(channels, completionSignal.Channel) } @@ -1004,12 +1005,14 @@ func (app *App) ProcessTxConcurrent( ) { defer wg.Done() // Store the Channels in the Context Object for each transaction - ctx = ctx.WithTxBlockingChannels(getChannelsFromSignalMapping(txBlockingSignalsMap)) - ctx = ctx.WithTxCompletionChannels(getChannelsFromSignalMapping(txCompletionSignalingMap)) + ctx = ctx.WithTxCompletionChannels(GetChannelsFromSignalMapping(txCompletionSignalingMap)) + ctx = ctx.WithTxBlockingChannels(GetChannelsFromSignalMapping(txBlockingSignalsMap)) ctx = ctx.WithTxMsgAccessOps(txMsgAccessOpMapping) + ctx = ctx.WithMsgValidator( + sdkacltypes.NewMsgValidator(aclutils.StoreKeyToResourceTypePrefixMap), + ) // Deliver the transaction and store the result in the channel - resultChan <- ChannelResult{txIndex, app.DeliverTxWithResult(ctx, txBytes)} metrics.IncrTxProcessTypeCounter(metrics.CONCURRENT) } @@ -1139,7 +1142,6 @@ func (app *App) ProcessBlock(ctx sdk.Context, txs [][]byte, req BlockProcessRequ dependencyDag.TxMsgAccessOpMapping, ) if ok { - ctx.Logger().Info("Concurrent Execution succeeded, proceeding to commit block") txResults = concurrentResults // Write the results back to the concurrent contexts - if concurrent execution fails, // this should not be called and the state is rolled back and retried with synchronous execution diff --git a/app/app_test.go b/app/app_test.go index 5b8eb2c5d1..706ba6868a 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -6,6 +6,9 @@ import ( "time" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol" + acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types" + "github.com/k0kubun/pp/v3" "github.com/sei-protocol/sei-chain/app" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -29,3 +32,47 @@ func TestEmptyBlockIdempotency(t *testing.T) { require.Equal(t, len(referenceData), len(data)) } } + +func TestGetChannelsFromSignalMapping(t *testing.T) { + dag := acltypes.NewDag() + commit := *acltypes.CommitAccessOp() + writeA := sdkacltypes.AccessOperation{ + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: "ResourceA", + } + readA := sdkacltypes.AccessOperation{ + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: "ResourceA", + } + readAll := sdkacltypes.AccessOperation{ + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_ANY, + IdentifierTemplate: "*", + } + + dag.AddNodeBuildDependency(0, 0, writeA) + dag.AddNodeBuildDependency(0, 0, commit) + dag.AddNodeBuildDependency(1, 0, readAll) + dag.AddNodeBuildDependency(1, 0, commit) + dag.AddNodeBuildDependency(2, 0, writeA) + dag.AddNodeBuildDependency(2, 0, commit) + dag.AddNodeBuildDependency(3, 0, writeA) + dag.AddNodeBuildDependency(3, 0, commit) + + dag.AddNodeBuildDependency(0, 1, writeA) + dag.AddNodeBuildDependency(0, 1, commit) + dag.AddNodeBuildDependency(1, 1, readA) + dag.AddNodeBuildDependency(1, 1, commit) + + completionSignalsMap, blockingSignalsMap := dag.CompletionSignalingMap, dag.BlockingSignalsMap + + pp.Default.SetColoringEnabled(false) + + resultCompletionSignalsMap := app.GetChannelsFromSignalMapping(completionSignalsMap[0]) + resultBlockingSignalsMap := app.GetChannelsFromSignalMapping(blockingSignalsMap[1]) + + require.True(t, len(resultCompletionSignalsMap) > 1) + require.True(t, len(resultBlockingSignalsMap) > 1) +} diff --git a/go.mod b/go.mod index 0f489b49c9..7f636933fc 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/golang/protobuf v1.5.2 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/k0kubun/pp/v3 v3.2.0 github.com/pkg/errors v0.9.1 github.com/regen-network/cosmos-proto v0.3.1 github.com/spf13/cast v1.5.0 @@ -88,6 +89,7 @@ require ( github.com/lib/pq v1.10.6 // indirect github.com/libp2p/go-buffer-pool v0.0.2 // indirect github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -132,7 +134,7 @@ require ( ) replace ( - github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.1.249 + github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.1.267 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.1.59 diff --git a/go.sum b/go.sum index 777d3e8398..11ea2d80a6 100644 --- a/go.sum +++ b/go.sum @@ -734,6 +734,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -800,6 +802,8 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -1100,8 +1104,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/securego/gosec/v2 v2.11.0/go.mod h1:SX8bptShuG8reGC0XS09+a4H2BoWSJi+fscA+Pulbpo= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= -github.com/sei-protocol/sei-cosmos v0.1.249 h1:beHuyOOGxDewo1G7l067OPO56BKyfVi1boDiuS8jNfw= -github.com/sei-protocol/sei-cosmos v0.1.249/go.mod h1:KPV8lFdD2Ki/M2wZTpfX3LCcuMAZnmcUzYJycjbmOYM= +github.com/sei-protocol/sei-cosmos v0.1.267 h1:B+glrt6ydiKNvtuyUSVes9V7ReKVqx4vyqxTW4KzAaU= +github.com/sei-protocol/sei-cosmos v0.1.267/go.mod h1:o4+r4xXqBLvskNeg5eCmFH+K1Rqd08UPMvnzWI+OxRo= github.com/sei-protocol/sei-tendermint v0.1.59 h1:POGL60PumMQHF4EzAHzvkGfDnodQJLHpl65LuiwSO/Y= github.com/sei-protocol/sei-tendermint v0.1.59/go.mod h1:Olwbjyagrpoxj5DAUhHxMTWDVEfQ3FYdpypaJ3+6Hs8= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=