12 breaking releases
new 0.13.2 | Dec 20, 2024 |
---|---|
0.13.0 | Sep 26, 2024 |
0.12.0 | Jul 19, 2024 |
0.5.0 | Mar 19, 2024 |
0.1.0 | Dec 13, 2023 |
#425 in Magic Beans
98,606 downloads per month
Used in 15 crates
(5 directly)
3MB
51K
SLoC
Release
Polkadot SDK stable2409
lib.rs
:
Module that adds XCM support to bridge pallets. The pallet allows to dynamically open and close bridges between local (to this pallet location) and remote XCM destinations.
The pallet_xcm_bridge_hub
pallet is used to manage (open, close) bridges between chains from
different consensuses. The new extrinsics fn open_bridge
and fn close_bridge
are introduced.
Other chains can manage channels with different bridged global consensuses.
Concept of lane
and LaneId
There is another pallet_bridge_messages
pallet that handles inbound/outbound lanes for
messages. Each lane is a unique connection between two chains from different consensuses and is
identified by LaneId
. LaneId
is generated once when a new bridge is requested by fn open_bridge
. It is generated by BridgeLocations::calculate_lane_id
based on the following
parameters:
- Source
bridge_origin_universal_location
(latest XCM) - Destination
bridge_destination_universal_location
(latest XCM) - XCM version (both sides of the bridge must use the same parameters to generate the same
LaneId
)bridge_origin_universal_location
,bridge_destination_universal_location
is converted to theVersioned*
structs
LaneId
is expected to never change because:
- We need the same
LaneId
on both sides of the bridge, asLaneId
is part of the message key proofs. - Runtime upgrades are entirely asynchronous.
- We already have a running production Polkadot/Kusama bridge that uses
LaneId([0, 0, 0, 0])
.
LaneId
is backward compatible, meaning it can be encoded/decoded from the older format [u8; 4]
used for static lanes, as well as the new format H256
generated by
BridgeLocations::calculate_lane_id
.
Concept of bridge
and BridgeId
The pallet_xcm_bridge_hub
pallet needs to store some metadata about opened bridges. The bridge
(or bridge metadata) is stored under the BridgeId
key.
BridgeId
is generated from bridge_origin_relative_location
and
bridge_origin_universal_location
using the latest
XCM structs. BridgeId
is not transferred
over the bridge; it is only important for local consensus. It essentially serves as an index/key
to bridge metadata. All the XCM infrastructure around XcmExecutor
, SendXcm
, ExportXcm
use
the latest
XCM, so BridgeId
must remain compatible with the latest
XCM. For example, we
have an ExportXcm
implementation in exporter.rs
that handles the ExportMessage
instruction
with universal_source
and destination
(latest XCM), so we need to create BridgeId
and the
corresponding LaneId
.
Migrations and State
This pallet implements try_state
, ensuring compatibility and checking everything so we know if
any migration is needed. do_try_state
checks for BridgeId
compatibility, which is
recalculated on runtime upgrade. Upgrading to a new XCM version should not break anything,
except removing older XCM versions. In such cases, we need to add migration for BridgeId
and
stored Versioned*
structs and update LaneToBridge
mapping, but this won't affect LaneId
over the bridge.
How to Open a Bridge?
The pallet_xcm_bridge_hub
pallet has the extrinsic fn open_bridge
and an important
configuration pallet_xcm_bridge_hub::Config::OpenBridgeOrigin
, which translates the call's
origin to the XCM Location
and converts it to the bridge_origin_universal_location
. With the
current setup, this origin/location is expected to be either the relay chain or a sibling
parachain as one side of the bridge. Another parameter is
bridge_destination_universal_location
, which is the other side of the bridge from a different
global consensus.
Every bridge between two XCM locations has a dedicated lane in associated messages pallet. Assuming that this pallet is deployed at the bridge hub parachain and there's a similar pallet at the bridged network, the dynamic bridge lifetime is as follows:
-
the sibling parachain opens a XCMP channel with this bridge hub;
-
the sibling parachain funds its sovereign parachain account at this bridge hub. It shall hold enough funds to pay for the bridge (see
BridgeDeposit
); -
the sibling parachain opens the bridge by sending XCM
Transact
instruction with theopen_bridge
call. TheBridgeDeposit
amount is reserved on the sovereign account of sibling parachain; -
at the other side of the bridge, the same thing (1, 2, 3) happens. Parachains that need to connect over the bridge need to coordinate the moment when they start sending messages over the bridge. Otherwise they may lose messages and/or bundled assets;
-
when either side wants to close the bridge, it sends the XCM
Transact
with theclose_bridge
call. The bridge is closed immediately if there are no queued messages. Otherwise, the owner must repeat theclose_bridge
call to prune all queued messages first.
The pallet doesn't provide any mechanism for graceful closure, because it always involves some contract between two connected chains and the bridge hub knows nothing about that. It is the task for the connected chains to make sure that all required actions are completed before the closure. In the end, the bridge hub can't even guarantee that all messages that are delivered to the destination, are processed in the way their sender expects. So if we can't guarantee that, we shall not care about more complex procedures and leave it to the participating parties.
Example
Example of opening a bridge between some random parachains from Polkadot and Kusama:
- Let's have:
- BridgeHubPolkadot with
UniversalLocation
=[GlobalConsensus(Polkadot), Parachain(1002)]
- BridgeHubKusama with
UniversalLocation
=[GlobalConsensus(Kusama), Parachain(1002)]
- BridgeHubPolkadot with
- The Polkadot local sibling parachain
Location::new(1, Parachain(1234))
must send some DOTs to its sovereign account on BridgeHubPolkadot to coverBridgeDeposit
, fees forTransact
, and the existential deposit. - Send a call to the BridgeHubPolkadot from the local sibling parachain:
Location::new(1, Parachain(1234))
xcm::Transact( origin_kind: OriginKind::Xcm, XcmOverBridgeHubKusama::open_bridge( VersionedInteriorLocation::V4([GlobalConsensus(Kusama), Parachain(4567)].into()), ); )
- Check the stored bridge metadata and generated
LaneId
. - The Kusama local sibling parachain
Location::new(1, Parachain(4567))
must send some KSMs to its sovereign account on BridgeHubKusama to coverBridgeDeposit
, fees forTransact
, and the existential deposit. - Send a call to the BridgeHubKusama from the local sibling parachain:
Location::new(1, Parachain(4567))
xcm::Transact( origin_kind: OriginKind::Xcm, XcmOverBridgeHubKusama::open_bridge( VersionedInteriorLocation::V4([GlobalConsensus(Polkadot), Parachain(1234)].into()), ); )
- Check the stored bridge metadata and generated
LaneId
. - Both
LaneId
s from steps 3 and 6 must be the same (see above Concept oflane
andLaneId
). - Run the bridge messages relayer for
LaneId
. - Send messages from both sides.
The opening bridge holds the configured BridgeDeposit
from the origin's sovereign account, but
this deposit is returned when the bridge is closed with fn close_bridge
.
Dependencies
~21–36MB
~609K SLoC