Skip to content

Commit

Permalink
[acl] pass message info when getting wasm access ops (sei-protocol#130)
Browse files Browse the repository at this point in the history
## Describe your changes and provide context
Callers to `GetWasmDependencyAccessOps` now needs to pass in a
`WasmMessageInfo` instead of raw message bytes so that the function can
identify whether the message is a query or execute. This PR introduced
helpers under `types/wasm.go` to help creating such `WasmMessageInfo`.
`GetWasmDependencyAccessOps` would then extend the access operations
list with the appropriate specific access ops.

Next steps:
- Add separate selector enums for query reference and execute reference
- Handle new selector enums by passing the current WasmMessageInfo

## Testing performed to validate your change
unit tests
  • Loading branch information
codchen authored Jan 12, 2023
1 parent fd8431b commit 0e0e245
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 23 deletions.
38 changes: 27 additions & 11 deletions x/accesscontrol/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (k Keeper) GetRawWasmDependencyMapping(ctx sdk.Context, contractAddress sdk
return &dependencyMapping, nil
}

func (k Keeper) GetWasmDependencyAccessOps(ctx sdk.Context, contractAddress sdk.AccAddress, senderBech string, msgBody []byte, circularDepLookup ContractReferenceLookupMap) ([]acltypes.AccessOperation, error) {
func (k Keeper) GetWasmDependencyAccessOps(ctx sdk.Context, contractAddress sdk.AccAddress, senderBech string, msgInfo *types.WasmMessageInfo, circularDepLookup ContractReferenceLookupMap) ([]acltypes.AccessOperation, error) {
uniqueIdentifier := contractAddress.String()
if _, ok := circularDepLookup[uniqueIdentifier]; ok {
// we've already seen this contract, we should simply return synchronous access Ops
Expand All @@ -156,15 +156,28 @@ func (k Keeper) GetWasmDependencyAccessOps(ctx sdk.Context, contractAddress sdk.
return nil, err
}

// TODO: extend base access op with message-specific ops once message type identifier is passed in
selectedAccessOps, err := k.BuildSelectorOps(ctx, contractAddress, dependencyMapping.BaseAccessOps, senderBech, msgBody, circularDepLookup)
accessOps := dependencyMapping.BaseAccessOps
specificAccessOpsMapping := []*acltypes.WasmAccessOperations{}
if msgInfo.MessageType == acltypes.WasmMessageSubtype_EXECUTE && len(dependencyMapping.ExecuteAccessOps) > 0 {
specificAccessOpsMapping = dependencyMapping.ExecuteAccessOps
} else if msgInfo.MessageType == acltypes.WasmMessageSubtype_QUERY && len(dependencyMapping.QueryAccessOps) > 0 {
specificAccessOpsMapping = dependencyMapping.QueryAccessOps
}
for _, specificAccessOps := range specificAccessOpsMapping {
if specificAccessOps.MessageName == msgInfo.MessageName {
accessOps = append(accessOps, specificAccessOps.WasmOperations...)
break
}
}

selectedAccessOps, err := k.BuildSelectorOps(ctx, contractAddress, accessOps, senderBech, msgInfo, circularDepLookup)
if err != nil {
return nil, err
}
return selectedAccessOps, nil
}

func (k Keeper) BuildSelectorOps(ctx sdk.Context, contractAddr sdk.AccAddress, accessOps []*acltypes.WasmAccessOperation, senderBech string, msgBody []byte, circularDepLookup ContractReferenceLookupMap) ([]acltypes.AccessOperation, error) {
func (k Keeper) BuildSelectorOps(ctx sdk.Context, contractAddr sdk.AccAddress, accessOps []*acltypes.WasmAccessOperation, senderBech string, msgInfo *types.WasmMessageInfo, circularDepLookup ContractReferenceLookupMap) ([]acltypes.AccessOperation, error) {
selectedAccessOps := types.NewEmptyAccessOperationSet()
// when we build selector ops here, we want to generate "*" if the proper fields aren't present
// if size of circular dep map > 1 then it means we're in a contract reference
Expand All @@ -179,7 +192,7 @@ accessOpLoop:
if err != nil {
return nil, err
}
data, err := op.Apply(msgBody)
data, err := op.Apply(msgInfo.MessageFullBody)
if err != nil {
if withinContractReference {
opWithSelector.Operation.IdentifierTemplate = "*"
Expand All @@ -198,7 +211,7 @@ accessOpLoop:
if err != nil {
return nil, err
}
data, err := op.Apply(msgBody)
data, err := op.Apply(msgInfo.MessageFullBody)
if err != nil {
if withinContractReference {
opWithSelector.Operation.IdentifierTemplate = "*"
Expand All @@ -222,7 +235,7 @@ accessOpLoop:
if err != nil {
return nil, err
}
data, err := op.Apply(msgBody)
data, err := op.Apply(msgInfo.MessageFullBody)
if err != nil {
if withinContractReference {
opWithSelector.Operation.IdentifierTemplate = "*"
Expand Down Expand Up @@ -275,7 +288,7 @@ accessOpLoop:
if err != nil {
return nil, err
}
_, err = op.Apply(msgBody)
_, err = op.Apply(msgInfo.MessageFullBody)
// if we are in a contract reference, we have to assume that this is necessary
// TODO: after partitioning changes are merged, the MESSAGE_CONDITIONAL can be deprecated in favor of partitioned deps
if err != nil && !withinContractReference {
Expand All @@ -297,8 +310,11 @@ accessOpLoop:
// TODO: add a circular dependency check here to ignore if we've already seen this contract/identifier in our reference chain
// for now, we will just pass in the same message body, this needs to be changed later though
// TODO: build new msgbody for the new contract execute / query msg in later milestone tasks
emptyJSON := []byte("{}")
wasmDeps, err := k.GetWasmDependencyAccessOps(ctx, interContractAddress, contractAddr.String(), emptyJSON, circularDepLookup)
emptyJSON := []byte("{\"\":{}}")
// TODO: add separate enum for query reference vs execute reference and build msgInfo according to whether
// the reference is a query one or an execute one. For now defaulting to execute.
emptyMsgInfo, _ := types.NewExecuteMessageInfo(emptyJSON)
wasmDeps, err := k.GetWasmDependencyAccessOps(ctx, interContractAddress, contractAddr.String(), emptyMsgInfo, circularDepLookup)

if err != nil {
// if we have an error fetching the dependency mapping or the mapping is disabled, we want to use the synchronous mappings instead
Expand All @@ -313,7 +329,7 @@ accessOpLoop:
}
selectedAccessOps.Add(*opWithSelector.Operation)
}
// TODO: add logic to deduplicate access operations that are the same

return selectedAccessOps.ToSlice(), nil
}

Expand Down
122 changes: 110 additions & 12 deletions x/accesscontrol/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,92 @@ func TestWasmDependencyMapping(t *testing.T) {
require.Error(t, aclkeeper.ErrWasmDependencyMappingNotFound, err)
}

func TestWasmDependencyMappingWithExecuteMsgInfo(t *testing.T) {
app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

wasmContractAddresses := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(30000000))
wasmContractAddress := wasmContractAddresses[0]
wasmMapping := acltypes.WasmDependencyMapping{
BaseAccessOps: []*acltypes.WasmAccessOperation{
{
Operation: types.CommitAccessOp(),
SelectorType: acltypes.AccessOperationSelectorType_NONE,
},
},
ExecuteAccessOps: []*acltypes.WasmAccessOperations{
{
MessageName: "test",
WasmOperations: []*acltypes.WasmAccessOperation{
{
Operation: &acltypes.AccessOperation{ResourceType: acltypes.ResourceType_KV, AccessType: acltypes.AccessType_WRITE, IdentifierTemplate: "someResource"},
SelectorType: acltypes.AccessOperationSelectorType_NONE,
},
},
},
},
ContractAddress: wasmContractAddress.String(),
}
// set the dependency mapping
err := app.AccessControlKeeper.SetWasmDependencyMapping(ctx, wasmMapping)
require.NoError(t, err)
// test getting the access operations
info, _ := types.NewExecuteMessageInfo(
[]byte("{\"test\":{}}"),
)
ops, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(ctx, wasmContractAddress, "", info, make(aclkeeper.ContractReferenceLookupMap))
require.NoError(t, err)

expectedAccessOps := []acltypes.AccessOperation{
{ResourceType: acltypes.ResourceType_KV, AccessType: acltypes.AccessType_WRITE, IdentifierTemplate: "someResource"},
*types.CommitAccessOp(),
}
require.Equal(t, ops, expectedAccessOps)
}

func TestWasmDependencyMappingWithQueryMsgInfo(t *testing.T) {
app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

wasmContractAddresses := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(30000000))
wasmContractAddress := wasmContractAddresses[0]
wasmMapping := acltypes.WasmDependencyMapping{
BaseAccessOps: []*acltypes.WasmAccessOperation{
{
Operation: types.CommitAccessOp(),
SelectorType: acltypes.AccessOperationSelectorType_NONE,
},
},
QueryAccessOps: []*acltypes.WasmAccessOperations{
{
MessageName: "test",
WasmOperations: []*acltypes.WasmAccessOperation{
{
Operation: &acltypes.AccessOperation{ResourceType: acltypes.ResourceType_KV, AccessType: acltypes.AccessType_WRITE, IdentifierTemplate: "someResource"},
SelectorType: acltypes.AccessOperationSelectorType_NONE,
},
},
},
},
ContractAddress: wasmContractAddress.String(),
}
// set the dependency mapping
err := app.AccessControlKeeper.SetWasmDependencyMapping(ctx, wasmMapping)
require.NoError(t, err)
// test getting the access operations
info, _ := types.NewQueryMessageInfo(
[]byte("{\"test\":{}}"),
)
ops, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(ctx, wasmContractAddress, "", info, make(aclkeeper.ContractReferenceLookupMap))
require.NoError(t, err)

expectedAccessOps := []acltypes.AccessOperation{
{ResourceType: acltypes.ResourceType_KV, AccessType: acltypes.AccessType_WRITE, IdentifierTemplate: "someResource"},
*types.CommitAccessOp(),
}
require.Equal(t, ops, expectedAccessOps)
}

func TestResetWasmDependencyMapping(t *testing.T) {
app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
Expand Down Expand Up @@ -206,11 +292,12 @@ func TestWasmDependencyMappingWithJQSelector(t *testing.T) {
require.NoError(t, err)
require.Equal(t, wasmMapping, *mapping)
// test getting a dependency mapping with selector
info, _ := types.NewExecuteMessageInfo([]byte("{\"send\":{\"from\":\"bob\",\"amount\":10}}"))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
"",
[]byte("{\"send\":{\"from\":\"bob\",\"amount\":10}}"),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -262,11 +349,12 @@ func TestWasmDependencyMappingWithJQBech32Selector(t *testing.T) {
require.Equal(t, wasmMapping, *mapping)
// test getting a dependency mapping with selector
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
"",
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -317,11 +405,12 @@ func TestWasmDependencyMappingWithJQLengthPrefixedAddressSelector(t *testing.T)
require.Equal(t, wasmMapping, *mapping)
// test getting a dependency mapping with selector
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
"",
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -362,11 +451,12 @@ func TestWasmDependencyMappingWithSenderBech32Selector(t *testing.T) {
require.Equal(t, wasmMapping, *mapping)
// test getting a dependency mapping with selector
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
wasmBech32,
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -407,11 +497,12 @@ func TestWasmDependencyMappingWithSenderLengthPrefixedSelector(t *testing.T) {
require.Equal(t, wasmMapping, *mapping)
// test getting a dependency mapping with selector
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
wasmBech32,
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -462,11 +553,12 @@ func TestWasmDependencyMappingWithConditionalSelector(t *testing.T) {
require.Equal(t, wasmMapping, *mapping)
// test getting a dependency mapping with selector
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
wasmBech32,
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -509,11 +601,12 @@ func TestWasmDependencyMappingWithConstantSelector(t *testing.T) {
require.Equal(t, wasmMapping, *mapping)
// test getting a dependency mapping with selector
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
wasmBech32,
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -601,11 +694,12 @@ func TestWasmDependencyMappingWithContractReferenceSelector(t *testing.T) {

// test getting a dependency mapping with selector that expands the inter-contract reference into the contract's dependencies
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", thirdAddr.String())))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
thirdAddr.String(),
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", thirdAddr.String())),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -720,11 +814,12 @@ func TestWasmDependencyMappingWithContractReferenceSelectorMultipleReferences(t

// test getting a dependency mapping with selector that expands the inter-contract reference into the contract's dependencies
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", otherAddr.String())))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
otherAddr.String(),
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", otherAddr.String())),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -848,11 +943,12 @@ func TestWasmDependencyMappingWithContractReferenceSelectorCircularDependency(t

// test getting a dependency mapping with selector that expands the inter-contract reference into the contract's dependencies
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", otherAddr.String())))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
otherAddr.String(),
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", otherAddr.String())),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -915,11 +1011,12 @@ func TestWasmDependencyMappingWithContractReferenceDisabled(t *testing.T) {

// test getting a dependency mapping with selector that expands the inter-contract reference into the contract's dependencies
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
wasmContractAddresses[2].String(),
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down Expand Up @@ -968,11 +1065,12 @@ func TestWasmDependencyMappingWithContractReferenceDNE(t *testing.T) {

// test getting a dependency mapping with selector that expands the inter-contract reference into the contract's dependencies
require.NoError(t, err)
info, _ := types.NewExecuteMessageInfo([]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)))
deps, err := app.AccessControlKeeper.GetWasmDependencyAccessOps(
ctx,
wasmContractAddress,
wasmContractAddresses[2].String(),
[]byte(fmt.Sprintf("{\"send\":{\"address\":\"%s\",\"amount\":10}}", wasmBech32)),
info,
make(aclkeeper.ContractReferenceLookupMap),
)
require.NoError(t, err)
Expand Down
8 changes: 8 additions & 0 deletions x/accesscontrol/types/message_dependency_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var (
ErrNoCommitAccessOp = fmt.Errorf("MessageDependencyMapping doesn't terminate with AccessType_COMMIT")
ErrEmptyIdentifierString = fmt.Errorf("IdentifierTemplate cannot be an empty string")
ErrNonLeafResourceTypeWithIdentifier = fmt.Errorf("IdentifierTemplate must be '*' for non leaf resource types")
ErrDuplicateWasmMethodName = fmt.Errorf("A method name is defined multiple times in specific access operation list")
)

type MessageKey string
Expand Down Expand Up @@ -131,5 +132,12 @@ func ValidateWasmDependencyMapping(mapping acltypes.WasmDependencyMapping) error
if lastAccessOp.Operation.AccessType != acltypes.AccessType_COMMIT {
return ErrNoCommitAccessOp
}
seenMessageNames := map[string]struct{}{}
for _, ops := range append(mapping.ExecuteAccessOps, mapping.QueryAccessOps...) {
if _, ok := seenMessageNames[ops.MessageName]; ok {
return ErrDuplicateWasmMethodName
}
seenMessageNames[ops.MessageName] = struct{}{}
}
return nil
}
Loading

0 comments on commit 0e0e245

Please sign in to comment.