• Branislav Kontur's avatar
    Ensure xcm versions over bridge (on sending chains) (#2481) · 575b8f8d
    Branislav Kontur authored
    ## Summary
    
    This pull request proposes a solution for improved control of the
    versioned XCM flow over the bridge (across different consensus chains)
    and resolves the situation where the sending chain/consensus has already
    migrated to a higher XCM version than the receiving chain/consensus.
    
    ## Problem/Motivation
    
    The current flow over the bridge involves a transfer from AssetHubRococo
    (AHR) to BridgeHubRococo (BHR) to BridgeHubWestend (BHW) and finally to
    AssetHubWestend (AHW), beginning with a reserve-backed transfer on AHR.
    
    In this process:
    1. AHR sends XCM `ExportMessage` through `XcmpQueue`, incorporating XCM
    version checks using the `WrapVersion` feature, influenced by
    `pallet_xcm::SupportedVersion` (managed by
    `pallet_xcm::force_xcm_version` or version discovery).
    
    2. BHR handles the `ExportMessage` instruction, utilizing the latest XCM
    version. The `HaulBlobExporter` converts the inner XCM to
    [`VersionedXcm::from`](https://github.com/paritytech/polkadot-sdk/blob/63ac2471aa0210f0ac9903bdd7d8f9351f9a635f/polkadot/xcm/xcm-builder/src/universal_exports.rs#L465-L467),
    also using the latest XCM version.
    
    However, challenges arise:
    - Incompatibility when BHW uses a different version than BHR. For
    instance, if BHR migrates to **XCMv4** while BHW remains on **XCMv3**,
    BHR's `VersionedXcm::from` uses `VersionedXcm::V4` variant, causing
    encoding issues for BHW.
      ```
    	/// Just a simulation of possible error, which could happen on BHW
    	/// (this code is based on actual master without XCMv4)
    	let encoded = hex_literal::hex!("0400");
    	println!("{:?}", VersionedXcm::<()>::decode(&mut &encoded[..]));
    
    Err(Error { cause: None, desc: "Could not decode `VersionedXcm`, variant
    doesn't exist" })
      ``` 
    - Similar compatibility issues exist between AHR and AHW.
    
    ## Solution
    
    This pull request introduces the following solutions:
    
    1. **New trait `CheckVersion`** - added to the `xcm` module and exposing
    `pallet_xcm::SupportedVersion`. This enhancement allows checking the
    actual XCM version for desired destinations outside of the `pallet_xcm`
    module.
    
    2. **Version Check in `HaulBlobExporter`** uses `CheckVersion` to check
    known/configured destination versions, ensuring compatibility. For
    example, in the scenario mentioned, BHR can store the version `3` for
    BHW. If BHR is on XCMv4, it will attempt to downgrade the message to
    version `3` instead of using the latest version `4`.
    
    3. **Version Check in `pallet-xcm-bridge-hub-router`** - this check
    ensures compatibility with the real destination's XCM version,
    preventing the unnecessary sending of messages to the local bridge hub
    if versions are incompatible.
    
    These additions aim to improve the control and compatibility of XCM
    flows over the bridge and addressing issues related to version
    mismatches.
    
    ## Possible alternative solution
    
    _(More investigation is needed, and at the very least, it should extend
    to XCMv4/5. If this proves to be a viable option, I can open an RFC for
    XCM.)._
    
    Add the `XcmVersion` attribute to the `ExportMessage` so that the
    sending chain can determine, based on what is stored in
    `pallet_xcm::SupportedVersion`, the version the destination is using.
    This way, we may not need to handle the version in `HaulBlobExporter`.
    
    ```
    ExportMessage {
    	network: NetworkId,
    	destination: InteriorMultiLocation,
    	xcm: Xcm<()>
    	destination_xcm_version: Version, // <- new attritbute
    },
    ```
    
    ```
    pub trait ExportXcm {
            fn validate(
    		network: NetworkId,
    		channel: u32,
    		universal_source: &mut Option<InteriorMultiLocation>,
    		destination: &mut Option<InteriorMultiLocation>,
    		message: &mut Option<Xcm<()>>,
                    destination_xcm_version: Version, , // <- new attritbute
    	) -> SendResult<Self::Ticket>;
    ```
    
    ## Future Directions
    
    This PR does not fix version discovery over bridge, further
    investigation will be conducted here:
    https://github.com/paritytech/polkadot-sdk/issues/2417.
    
    ## TODO
    
    - [x] `pallet_xcm` mock for tests uses hard-coded XCM version `2` -
    change to 3 or lastest?
    - [x] fix `pallet-xcm-bridge-hub-router`
    - [x] fix HaulBlobExporter with version determination
    [here](https://github.com/paritytech/polkadot-sdk/blob/2183669d05f9b510f979a0cc3c7847707bacba2e/polkadot/xcm/xcm-builder/src/universal_exports.rs#L465)
    - [x] add unit-tests to the runtimes
    - [x] run benchmarks for `ExportMessage`
    - [x] extend local run scripts about `force_xcm_version(dest, version)`
    - [ ] when merged, prepare governance calls for Rococo/Westend
    - [ ] add PRDoc
    
    Part of: https://github.com/paritytech/parity-bridges-common/issues/2719
    
    ---------
    
    Co-authored-by: command-bot <>
    575b8f8d