-
Notifications
You must be signed in to change notification settings - Fork 71
RFC-0160: PubSub Mechanism #160
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
base: main
Are you sure you want to change the base?
Conversation
text/0160-pub-sub-mechanism.md
Outdated
|
|
||
| ## Summary | ||
|
|
||
| Add a publish/subscribe mechanism that allows parachains to publish data to the relay chain and other parachains to consume it via verified relay chain storage proofs. |
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.
| Add a publish/subscribe mechanism that allows parachains to publish data to the relay chain and other parachains to consume it via verified relay chain storage proofs. | |
| Add a publish/subscribe mechanism that allows parachains to publish data to the relay chain and other parachains to consume it via relay chain storage proofs. |
text/0160-pub-sub-mechanism.md
Outdated
|
|
||
| ## Motivation | ||
|
|
||
| As pointed out in issue #606, when parachains need to exchange information, the only available options are repeated point-to-point XCM messages or ad-hoc off-chain infrastructure. This RFC is motivated by the desire to propose an alternative based on a relay-assisted publish/subscribe mechanism that enables one-to-many, verifiable, cross-parachain data distribution. |
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 provide a full link to the issue.
text/0160-pub-sub-mechanism.md
Outdated
| Publish { data: PublishData } | ||
| PublishData = BoundedVec<(PublishKey, BoundedVec<u8, MaxPublishValueLength>), MaxPublishItems> |
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.
| Publish { data: PublishData } | |
| PublishData = BoundedVec<(PublishKey, BoundedVec<u8, MaxPublishValueLength>), MaxPublishItems> | |
| Publish { key: PublishKey, data: BoundedVec<u8, MaxPublishValueLength>, ttl: u32 } |
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.
No need to publish multiple things at once and if this is needed, these messages can just be batched.
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.
ttl 0 means infinite and otherwise the number in blocks the data is valid. In the end it is up to the subscriber to decide if they still process the data or not.
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.
Yeah we could include multiple of these instructions in one same XCM
| ## Explanation | ||
|
|
||
| This RFC defines a relay-assisted publish/subscribe mechanism that enables parachains to share arbitrary key-value data through the relay chain without relying on repeated XCM messages or off-chain protocols. The core idea is to use the relay chain as a common, verifiable distribution layer, allowing data to be published once and consumed by many parachains with bounded and predictable costs. | ||
| Parachains publish data via a new XCM v5 `Publish` instruction. Published entries are stored on the relay chain in a per-publisher child trie managed by the broadcaster pallet. Publishers must be registered, and both the number of keys and their sizes are bounded to prevent unbounded state growth. Each published item is addressed by a fixed-size key. The suggestion is to backport the new instruction to v5 as v6 discussion advances. |
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.
Key doesn't need to be fixed size.
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.
"Each published item is addressed by a size-capped key." ?
| On the consumer side, subscribing parachains declare their interests at the runtime level by specifying which publishers and keys they want to subscribe to. This information is exposed to the collator through a new dedicated runtime API (`KeyToIncludeInRelayProofApi`), allowing the collator to know which relay-chain storage keys must be proven for a given block. During block production, the collator fetches relay-chain state proofs for those keys, including child-trie entries, and embeds them in the ParachainInherentData. | ||
|
|
||
| ```rust | ||
| /// A relay chain storage key to be included in the storage proof. | ||
| #[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, Eq)] | ||
| pub enum RelayStorageKey { | ||
| /// Top-level relay chain storage key. | ||
| Top(Vec<u8>), | ||
| /// Child trie storage key. | ||
| Child { | ||
| /// Unprefixed storage key identifying the child trie root location. | ||
| /// Prefix `:child_storage:default:` is added when accessing storage. | ||
| /// Used to derive `ChildInfo` for reading child trie data. | ||
| storage_key: Vec<u8>, | ||
| /// Key within the child trie. | ||
| key: Vec<u8>, | ||
| }, | ||
| } | ||
|
|
||
| /// Request for proving relay chain storage data. | ||
| /// | ||
| /// Contains a list of storage keys (either top-level or child trie keys) | ||
| /// to be included in the relay chain state proof. | ||
| #[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, Eq, Default)] | ||
| pub struct RelayProofRequest { | ||
| /// Storage keys to include in the relay chain state proof. | ||
| pub keys: Vec<RelayStorageKey>, | ||
| } | ||
|
|
||
| /// API for specifying which relay chain storage data to include in storage proofs. | ||
| /// | ||
| /// This API allows parachains to request both top-level relay chain storage keys | ||
| /// and child trie storage keys to be included in the relay chain state proof. | ||
| pub trait KeyToIncludeInRelayProofApi { | ||
| /// Returns relay chain storage proof requests. | ||
| /// | ||
| /// The returned `RelayProofRequest` contains a list of storage keys where each key | ||
| /// can be either: | ||
| /// - `RelayStorageKey::Top`: Top-level relay chain storage key | ||
| /// - `RelayStorageKey::Child`: Child trie storage, containing the child trie identifier | ||
| /// and the key to prove from that child trie | ||
| /// | ||
| /// The collator generates proofs for these and includes them in the relay chain state proof. | ||
| fn keys_to_prove() -> RelayProofRequest; | ||
| } | ||
| ``` | ||
|
|
||
| The parachain verifies the relay-chain state proofs as part of `set_validation_data` of `parachain-system`. A processing hook extracts the relevant key–value pairs from the proofs and only triggers updates when the corresponding child-trie root has changed since the previous block, avoiding redundant child trie data extractions which are the most significant computation-demanding process of the feature. Verified updates are then delivered to the runtime through a subscription handler, enabling consumption of cross-chain data. | ||
|
|
||
| ```rust | ||
| /// Processor for relay chain proof keys. | ||
| /// | ||
| /// This allows parachains to process data from the relay chain state proof, | ||
| /// including both child trie keys and main trie keys that were requested | ||
| /// via `KeyToIncludeInRelayProofApi`. | ||
| type RelayProofKeysProcessor: ProcessRelayProofKeys; | ||
|
|
||
| /// Process keys from verified relay chain state proofs. | ||
| /// | ||
| /// This trait allows processing of relay chain storage data from the verified proof. | ||
| pub trait ProcessRelayProofKeys { | ||
| /// Process keys from a verified relay state proof. | ||
| fn process_relay_proof_keys(verified_proof: &RelayChainStateProof) -> Weight; | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| ```rust | ||
| /// Define subscriptions and handle received data. | ||
| pub trait SubscriptionHandler { | ||
| /// List of subscriptions as (ParaId, keys) tuples. | ||
| /// Returns (subscriptions, weight) where weight is the cost of computing the subscriptions. | ||
| fn subscriptions() -> (Vec<(ParaId, Vec<Vec<u8>>)>, Weight); | ||
|
|
||
| /// Called when subscribed data is updated. | ||
| /// Returns the weight consumed by processing the data. | ||
| fn on_data_updated(publisher: ParaId, key: Vec<u8>, value: Vec<u8>) -> Weight; | ||
| } | ||
| ``` | ||
|
|
||
| This design intents to address the issue #606 by reusing existing `cumulus` and relay-chain infrastructure. |
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.
IMO not important for the XCM changes here. Because these are just implementation details.
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.
can you clarify this one ?
text/0160-pub-sub-mechanism.md
Outdated
|
|
||
| - The relay-chain state passed during collator block construction increases, and its size and impact must be carefully monitored. | ||
| - Processing relay state proofs introduces additional overhead for data extraction and verification in the runtime. | ||
| - `KeyToIncludeInRelayProofApi` currently works for child tries using the default route; making it fully generic would require extending `ChildInfo` with `TypeInfo` or a workaround, which should be evaluated separately. |
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.
| - `KeyToIncludeInRelayProofApi` currently works for child tries using the default route; making it fully generic would require extending `ChildInfo` with `TypeInfo` or a workaround, which should be evaluated separately. |
| ## Explanation | ||
|
|
||
| This RFC defines a relay-assisted publish/subscribe mechanism that enables parachains to share arbitrary key-value data through the relay chain without relying on repeated XCM messages or off-chain protocols. The core idea is to use the relay chain as a common, verifiable distribution layer, allowing data to be published once and consumed by many parachains with bounded and predictable costs. | ||
| Parachains publish data via a new XCM v5 `Publish` instruction. Published entries are stored on the relay chain in a per-publisher child trie managed by the broadcaster pallet. Publishers must be registered, and both the number of keys and their sizes are bounded to prevent unbounded state growth. Each published item is addressed by a fixed-size key. The suggestion is to backport the new instruction to v5 as v6 discussion advances. |
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.
We can't add new instructions to XCMv5, it would have to go into v6.
Data can be published via Transact meanwhile.
text/0160-pub-sub-mechanism.md
Outdated
| Publish { data: PublishData } | ||
| PublishData = BoundedVec<(PublishKey, BoundedVec<u8, MaxPublishValueLength>), MaxPublishItems> |
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.
Yeah we could include multiple of these instructions in one same XCM
text/0160-pub-sub-mechanism.md
Outdated
|
|
||
| ```rust | ||
| Publish { data: PublishData } | ||
| PublishData = BoundedVec<(PublishKey, BoundedVec<u8, MaxPublishValueLength>), MaxPublishItems> |
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.
PublishKey is not defined?
text/0160-pub-sub-mechanism.md
Outdated
| /// Unprefixed storage key identifying the child trie root location. | ||
| /// Prefix `:child_storage:default:` is added when accessing storage. | ||
| /// Used to derive `ChildInfo` for reading child trie data. | ||
| storage_key: Vec<u8>, |
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.
| storage_key: Vec<u8>, | |
| trie_key: Vec<u8>, |
text/0160-pub-sub-mechanism.md
Outdated
| /// Used to derive `ChildInfo` for reading child trie data. | ||
| storage_key: Vec<u8>, | ||
| /// Key within the child trie. | ||
| key: Vec<u8>, |
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.
| key: Vec<u8>, | |
| item_key: Vec<u8>, |
text/0160-pub-sub-mechanism.md
Outdated
|
|
||
| This proposal is a necessary trade-off rather than a pure optimization, adding relay-chain state proofs and runtime verification to avoid repeated point-to-point XCM requests and off-chain systems. Overhead is limited by proving only explicitly subscribed keys and enforcing strict bounds, while update handling based on child-trie root changes is managed by the Subscriber. | ||
|
|
||
| Does not present any overhead when not implemented. Runtimes can decide simply not to implement the API and pallet broadcaster rr subscriber and there will be no performance impact. |
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.
rr?
| ## Explanation | ||
|
|
||
| This RFC defines a relay-assisted publish/subscribe mechanism that enables parachains to share arbitrary key-value data through the relay chain without relying on repeated XCM messages or off-chain protocols. The core idea is to use the relay chain as a common, verifiable distribution layer, allowing data to be published once and consumed by many parachains with bounded and predictable costs. | ||
| Parachains publish data via a new XCM v5 `Publish` instruction. Published entries are stored on the relay chain in a per-publisher child trie managed by the broadcaster pallet. Publishers must be registered, and both the number of keys and their sizes are bounded to prevent unbounded state growth. Each published item is addressed by a fixed-size key. The suggestion is to backport the new instruction to v5 as v6 discussion advances. |
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 presume publishers should put down a deposit when publishing to the relay?
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.
They must register as publishers and reserve a deposit on the relay.
| Parachains publish data via a new XCM v5 `Publish` instruction. Published entries are stored on the relay chain in a per-publisher child trie managed by the broadcaster pallet. Publishers must be registered, and both the number of keys and their sizes are bounded to prevent unbounded state growth. Each published item is addressed by a fixed-size key. The suggestion is to backport the new instruction to v5 as v6 discussion advances. | ||
|
|
||
| ```rust | ||
| Publish { key: PublishKey, data: BoundedVec<u8, MaxPublishValueLength>, ttl: u32 } |
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.
We should probably make PublishKey directly a Hash. This saves bandwidth and computation time on the relay chain.
WDYT @franciscoaguirre?
Co-authored-by: Francisco Aguirre <[email protected]>
|
At some level scalability requires off-chain infrastructure, but whether you need this depends upon how much you & everyone else sends. An XCM message goes into relay chain blocks, which then get synced by every RC node and every collator of every parachain. It follows that XCM protocols are always more global broadcast announcements to all nodes of all parachains and XCM protocol being called "pub-sub" feels dubious to me. All parachains place their state roots into their candidate receipts, so message can authenticate via some method like relay parents. If you want long delays and accept delivery failures then you can simply insert a proof of some parachain data into the RC DHT, so then subscribing means checking the right DHT locations. If you want more assurances, then we can have gossip protocols that say which messages go where, but how you want RC nodes involved changes things. Anyways XCM is a protocol that works, so use it when you need it. Also XCMs having a recipient group is much less stupid than XCMs having only one recipient, so this maybe a major improvement over the current XCM. I anyways wanted to point out that the "subscribe" part feels a bit overstated here. |
This RFC proposes a relay-assisted publish/subscribe mechanism that enables parachains to share arbitrary key-value data through the relay chain without relying on repeated XCM messages or off-chain protocols.