Skip to content
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

Open
wants to merge 91 commits into
base: master
Choose a base branch
from

Conversation

bkontur
Copy link
Contributor

@bkontur bkontur commented Jan 16, 2025

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 of DenyThenTry<Deny, Allow> with DenyThenTry<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

  • Evaluate PoC, more details at poc(XCM): Deny Nested XCM Barriers #7351:
    • DenyNestedXcmInstructions: Keep it as it is and be explicit:
      1. Name the Deny barriers for the top level.
      2. Name the Deny barrier for nested with DenyInstructionsWithXcm.
    • DenyThenTry<DenyInstructionsWithXcm, Allow>: Alternatively, hard-code those three instructions in DenyThenTry, so we wouldn’t need DenyInstructionsWithXcm. However, this approach wouldn’t be as general.
    • DenyInstructionsWithXcmFor: Another possibility is to check DenyInstructionsWithXcm::Inner for the actual message, 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:
    DenyThenTry<
                 (
                                // Deny for top level XCM program 
                                DenyReserveTransferToRelayChain,
                                // Dedicated barrier for nested XCM programs
                                DenyInstructionsWithXcmFor<
                                                 // Repeat all Deny filters here 
                                                 DenyReserveTransferToRelayChain,
                                 >
                 ),
    
    we could just use:
    DenyThenTry<
                 (
                                // Dedicated barrier for XCM programs
                                DenyInstructionsWithXcmFor<
                                                 // Add all `Deny` filters here 
                                                 DenyReserveTransferToRelayChain,
                                                 ...
                                 >
                 ),
    
  • POC Evaluation
  • Consider better name DenyInstructionsWithXcm => DenyRecursively, more details at here
  • Clean-up and docs
  • Merge xcm: fix for DenyThenTry Barrier #7169 or rebase this branch on the top of yrong:fix-for-deny-then-try
  • Set for the runtimes where we use DenyThenTry<Deny, Allow> => DenyThenTry<DenyRecursively<Deny>, Allow>
  • Schedule sec.audit

@bkontur bkontur added T6-XCM This PR/Issue is related to XCM. C1-mentor A task where a mentor is available. Please indicate in the issue who the mentor could be. labels Jan 16, 2025
@bkontur bkontur force-pushed the bko-deny-nested-xcm-barrier branch from 2c6325f to 9c80770 Compare January 22, 2025 09:31
@bkontur bkontur added the C2-good-first-issue A task for a first time contributor to become familiar with the Polkadot-SDK. label Jan 22, 2025
@bkontur bkontur assigned bkontur and unassigned bkontur Jan 22, 2025
@raymondkfcheung raymondkfcheung self-assigned this Jan 23, 2025
@raymondkfcheung
Copy link

/cmd fmt

@raymondkfcheung
Copy link

/cmd fmt

raymondkfcheung and others added 2 commits January 31, 2025 13:41
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>
Comment on lines 887 to 906
// `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),
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POC Evaluation

  • Redefine DenyThenTry as RecursiveDenyThenTry: 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.

Copy link

@raymondkfcheung raymondkfcheung Feb 3, 2025

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(()))?;
Copy link
Contributor

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

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.

Copy link
Contributor

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 barrier
  • Self::deny_execution(origin, xcm.inner_mut(), max_weight, properties)?; -> shortcircuits with Err(deny_reason) if current recursion layer returns Err.
  • Ok(ControlFlow::Continue(())) otherwise

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>);
Copy link
Contributor

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.

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).

Copy link
Contributor

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to DenyRecursively

polkadot/xcm/xcm-builder/src/tests/barriers.rs Outdated Show resolved Hide resolved

// Dummy filter which wraps `DenyExecution` on `ShouldExecution`
struct DenyWrapper<Deny: ShouldExecute>(PhantomData<Deny>);
impl<Deny: ShouldExecute> DenyExecution for DenyWrapper<Deny> {
Copy link
Contributor

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

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.

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.

polkadot/xcm/xcm-builder/src/barriers.rs Outdated Show resolved Hide resolved
///
/// 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>);
Copy link
Contributor

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(()))?;
Copy link
Contributor

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 barrier
  • Self::deny_execution(origin, xcm.inner_mut(), max_weight, properties)?; -> shortcircuits with Err(deny_reason) if current recursion layer returns Err.
  • Ok(ControlFlow::Continue(())) otherwise

Comment on lines 6 to 11
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.
Copy link
Contributor

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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, pls check.

}
*count = count.saturating_add(1);
Some(())
}).flatten().ok_or(ProcessMessageError::StackLimitReached)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C1-mentor A task where a mentor is available. Please indicate in the issue who the mentor could be. C2-good-first-issue A task for a first time contributor to become familiar with the Polkadot-SDK. T6-XCM This PR/Issue is related to XCM.
Projects
Status: In-Review
Development

Successfully merging this pull request may close these issues.

[XCM] Investigate better support for filtering XCM programs with Barrier
4 participants