-
Notifications
You must be signed in to change notification settings - Fork 823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XCM: Deny barrier checks for nested XCMs with specific instructions to be executed on the local chain #7200
base: master
Are you sure you want to change the base?
Conversation
2c6325f
to
9c80770
Compare
/cmd fmt |
/cmd fmt |
Resolves (partially): #7148 Depends on: #7169, #7200 # Description For context and additional information, please refer to #7148 and #7200. # TODOs * [x] Rebase #7169 and #7200 * [x] Evaluate PoC described on #7200 | POC | Top-Level Denial | Nested Denial | Try `Allow` | Remark | |--------------------------------|--------------------|--------------------|--------------------|----------------------------------------------------------------------------------| | `DenyThenTry` | ✅ | ❌ | ✅ | Blocks top-level instructions only. | | `RecursiveDenyThenTry` | ✅ | ✅ | ✅ | Blocks both top-level and nested instructions. | | `DenyInstructionsWithXcm` | ❌ | ✅ | ❌ | Focuses on nested instructions, requires additional checks for top-level denial. | | `DenyFirstInstructionsWithXcm` | ✅ | ✅ | ❌ | Prioritises top-level denial before recursive checks. | --------- Co-authored-by: ron <[email protected]> Co-authored-by: Branislav Kontur <[email protected]> Co-authored-by: Francisco Aguirre <[email protected]> Co-authored-by: command-bot <> Co-authored-by: Clara van Staden <[email protected]> Co-authored-by: Adrian Catangiu <[email protected]> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
// `DenyThenTry`: Top-level=Deny, Nested=Allow, TryAllow=Yes | ||
assert_barrier::<DenyThenTry<Denies, AllowAll>>(Err(ProcessMessageError::Unsupported), Ok(())); | ||
|
||
// `RecursiveDenyThenTry`: Top-level=Deny, Nested=Deny, TryAllow=Yes | ||
assert_barrier::<RecursiveDenyThenTry<Denies, AllowAll>>( | ||
Err(ProcessMessageError::Unsupported), | ||
Err(ProcessMessageError::Unsupported), | ||
); | ||
|
||
// `DenyInstructionsWithXcm`: Top-level=Allow, Nested=Deny, TryAllow=No | ||
assert_deny_barrier::<DenyInstructionsWithXcm<Denies>>( | ||
Ok(()), | ||
Err(ProcessMessageError::Unsupported), | ||
); | ||
|
||
// `DenyFirstInstructionsWithXcm`: Top-level=Deny, Nested=Deny, TryAllow=No | ||
assert_deny_barrier::<DenyFirstInstructionsWithXcm<Denies>>( | ||
Err(ProcessMessageError::Unsupported), | ||
Err(ProcessMessageError::Unsupported), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
POC Evaluation
- Redefine
DenyThenTry
asRecursiveDenyThenTry
: blocks both top-level and nested instructions, before trying to allow execution. - Create a new
DenyFirstInstructionsWithXcm
: blocks both top-level and nested instructions. - Establish a helper
DenyInstructionsWithXcm
, handles nested denies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to match the latest implementations:
PoC | Top-Level Denial | Nested Denial | Try Allow | Remark |
---|---|---|---|---|
DenyThenTry<Deny, Allow> |
✅ | ❌ | ✅ | Blocks only top-level instructions, allowing nested ones unless denied elsewhere. |
DenyThenTry<DenyRecursively<Deny>, Allow> |
✅ | ✅ | ✅ | Blocks both top-level and nested local instructions, then applies the allow condition. |
DenyRecursively<Deny> |
✅ | ✅ | ❌ | Blocks both top-level and nested local instructions, without attempting to allow execution. |
Ok(()) | ||
}) | ||
// Fallback safety in case of an unexpected failure. | ||
.unwrap_or(Ok(()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want to propagate this error? I'm confused by the double error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This unwrap_or(Ok(()))
fallback pattern is used elsewhere in the codebase (see search results). While it's intended as a safety measure, let's discuss whether a more robust approach might be preferable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Result<Result<ControlFlow<()>, ProcessMessageError>, ProcessMessageError>
could be flattened to Result<ControlFlow<()>, ProcessMessageError>
There are 3 return "branches" this fn should take:
Err(ProcessMessageError::StackLimitReached)
in case of too much recursion -> message denied by barrierSelf::deny_execution(origin, xcm.inner_mut(), max_weight, properties)?;
-> shortcircuits withErr(deny_reason)
if current recursion layer returns Err.Ok(ControlFlow::Continue(()))
otherwise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored the code to make it more readable, pls check.
/// | ||
/// Note: Ensures that restricted instructions do not execute on the local chain, enforcing stricter | ||
/// execution policies, while allowing remote chains to enforce their own rules. | ||
pub struct DenyLocalInstructions<Inner>(PhantomData<Inner>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't DenyRecursively
a better name? I understand that this only recurses to the nested xcms in SetAppendix, SetErrorHandler and ExecuteWithOrigin and not InitiateTransfer for example, but we could just clarify that in the docs.
We never deny stuff inside a remote_xcm
. We always expect that to be filtered by the destination's barrier
The current name doesn't even mention recursion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DenyRecursively
might be clearer. We discussed DenyLocalInstructions
with @acatangiu and @bkontur, focusing on local instructions, but I'm happy to revisit the name if the team prefers DenyRecursively
(or another suggestion).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, DenyLocalInstructions
is confusing when read at the barrier config level. DenyRecursively
is more explicit in its intent.
I support renaming to DenyRecursively
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to DenyRecursively
|
||
// Dummy filter which wraps `DenyExecution` on `ShouldExecution` | ||
struct DenyWrapper<Deny: ShouldExecute>(PhantomData<Deny>); | ||
impl<Deny: ShouldExecute> DenyExecution for DenyWrapper<Deny> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this? It's kind of confusing to just call should_execute
but call the method deny_execution
. Beats the purpose of having two traits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DenyWrapper
is a testing helper that wraps the ShouldExecute
trait. With the introduction of the DenyExecution
trait in #7169, I added this wrapper to simplify tests. I'll investigate cleaner ways to handle this testing logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to Executable
to hopefully reduce the confusion around its purpose.
Co-authored-by: Francisco Aguirre <[email protected]>
/// | ||
/// Note: Ensures that restricted instructions do not execute on the local chain, enforcing stricter | ||
/// execution policies, while allowing remote chains to enforce their own rules. | ||
pub struct DenyLocalInstructions<Inner>(PhantomData<Inner>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, DenyLocalInstructions
is confusing when read at the barrier config level. DenyRecursively
is more explicit in its intent.
I support renaming to DenyRecursively
Ok(()) | ||
}) | ||
// Fallback safety in case of an unexpected failure. | ||
.unwrap_or(Ok(()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Result<Result<ControlFlow<()>, ProcessMessageError>, ProcessMessageError>
could be flattened to Result<ControlFlow<()>, ProcessMessageError>
There are 3 return "branches" this fn should take:
Err(ProcessMessageError::StackLimitReached)
in case of too much recursion -> message denied by barrierSelf::deny_execution(origin, xcm.inner_mut(), max_weight, properties)?;
-> shortcircuits withErr(deny_reason)
if current recursion layer returns Err.Ok(ControlFlow::Continue(()))
otherwise
prdoc/pr_7200.prdoc
Outdated
This PR addresses partially #7148 (Problem 2) and ensures the proper checking | ||
of nested local instructions. It introduces a new barrier - | ||
`DenyLocalInstructions` - to provide more refined control over instruction | ||
denial. The main change is the replacement of `DenyThenTry<Deny, Allow>` with | ||
`DenyThenTry<DenyLocalInstructions<Deny>, Allow>` which handles both top-level | ||
and nested local instructions by applying allow condition after denial. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please reword this to be clear to the reader without having to go read github issues (don't reference issue numbers).
You can reuse most of the documentation of the new DenyRecursively
type here and should be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, pls check.
Co-authored-by: Adrian Catangiu <[email protected]>
} | ||
*count = count.saturating_add(1); | ||
Some(()) | ||
}).flatten().ok_or(ProcessMessageError::StackLimitReached)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Resolves (partially): #7148
Depends on: #7169
Description
This PR addresses partially #7148 (Problem 2) and ensures the proper checking of nested local instructions. It introduces a new barrier -
DenyRecursively
- to provide more refined control over instruction denial. The main change is the replacement ofDenyThenTry<Deny, Allow>
withDenyThenTry<DenyRecursively<Deny>, Allow>
which handles both top-level and nested local instructions by applying allow condition after denial.For context and additional information, please refer to Problem 2 - Barrier vs nested XCM validation.
TODO
DenyInstructionsWithXcm
.DenyThenTry
, so we wouldn’t needDenyInstructionsWithXcm
. However, this approach wouldn’t be as general.DenyInstructionsWithXcm::Inner
for the actualmessage
, so we don’t need duplication for top-level and nested (not sure, maybe be explicit is good thing) - see Problem2 - example. Instead of this:DenyInstructionsWithXcm
=>DenyRecursively
, more details at hereyrong:fix-for-deny-then-try
DenyThenTry<Deny, Allow>
=>DenyThenTry<DenyRecursively<Deny>, Allow>