diff --git a/aclmapping/dependency_generator.go b/aclmapping/dependency_generator.go index 2d875e4022..beec9783a8 100644 --- a/aclmapping/dependency_generator.go +++ b/aclmapping/dependency_generator.go @@ -1,19 +1,16 @@ package aclmapping import ( - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" aclkeeper "github.com/cosmos/cosmos-sdk/x/accesscontrol/keeper" aclbankmapping "github.com/sei-protocol/sei-chain/aclmapping/bank" acldexmapping "github.com/sei-protocol/sei-chain/aclmapping/dex" aclwasmmapping "github.com/sei-protocol/sei-chain/aclmapping/wasm" ) -type CustomDependencyGenerator struct { - WasmKeeper wasmkeeper.Keeper -} +type CustomDependencyGenerator struct{} -func NewCustomDependencyGenerator(wasmKeeper wasmkeeper.Keeper) CustomDependencyGenerator { - return CustomDependencyGenerator{WasmKeeper: wasmKeeper} +func NewCustomDependencyGenerator() CustomDependencyGenerator { + return CustomDependencyGenerator{} } func (customDepGen CustomDependencyGenerator) GetCustomDependencyGenerators() aclkeeper.DependencyGeneratorMap { @@ -21,7 +18,7 @@ func (customDepGen CustomDependencyGenerator) GetCustomDependencyGenerators() ac dependencyGeneratorMap.Merge(acldexmapping.GetDexDependencyGenerators()) dependencyGeneratorMap.Merge(aclbankmapping.GetBankDepedencyGenerator()) - wasmDependencyGenerators := aclwasmmapping.NewWasmDependencyGenerator(customDepGen.WasmKeeper) + wasmDependencyGenerators := aclwasmmapping.NewWasmDependencyGenerator() dependencyGeneratorMap.Merge(wasmDependencyGenerators.GetWasmDependencyGenerators()) return dependencyGeneratorMap diff --git a/aclmapping/wasm/mappings.go b/aclmapping/wasm/mappings.go index 4dc3d1d1e2..17e8acaeca 100644 --- a/aclmapping/wasm/mappings.go +++ b/aclmapping/wasm/mappings.go @@ -1,10 +1,8 @@ package aclwasmmapping import ( - "encoding/json" "fmt" - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol" @@ -18,14 +16,10 @@ var ( ErrWasmFunctionDependenciesDisabled = fmt.Errorf("wasm function dependency mapping disabled") ) -type WasmDependencyGenerator struct { - WasmKeeper wasmkeeper.Keeper -} +type WasmDependencyGenerator struct{} -func NewWasmDependencyGenerator(wasmKeeper wasmkeeper.Keeper) WasmDependencyGenerator { - return WasmDependencyGenerator{ - WasmKeeper: wasmKeeper, - } +func NewWasmDependencyGenerator() WasmDependencyGenerator { + return WasmDependencyGenerator{} } func (wasmDepGen WasmDependencyGenerator) GetWasmDependencyGenerators() aclkeeper.DependencyGeneratorMap { @@ -47,28 +41,7 @@ func (wasmDepGen WasmDependencyGenerator) WasmExecuteContractGenerator(keeper ac if err != nil { return []sdkacltypes.AccessOperation{}, err } - contractInfo := wasmDepGen.WasmKeeper.GetContractInfo(ctx, contractAddr) - codeID := contractInfo.CodeID - - jsonObj := make(map[string]interface{}) - jsonErr := json.Unmarshal(executeContractMsg.Msg, &jsonObj) - var wasmFunction string - if jsonErr != nil { - // try unmarshalling to string for execute function with no params - jsonErr2 := json.Unmarshal(executeContractMsg.Msg, &wasmFunction) - if jsonErr2 != nil { - return []sdkacltypes.AccessOperation{}, ErrInvalidWasmFunction - } - } else { - if len(jsonObj) != 1 { - return []sdkacltypes.AccessOperation{}, ErrInvalidWasmFunction - } - for fieldName := range jsonObj { - // this should only run once based on the check above - wasmFunction = fieldName - } - } - wasmDependencyMapping, err := keeper.GetWasmFunctionDependencyMapping(ctx, codeID, wasmFunction) + wasmDependencyMapping, err := keeper.GetWasmDependencyMapping(ctx, contractAddr) if err != nil { return []sdkacltypes.AccessOperation{}, err } diff --git a/app/app.go b/app/app.go index 7f9937622c..763668d63c 100644 --- a/app/app.go +++ b/app/app.go @@ -499,10 +499,33 @@ func New( app.BankKeeper.(bankkeeper.BaseKeeper).WithMintCoinsRestriction(tokenfactorytypes.NewTokenFactoryDenomMintCoinsRestriction()), app.DistrKeeper, ) + customDependencyGenerators := aclmapping.NewCustomDependencyGenerator() + aclOpts = append(aclOpts, aclkeeper.WithDependencyGeneratorMappings(customDependencyGenerators.GetCustomDependencyGenerators())) + app.AccessControlKeeper = aclkeeper.NewKeeper( + appCodec, + app.keys[acltypes.StoreKey], + app.GetSubspace(acltypes.ModuleName), + aclOpts..., + ) // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks supportedFeatures := "iterator,staking,stargate,sei" - wasmOpts = append(wasmbinding.RegisterCustomPlugins(&app.OracleKeeper, &app.DexKeeper, &app.EpochKeeper), wasmOpts...) + wasmOpts = append( + wasmbinding.RegisterCustomPlugins( + &app.OracleKeeper, + &app.DexKeeper, + &app.EpochKeeper, + &app.AccountKeeper, + app.MsgServiceRouter(), + app.IBCKeeper.ChannelKeeper, + scopedWasmKeeper, + app.BankKeeper, + appCodec, + app.TransferKeeper, + app.AccessControlKeeper, + ), + wasmOpts..., + ) app.WasmKeeper = wasm.NewKeeper( appCodec, keys[wasm.StoreKey], @@ -526,14 +549,6 @@ func New( dexModule := dexmodule.NewAppModule(appCodec, app.DexKeeper, app.AccountKeeper, app.BankKeeper, app.WasmKeeper, app.tracingInfo) epochModule := epochmodule.NewAppModule(appCodec, app.EpochKeeper, app.AccountKeeper, app.BankKeeper) - customDependencyGenerators := aclmapping.NewCustomDependencyGenerator(app.WasmKeeper) - aclOpts = append(aclOpts, aclkeeper.WithDependencyGeneratorMappings(customDependencyGenerators.GetCustomDependencyGenerators())) - app.AccessControlKeeper = aclkeeper.NewKeeper( - appCodec, - app.keys[acltypes.StoreKey], - app.GetSubspace(acltypes.ModuleName), - aclOpts..., - ) // register the proposal types govRouter := govtypes.NewRouter() govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler). diff --git a/cmd/seid/cmd/root.go b/cmd/seid/cmd/root.go index 2a49e8c3b8..9434693d8d 100644 --- a/cmd/seid/cmd/root.go +++ b/cmd/seid/cmd/root.go @@ -7,7 +7,6 @@ import ( "path/filepath" "github.com/CosmWasm/wasmd/x/wasm" - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" @@ -31,7 +30,6 @@ import ( "github.com/sei-protocol/sei-chain/app" "github.com/sei-protocol/sei-chain/app/params" "github.com/sei-protocol/sei-chain/utils/tracing" - "github.com/sei-protocol/sei-chain/wasmbinding" "github.com/spf13/cast" "github.com/spf13/cobra" tmcli "github.com/tendermint/tendermint/libs/cli" @@ -240,10 +238,6 @@ func newApp( panic(err) } - wasmopts := []wasm.Option{wasmkeeper.WithMessageEncoders(&wasmkeeper.MessageEncoders{ - Custom: wasmbinding.CustomEncoder, - })} - return app.New( logger, db, @@ -255,7 +249,7 @@ func newApp( app.MakeEncodingConfig(), wasm.EnableAllProposals, appOpts, - wasmopts, + []wasm.Option{}, []aclkeeper.Option{}, baseapp.SetPruning(pruningOpts), baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), diff --git a/go.mod b/go.mod index 2a4dbda7a0..b4daa07ce1 100644 --- a/go.mod +++ b/go.mod @@ -131,7 +131,7 @@ require ( ) replace ( - github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.1.114 + github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.1.153 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 855691039a..48eb71b118 100644 --- a/go.sum +++ b/go.sum @@ -1097,8 +1097,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.114 h1:Cy5MnBdvql5VJw5pC104DsivQxPg0xkvVOuq6VwDiRk= -github.com/sei-protocol/sei-cosmos v0.1.114/go.mod h1:8ccWQxpBkWbpvBos/T4QO9K9gQxFs0duTqKRnagKo+0= +github.com/sei-protocol/sei-cosmos v0.1.153 h1:6jDUjEicctFtcwZ+NjiNSGe1owFRlLGcam5bR+PFT64= +github.com/sei-protocol/sei-cosmos v0.1.153/go.mod h1:8ccWQxpBkWbpvBos/T4QO9K9gQxFs0duTqKRnagKo+0= 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= diff --git a/wasmbinding/message_plugin.go b/wasmbinding/message_plugin.go new file mode 100644 index 0000000000..e3776be2a6 --- /dev/null +++ b/wasmbinding/message_plugin.go @@ -0,0 +1,157 @@ +package wasmbinding + +import ( + "fmt" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + 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" +) + +var ErrUnexpectedWasmDependency = fmt.Errorf("unexpected wasm dependency detected") + +// forked from wasm +func CustomMessageHandler( + router wasmkeeper.MessageRouter, + channelKeeper wasmtypes.ChannelKeeper, + capabilityKeeper wasmtypes.CapabilityKeeper, + bankKeeper wasmtypes.Burner, + unpacker codectypes.AnyUnpacker, + portSource wasmtypes.ICS20TransferPortSource, + aclKeeper aclkeeper.Keeper, +) wasmkeeper.Messenger { + encoders := wasmkeeper.DefaultEncoders(unpacker, portSource) + encoders = encoders.Merge( + &wasmkeeper.MessageEncoders{ + Custom: CustomEncoder, + }) + return wasmkeeper.NewMessageHandlerChain( + NewSDKMessageDependencyDecorator(wasmkeeper.NewSDKMessageHandler(router, encoders), aclKeeper, encoders), + wasmkeeper.NewIBCRawPacketHandler(channelKeeper, capabilityKeeper), + wasmkeeper.NewBurnCoinMessageHandler(bankKeeper), + ) +} + +// SDKMessageHandler can handles messages that can be encoded into sdk.Message types and routed. +type SDKMessageDependencyDecorator struct { + wrapped wasmkeeper.Messenger + aclKeeper aclkeeper.Keeper + encoders wasmkeeper.MessageEncoders +} + +func NewSDKMessageDependencyDecorator(handler wasmkeeper.Messenger, aclKeeper aclkeeper.Keeper, encoders wasmkeeper.MessageEncoders) SDKMessageDependencyDecorator { + return SDKMessageDependencyDecorator{ + wrapped: handler, + aclKeeper: aclKeeper, + encoders: encoders, + } +} + +func BuildWasmDependencyLookupMap(accessOps []sdkacltypes.AccessOperation) map[acltypes.ResourceAccess]map[string]struct{} { + lookupMap := make(map[acltypes.ResourceAccess]map[string]struct{}) + for _, accessOp := range accessOps { + resourceAccess := acltypes.ResourceAccess{ + ResourceType: accessOp.ResourceType, + AccessType: accessOp.AccessType, + } + if _, ok := lookupMap[resourceAccess]; !ok { + // we haven't added any identifiers for this resource type, so lets initialize the nested map (set) + lookupMap[resourceAccess] = make(map[string]struct{}) + } + lookupMap[resourceAccess][accessOp.IdentifierTemplate] = struct{}{} + } + return lookupMap +} + +func GenerateAllowedResourceAccess(resource sdkacltypes.ResourceType, access sdkacltypes.AccessType) []acltypes.ResourceAccess { + // by default, write, and unknown are ok + accesses := []acltypes.ResourceAccess{ + { + ResourceType: resource, + AccessType: sdkacltypes.AccessType_WRITE, + }, + { + ResourceType: resource, + AccessType: sdkacltypes.AccessType_UNKNOWN, + }, + } + if access == sdkacltypes.AccessType_READ { + accesses = append(accesses, acltypes.ResourceAccess{ + ResourceType: resource, + AccessType: access, + }) + } + return accesses +} + +func AreDependenciesFulfilled(lookupMap map[acltypes.ResourceAccess]map[string]struct{}, accessOp sdkacltypes.AccessOperation) bool { + currResourceAccesses := GenerateAllowedResourceAccess(accessOp.ResourceType, accessOp.AccessType) + for _, currResourceAccess := range currResourceAccesses { + if identifierMap, ok := lookupMap[currResourceAccess]; ok { + if _, ok := identifierMap[accessOp.IdentifierTemplate]; ok { + // we found a proper listed dependency, we can go to the next access op + return true + } + } + } + + // what about parent resources + parentResources := accessOp.ResourceType.GetParentResources() + // for each of the parent resources, we need at least one to be defined in the wasmDependencies + for _, parentResource := range parentResources { + // make parent resource access with same access type + parentResourceAccesses := GenerateAllowedResourceAccess(parentResource, accessOp.AccessType) + // for each of the parent resources, we check to see if its in the lookup map (identifier doesnt matter bc parent) + for _, parentResourceAccess := range parentResourceAccesses { + if _, parentResourcePresent := lookupMap[parentResourceAccess]; parentResourcePresent { + // we can continue to the next access op + return true + } + } + } + return false +} + +func (decorator SDKMessageDependencyDecorator) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + sdkMsgs, err := decorator.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg) + if err != nil { + return nil, nil, err + } + // get the dependencies for the contract to validate against + wasmDependency, err := decorator.aclKeeper.GetWasmDependencyMapping(ctx, contractAddr) + // If no mapping exists, or mapping is disabled, this message would behave as blocking for all resources + if err == aclkeeper.ErrWasmDependencyMappingNotFound { + // no mapping, we can just continue + return decorator.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) + } + if err != nil { + return nil, nil, err + } + if !wasmDependency.Enabled { + // if not enabled, just move on + // TODO: confirm that this is ok, is there ever a case where we should still verify dependencies for a disabled dependency? IDTS + return decorator.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) + } + // convert wasm dependency to a map of resource access and identifier we can look up in + lookupMap := BuildWasmDependencyLookupMap(wasmDependency.AccessOps) + // wasm dependency enabled, we need to validate the message dependencies + for _, msg := range sdkMsgs { + accessOps := decorator.aclKeeper.GetMessageDependencies(ctx, msg) + // go through each access op, and check if there is a completion signal for it OR a parent + for _, accessOp := range accessOps { + // first check for our specific resource access AND identifier template + depsFulfilled := AreDependenciesFulfilled(lookupMap, accessOp) + if !depsFulfilled { + return nil, nil, ErrUnexpectedWasmDependency + } + } + } + // we've gone through all of the messages + // and verified their dependencies with the declared dependencies in the wasm contract dependencies, we can process it now + return decorator.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) +} diff --git a/wasmbinding/query_plugin.go b/wasmbinding/query_plugin.go index a4e9a6bff9..3c8c6eefa4 100644 --- a/wasmbinding/query_plugin.go +++ b/wasmbinding/query_plugin.go @@ -3,9 +3,12 @@ package wasmbinding import ( "encoding/json" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmvmtypes "github.com/CosmWasm/wasmvm/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/accesscontrol" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + aclkeeper "github.com/cosmos/cosmos-sdk/x/accesscontrol/keeper" ) const ( @@ -39,3 +42,148 @@ func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessag } } } + +type CustomQueryHandler struct { + QueryPlugins wasmkeeper.QueryPlugins + aclKeeper aclkeeper.Keeper +} + +func (queryHandler CustomQueryHandler) HandleQuery(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { + wasmDependency, err := queryHandler.aclKeeper.GetWasmDependencyMapping(ctx, caller) + // If no mapping exists, or mapping is disabled, this message would behave as blocking for all resources + needToCheckDependencies := true + if err == aclkeeper.ErrWasmDependencyMappingNotFound { + // no mapping, we can just continue + needToCheckDependencies = false + } + if err != nil { + return nil, err + } + if !wasmDependency.Enabled { + needToCheckDependencies = false + } + lookupMap := BuildWasmDependencyLookupMap(wasmDependency.AccessOps) + if request.Bank != nil { + // check for BANK resource type + accessOp := accesscontrol.AccessOperation{ + ResourceType: accesscontrol.ResourceType_KV_BANK, + AccessType: accesscontrol.AccessType_READ, + IdentifierTemplate: "*", + } + if needToCheckDependencies { + if !AreDependenciesFulfilled(lookupMap, accessOp) { + return nil, ErrUnexpectedWasmDependency + } + } + return queryHandler.QueryPlugins.Bank(ctx, request.Bank) + + } + if request.Custom != nil { + // TODO: specially break down the custom + var contractQuery SeiQueryWrapper + if err := json.Unmarshal(request.Custom, &contractQuery); err != nil { + return nil, sdkerrors.Wrap(err, "Error parsing request data") + } + resourceType := accesscontrol.ResourceType_ANY + switch contractQuery.Route { + case OracleRoute: + resourceType = accesscontrol.ResourceType_KV_ORACLE + case DexRoute: + resourceType = accesscontrol.ResourceType_KV_DEX + case EpochRoute: + resourceType = accesscontrol.ResourceType_KV_EPOCH + } + accessOp := accesscontrol.AccessOperation{ + ResourceType: resourceType, + AccessType: accesscontrol.AccessType_READ, + IdentifierTemplate: "*", + } + if needToCheckDependencies { + if !AreDependenciesFulfilled(lookupMap, accessOp) { + return nil, ErrUnexpectedWasmDependency + } + } + return queryHandler.QueryPlugins.Custom(ctx, request.Custom) + } + if request.IBC != nil { + // check for ANY resource type + // TODO: do we need a special resource type for IBC? + accessOp := accesscontrol.AccessOperation{ + ResourceType: accesscontrol.ResourceType_ANY, + AccessType: accesscontrol.AccessType_READ, + IdentifierTemplate: "*", + } + if needToCheckDependencies { + if !AreDependenciesFulfilled(lookupMap, accessOp) { + return nil, ErrUnexpectedWasmDependency + } + } + return queryHandler.QueryPlugins.IBC(ctx, caller, request.IBC) + } + if request.Staking != nil { + // check for STAKING resource type + accessOp := accesscontrol.AccessOperation{ + ResourceType: accesscontrol.ResourceType_KV_STAKING, + AccessType: accesscontrol.AccessType_READ, + IdentifierTemplate: "*", + } + if needToCheckDependencies { + if !AreDependenciesFulfilled(lookupMap, accessOp) { + return nil, ErrUnexpectedWasmDependency + } + } + return queryHandler.QueryPlugins.Staking(ctx, request.Staking) + } + if request.Stargate != nil { + // check for ANY resource type + // TODO: determine what Stargate dependency granularity looks like + accessOp := accesscontrol.AccessOperation{ + ResourceType: accesscontrol.ResourceType_ANY, + AccessType: accesscontrol.AccessType_READ, + IdentifierTemplate: "*", + } + if needToCheckDependencies { + if !AreDependenciesFulfilled(lookupMap, accessOp) { + return nil, ErrUnexpectedWasmDependency + } + } + return queryHandler.QueryPlugins.Stargate(ctx, request.Stargate) + } + if request.Wasm != nil { + // check for WASM resource type + accessOp := accesscontrol.AccessOperation{ + ResourceType: accesscontrol.ResourceType_KV_WASM, + AccessType: accesscontrol.AccessType_READ, + IdentifierTemplate: "*", + } + if needToCheckDependencies { + if !AreDependenciesFulfilled(lookupMap, accessOp) { + return nil, ErrUnexpectedWasmDependency + } + } + return queryHandler.QueryPlugins.Wasm(ctx, request.Wasm) + } + return nil, wasmvmtypes.Unknown{} +} + +func NewCustomQueryHandler(queryPlugins wasmkeeper.QueryPlugins, aclKeeper aclkeeper.Keeper) wasmkeeper.WasmVMQueryHandler { + return CustomQueryHandler{ + QueryPlugins: queryPlugins, + aclKeeper: aclKeeper, + } +} + +func CustomQueryHandlerDecorator(aclKeeper aclkeeper.Keeper, customQueryPlugin QueryPlugin) func(wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler { + // validate stuff, otherwise use default handler + return func(old wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler { + queryPlugins, ok := old.(wasmkeeper.QueryPlugins) + if !ok { + panic("Invalid query plugins") + } + + queryPlugins = queryPlugins.Merge(&wasmkeeper.QueryPlugins{ + Custom: CustomQuerier(&customQueryPlugin), + }) + return NewCustomQueryHandler(queryPlugins, aclKeeper) + } +} diff --git a/wasmbinding/test/encoder_test.go b/wasmbinding/test/encoder_test.go index 8c8d17b427..ad8a07580c 100644 --- a/wasmbinding/test/encoder_test.go +++ b/wasmbinding/test/encoder_test.go @@ -5,10 +5,11 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/sei-protocol/sei-chain/wasmbinding" "github.com/sei-protocol/sei-chain/wasmbinding/bindings" + dexwasm "github.com/sei-protocol/sei-chain/x/dex/client/wasm" "github.com/sei-protocol/sei-chain/x/dex/types" dextypes "github.com/sei-protocol/sei-chain/x/dex/types" + tokenfactorywasm "github.com/sei-protocol/sei-chain/x/tokenfactory/client/wasm" tokenfactorytypes "github.com/sei-protocol/sei-chain/x/tokenfactory/types" "github.com/stretchr/testify/require" ) @@ -36,13 +37,9 @@ func TestEncodePlaceOrder(t *testing.T) { Funds: []sdk.Coin{fund}, ContractAddr: TEST_TARGET_CONTRACT, } - serialized, _ := json.Marshal(msg) - msgData := wasmbinding.SeiWasmMessage{ - PlaceOrders: serialized, - } - serializedMsg, _ := json.Marshal(msgData) + serializedMsg, _ := json.Marshal(msg) - decodedMsgs, err := wasmbinding.CustomEncoder(contractAddr, serializedMsg) + decodedMsgs, err := dexwasm.EncodeDexPlaceOrders(serializedMsg, contractAddr) require.NoError(t, err) require.Equal(t, 1, len(decodedMsgs)) typedDecodedMsg, ok := decodedMsgs[0].(*dextypes.MsgPlaceOrders) @@ -65,13 +62,9 @@ func TestDecodeOrderCancellation(t *testing.T) { }, ContractAddr: TEST_TARGET_CONTRACT, } - serialized, _ := json.Marshal(msg) - msgData := wasmbinding.SeiWasmMessage{ - CancelOrders: serialized, - } - serializedMsg, _ := json.Marshal(msgData) + serializedMsg, _ := json.Marshal(msg) - decodedMsgs, err := wasmbinding.CustomEncoder(contractAddr, serializedMsg) + decodedMsgs, err := dexwasm.EncodeDexCancelOrders(serializedMsg, contractAddr) require.NoError(t, err) require.Equal(t, 1, len(decodedMsgs)) typedDecodedMsg, ok := decodedMsgs[0].(*dextypes.MsgCancelOrders) @@ -94,13 +87,9 @@ func TestEncodeCreateDenom(t *testing.T) { msg := bindings.CreateDenom{ Subdenom: "subdenom", } - serialized, _ := json.Marshal(msg) - msgData := wasmbinding.SeiWasmMessage{ - CreateDenom: serialized, - } - serializedMsg, _ := json.Marshal(msgData) + serializedMsg, _ := json.Marshal(msg) - decodedMsgs, err := wasmbinding.CustomEncoder(contractAddr, serializedMsg) + decodedMsgs, err := tokenfactorywasm.EncodeTokenFactoryCreateDenom(serializedMsg, contractAddr) require.NoError(t, err) require.Equal(t, 1, len(decodedMsgs)) typedDecodedMsg, ok := decodedMsgs[0].(*tokenfactorytypes.MsgCreateDenom) @@ -118,13 +107,9 @@ func TestEncodeMint(t *testing.T) { msg := bindings.MintTokens{ Amount: sdk.Coin{Amount: sdk.NewInt(100), Denom: "subdenom"}, } - serialized, _ := json.Marshal(msg) - msgData := wasmbinding.SeiWasmMessage{ - MintTokens: serialized, - } - serializedMsg, _ := json.Marshal(msgData) + serializedMsg, _ := json.Marshal(msg) - decodedMsgs, err := wasmbinding.CustomEncoder(contractAddr, serializedMsg) + decodedMsgs, err := tokenfactorywasm.EncodeTokenFactoryMint(serializedMsg, contractAddr) require.NoError(t, err) require.Equal(t, 1, len(decodedMsgs)) typedDecodedMsg, ok := decodedMsgs[0].(*tokenfactorytypes.MsgMint) @@ -142,13 +127,9 @@ func TestEncodeBurn(t *testing.T) { msg := bindings.BurnTokens{ Amount: sdk.Coin{Amount: sdk.NewInt(10), Denom: "subdenom"}, } - serialized, _ := json.Marshal(msg) - msgData := wasmbinding.SeiWasmMessage{ - BurnTokens: serialized, - } - serializedMsg, _ := json.Marshal(msgData) + serializedMsg, _ := json.Marshal(msg) - decodedMsgs, err := wasmbinding.CustomEncoder(contractAddr, serializedMsg) + decodedMsgs, err := tokenfactorywasm.EncodeTokenFactoryBurn(serializedMsg, contractAddr) require.NoError(t, err) require.Equal(t, 1, len(decodedMsgs)) typedDecodedMsg, ok := decodedMsgs[0].(*tokenfactorytypes.MsgBurn) @@ -167,13 +148,9 @@ func TestEncodeChangeAdmin(t *testing.T) { Denom: "factory/sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw/subdenom", NewAdminAddress: "sei1hjfwcza3e3uzeznf3qthhakdr9juetl7g6esl4", } - serialized, _ := json.Marshal(msg) - msgData := wasmbinding.SeiWasmMessage{ - ChangeAdmin: serialized, - } - serializedMsg, _ := json.Marshal(msgData) + serializedMsg, _ := json.Marshal(msg) - decodedMsgs, err := wasmbinding.CustomEncoder(contractAddr, serializedMsg) + decodedMsgs, err := tokenfactorywasm.EncodeTokenFactoryChangeAdmin(serializedMsg, contractAddr) require.NoError(t, err) require.Equal(t, 1, len(decodedMsgs)) typedDecodedMsg, ok := decodedMsgs[0].(*tokenfactorytypes.MsgChangeAdmin) diff --git a/wasmbinding/test/message_handler_test.go b/wasmbinding/test/message_handler_test.go new file mode 100644 index 0000000000..20dd685c6e --- /dev/null +++ b/wasmbinding/test/message_handler_test.go @@ -0,0 +1,131 @@ +package wasmbinding + +import ( + "encoding/json" + "testing" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/accesscontrol" + acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/sei-protocol/sei-chain/app" + "github.com/sei-protocol/sei-chain/wasmbinding" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/proto/tendermint/types" +) + +type MockMessenger struct{} + +func (m MockMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + return []sdk.Event{{ + Type: "test", + Attributes: []abci.EventAttribute{}, + }}, nil, nil +} + +func TestMessageHandlerDependencyDecorator(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + defaultEncoders := wasmkeeper.DefaultEncoders(app.AppCodec(), app.TransferKeeper) + dependencyDecorator := wasmbinding.NewSDKMessageDependencyDecorator(MockMessenger{}, app.AccessControlKeeper, defaultEncoders) + testContext := app.NewContext(false, types.Header{}) + + // setup bank send message with aclkeeper + app.AccessControlKeeper.SetResourceDependencyMapping(testContext, accesscontrol.MessageDependencyMapping{ + MessageKey: string(acltypes.GenerateMessageKey(&banktypes.MsgSend{})), + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + DynamicEnabled: false, + }) + + // setup the wasm contract's dependency mapping + app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_ANY, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + + events, _, _ := dependencyDecorator.DispatchMsg(testContext, contractAddr, "test", wasmvmtypes.CosmosMsg{ + Bank: &wasmvmtypes.BankMsg{ + Send: &wasmvmtypes.SendMsg{ + ToAddress: "sdfasdf", + Amount: []wasmvmtypes.Coin{ + { + Denom: "usei", + Amount: "12345", + }, + }, + }, + }, + }) + // we should have received the test event + require.Equal(t, []sdk.Event{ + { + Type: "test", + Attributes: []abci.EventAttribute{}, + }, + }, events) + + app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_KV, + IdentifierTemplate: "otherIdentifier", + }, + acltypes.CommitAccessOp(), + }, + }) + + _, _, err = dependencyDecorator.DispatchMsg(testContext, contractAddr, "test", wasmvmtypes.CosmosMsg{ + Bank: &wasmvmtypes.BankMsg{ + Send: &wasmvmtypes.SendMsg{ + ToAddress: "sdfasdf", + Amount: []wasmvmtypes.Coin{ + { + Denom: "usei", + Amount: "12345", + }, + }, + }, + }, + }) + // we expect an error now + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) + + // reenable wasm mapping that's correct + app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_KV, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + // lets try with a message that wont decode properly + _, _, err = dependencyDecorator.DispatchMsg(testContext, contractAddr, "test", wasmvmtypes.CosmosMsg{ + Custom: json.RawMessage{}, + }) + require.Error(t, wasmtypes.ErrUnknownMsg, err) +} diff --git a/wasmbinding/test/query_test.go b/wasmbinding/test/query_test.go index 7d240fe0f2..2d6891f030 100644 --- a/wasmbinding/test/query_test.go +++ b/wasmbinding/test/query_test.go @@ -6,8 +6,12 @@ import ( "testing" "time" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/accesscontrol" + acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types" "github.com/sei-protocol/sei-chain/app" "github.com/sei-protocol/sei-chain/wasmbinding" dexcache "github.com/sei-protocol/sei-chain/x/dex/cache" @@ -23,6 +27,7 @@ import ( oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" oracleutils "github.com/sei-protocol/sei-chain/x/oracle/utils" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/proto/tendermint/types" ) func SetupWasmbindingTest(t *testing.T) (*app.TestWrapper, func(ctx sdk.Context, request json.RawMessage) ([]byte, error)) { @@ -231,3 +236,391 @@ func TestWasmGetEpoch(t *testing.T) { require.Equal(t, time.Unix(12345, 0).UTC(), epoch.CurrentEpochStartTime) require.Equal(t, int64(40), epoch.CurrentEpochHeight) } + +func MockQueryPlugins() wasmkeeper.QueryPlugins { + return wasmkeeper.QueryPlugins{ + Bank: func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error) { return []byte{}, nil }, + IBC: func(ctx sdk.Context, caller sdk.AccAddress, request *wasmvmtypes.IBCQuery) ([]byte, error) { + return []byte{}, nil + }, + Custom: func(ctx sdk.Context, request json.RawMessage) ([]byte, error) { + println("test") + return []byte{}, nil + }, + Stargate: func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) { return []byte{}, nil }, + Staking: func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { return []byte{}, nil }, + Wasm: func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error) { return []byte{}, nil }, + } +} + +func TestQueryHandlerDependencyDecoratorBank(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_BANK, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Bank: &wasmvmtypes.BankQuery{}, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_KV_DEX, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Bank: &wasmvmtypes.BankQuery{}, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} + +func TestQueryHandlerDependencyDecoratorIBC(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_ANY, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + IBC: &wasmvmtypes.IBCQuery{}, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_KV, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + IBC: &wasmvmtypes.IBCQuery{}, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} + +func TestQueryHandlerDependencyDecoratorStaking(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_STAKING, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Staking: &wasmvmtypes.StakingQuery{}, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_KV_DEX, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Staking: &wasmvmtypes.StakingQuery{}, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} + +func TestQueryHandlerDependencyDecoratorStargate(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_ANY, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Stargate: &wasmvmtypes.StargateQuery{}, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_KV, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Stargate: &wasmvmtypes.StargateQuery{}, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} + +func TestQueryHandlerDependencyDecoratorWasm(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_WASM, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Wasm: &wasmvmtypes.WasmQuery{}, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_WRITE, + ResourceType: accesscontrol.ResourceType_KV_DEX, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Wasm: &wasmvmtypes.WasmQuery{}, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} + +func TestQueryHandlerDependencyDecoratorDex(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_DEX, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + customQuery, err := json.Marshal(wasmbinding.SeiQueryWrapper{ + Route: wasmbinding.DexRoute, + }) + require.NoError(t, err) + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Custom: customQuery, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_ORACLE, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Custom: customQuery, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} + +func TestQueryHandlerDependencyDecoratorOracle(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_ORACLE, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + customQuery, err := json.Marshal(wasmbinding.SeiQueryWrapper{ + Route: wasmbinding.OracleRoute, + }) + require.NoError(t, err) + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Custom: customQuery, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_BANK, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Custom: customQuery, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} + +func TestQueryHandlerDependencyDecoratorEpoch(t *testing.T) { + app := app.Setup(false) + contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw") + require.NoError(t, err) + queryDecorator := wasmbinding.NewCustomQueryHandler(MockQueryPlugins(), app.AccessControlKeeper) + testContext := app.NewContext(false, types.Header{}) + + // setup the wasm contract's dependency mapping + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_EPOCH, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + customQuery, err := json.Marshal(wasmbinding.SeiQueryWrapper{ + Route: wasmbinding.EpochRoute, + }) + require.NoError(t, err) + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Custom: customQuery, + }) + require.NoError(t, err) + + err = app.AccessControlKeeper.SetWasmDependencyMapping(testContext, contractAddr, accesscontrol.WasmDependencyMapping{ + Enabled: true, + AccessOps: []accesscontrol.AccessOperation{ + { + AccessType: accesscontrol.AccessType_READ, + ResourceType: accesscontrol.ResourceType_KV_BANK, + IdentifierTemplate: "*", + }, + acltypes.CommitAccessOp(), + }, + }) + require.NoError(t, err) + + _, err = queryDecorator.HandleQuery(testContext, contractAddr, wasmvmtypes.QueryRequest{ + Custom: customQuery, + }) + require.Error(t, wasmbinding.ErrUnexpectedWasmDependency, err) +} diff --git a/wasmbinding/wasm.go b/wasmbinding/wasm.go index d6d6844578..2dee25d948 100644 --- a/wasmbinding/wasm.go +++ b/wasmbinding/wasm.go @@ -3,6 +3,10 @@ package wasmbinding import ( "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + aclkeeper "github.com/cosmos/cosmos-sdk/x/accesscontrol/keeper" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" dexwasm "github.com/sei-protocol/sei-chain/x/dex/client/wasm" dexkeeper "github.com/sei-protocol/sei-chain/x/dex/keeper" epochwasm "github.com/sei-protocol/sei-chain/x/epoch/client/wasm" @@ -15,17 +19,27 @@ func RegisterCustomPlugins( oracle *oraclekeeper.Keeper, dex *dexkeeper.Keeper, epoch *epochkeeper.Keeper, + accountKeeper *authkeeper.AccountKeeper, + router wasmkeeper.MessageRouter, + channelKeeper wasmtypes.ChannelKeeper, + capabilityKeeper wasmtypes.CapabilityKeeper, + bankKeeper wasmtypes.Burner, + unpacker codectypes.AnyUnpacker, + portSource wasmtypes.ICS20TransferPortSource, + aclKeeper aclkeeper.Keeper, ) []wasmkeeper.Option { dexHandler := dexwasm.NewDexWasmQueryHandler(dex) oracleHandler := oraclewasm.NewOracleWasmQueryHandler(oracle) epochHandler := epochwasm.NewEpochWasmQueryHandler(epoch) wasmQueryPlugin := NewQueryPlugin(oracleHandler, dexHandler, epochHandler) - queryPluginOpt := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ - Custom: CustomQuerier(wasmQueryPlugin), - }) + queryOpt := wasmkeeper.WithQueryHandlerDecorator(CustomQueryHandlerDecorator(aclKeeper, *wasmQueryPlugin)) + messengerHandlerOpt := wasmkeeper.WithMessageHandler( + CustomMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, unpacker, portSource, aclKeeper), + ) return []wasm.Option{ - queryPluginOpt, + queryOpt, + messengerHandlerOpt, } }