16个重大版本发布
0.17.0 | 2024年7月18日 |
---|---|
0.14.0 | 2024年6月24日 |
0.10.0 | 2024年3月18日 |
0.6.0 | 2023年12月13日 |
0.0.0 | 2023年6月20日 |
1263 在 魔法豆
2,506 每月下载量
用于 9 个crate(8个直接使用)
2.5MB
46K SLoC
桥接消息模块
消息模块用于将源链的消息传递到目标链。消息对模块是(几乎)透明的,最终目标是把消息交给消息调度机制。
内容
概述
消息通道是一个单向通道,消息从源链发送到目标链。同时,单个消息模块实例支持出站通道和入站通道。因此,模块部署的链(本链),可能作为出站消息的源链(指向跨接链),也可能作为入站消息的目标链(来自跨接链)。
消息模块支持多个消息通道。每个消息通道都有一个4字节的标识符。通过该通道发送的消息被分配一个唯一的(对于该通道)递增整数值,称为nonce(“只能使用一次的数字”)。在同一通道上发送的消息保证按照它们从源链发送的顺序发送到目标链。换句话说,nonce为N
的消息将在发送nonce为N+1
的消息之前发送。
单个消息通道可以被视为单个应用(链上、链下或混合)的传输通道。同时,模块本身不会指定任何通道或消息规则。最终,是由运行时开发者定义这个运行时中的消息通道和消息的含义。
在我们的Kusama<>Polkadot 桥接中,我们使用通道作为不同中继链的两个平行链之间的通信通道。例如,通道[0, 0, 0, 0]
用于Polkadot <> Kusama Asset Hub通信。其他通道可以用来连接其他平行链。
消息工作流程
该组件不打算由最终用户使用,也不提供发送消息的公共调用。相反,它提供了运行时内部方法,允许其他组件(或其他运行时代码)排队出站消息。
当某些运行时代码调用组件的send_message()
方法时,“消息”就会出现。提交者指定他们愿意使用的通道以及消息本身。如果发送消息需要支付某些费用,则必须在组件之外支付。如果消息通过了所有检查(包括例如消息大小检查、禁用通道检查等),则会分配nonce并将消息存储在模块存储中。现在,消息处于“未发送”状态。
我们假设存在外部、链外演员,称为中继者,他们向目标和源链提交与模块相关的交易。组件本身对中继者的激励方案没有假设,但它提供了一些支付奖励的回调。有关详细信息,请参阅将消息模块集成到运行时。
最终,某个中继者会发现这个处于“未发送”状态的消息,并决定发送这个消息。然后,中继者为目标链上部署的消息模块实例制作receive_messages_proof()
交易(即交付交易)。中继者提供其在源链上的账户ID,消息证明(或多个消息),交易中的消息数量及其累积调度权重。一旦交易被挖掘,消息就认为已经“送达”。
一旦消息被投递,中继器可能希望向源链确认投递。这有两个原因。第一个原因是我们故意限制了入站通道中“已投递”但尚未“确认”的消息数量(请参阅关于消息模块配置特性中其他常量的内容以获取解释)。因此,在某个时刻,目标链可能会停止接受新的消息,直到中继器确认其中的一些。第二个原因是,如果中继器想要因投递而获得奖励,它必须证明它实际上已经投递了消息。而这个证明只能在投递交易被挖矿后生成。因此,中继器为源链上部署的消息模块实例创建了一个receive_messages_delivery_proof()
交易(也称为确认交易)。一旦这个交易被挖矿,消息就被认为是“已确认”的。
“已确认”状态是消息的最终状态。但是,与消息相关的一个最后一点是,它现在“已确认”且已向中继器支付了奖励(或者至少已调用此回调),必须向目标链确认这一点。否则,我们可能会达到目标链上“未确认”消息的限制,并且它将停止接受新的消息。因此,中继器有时会在下一个receive_messages_proof()
交易中包含最新“已确认”消息的nonce,以证明某些消息已被确认。
将消息模块集成到运行时
如上所述,消息模块支持出站和入站消息通道。因此,如果我们将在某个运行时中集成一个模块,它可能作为出站消息的源链运行时,以及作为入站消息的目标链运行时。在本节中,我们有时会称我们目前正在集成的链为“此链”,而将其他链称为“桥接链”。
消息模块不仅接受声称桥接链为我们有一些更新数据的交易。相反,该模块假设桥接链能够以某种方式证明这些更新数据。这个证明被抽象出来,可以是任何类型的。在我们从Substrate到Substrate的桥接中,我们使用运行时存储证明。其他桥接可能使用交易证明、Substrate头摘要或其他任何可以证明的内容。
重要提示:本章节以下所有内容描述了消息模块配置的细节。但是,如果您对两个基于Substrate的链的深入研究和相对容易的集成感兴趣,您可能想查看bridge-runtime-common crate。这个crate提供了一些用于集成的辅助工具,可以直接在您的运行时中使用。然后,如果您决定更改此方案中的某些内容,请回到这里以获取详细信息。
一般信息
消息模块支持实例。每个模块实例都应该桥接此链和某些桥接链。为了与其他链桥接,建议使用另一个实例(尽管代码中没有任何强制要求)。请注意,该包可以用来在多个链之间建立虚拟通道,正如我们在Polkadot <> Kusama桥中所做的那样。在那里,包实际上只桥接了两个平行链——Kusama桥接中心和Polkadot桥接中心。然而,其他Kusama和Polkadot平行链能够向它们的桥接中心发送(XCM)消息。这些消息将被投递到桥的另一侧,并在桥接链的共识中路由到正确的目标平行链。
消息提交者可以通过检查模块事件来跟踪消息进度。当消息被接受时,会触发MessageAccepted
事件。该事件包含消息通道标识符和已分配给消息的随机数。当消息被发送到目标链时,从receive_messages_delivery_proof
交易中触发MessagesDelivered
事件。该MessagesDelivered
事件包含消息通道标识符和已发送消息的随机数的范围。
该组件不提供获取目标链上消息分发结果的方法。如果需要,必须在组件外部完成。例如,XCM消息在发送时会有特殊指令将一些数据发送回发送者。其他分发器可能也会使用类似的机制。
如何将消息模块插入到跨链中发送和接收消息?
pallet_bridge_messages::Config
特质包含2个主要关联类型,用于处理入站消息。pallet_bridge_messages::BridgedChain
定义了跨链的基本原语。而pallet_bridge_messages::BridgedHeaderChain
定义了我们在运行时访问跨链头部的方式。如果你与使用GRANDPA最终性的链桥接,可以使用pallet_bridge_grandpa
;如果你与跨链桥接,可以使用pallet_bridge_parachains::ParachainHeaders
。
pallet_bridge_messages::Config::MessageDispatch
定义了如何分发已发送的消息。除了实际分发消息外,实现必须在调用分发之前返回消息的正确分发权重。
最后一个类型是pallet_bridge_messages::Config::DeliveryConfirmationPayments
。当收到确认交易时,我们会调用pay_reward
方法,传递已发送消息的范围。你可以使用pallet-bridge-relayers
组件及其适配器DeliveryConfirmationPaymentsAdapter
作为可能的实现。这允许你为转发消息支付固定奖励,并为确认交付支付其部分。
我在我的运行时中有一个消息模块,但我想要拒绝所有出站消息。我该怎么做?
你应该查看bp_messages::source_chain::ForbidOutboundMessages
结构体。它实现了所有必需的特质,并将简单地拒绝所有与出站消息相关的交易。
我在我的运行时中有一个消息模块,但我想要拒绝所有入站消息。我该怎么做?
你应该查看来自bp_messages::target_chain::ForbidInboundMessages
模块的结构体。它实现了所有必需的特质,并将简单地拒绝所有与入站消息相关的交易。
消息模块配置特质中的其他常量有哪些?
用于检查 send_message()
函数中的两个设置。pallet_bridge_messages::Config::ActiveOutboundLanes
是所有消息通道的数组,可用于发送消息。使用其他通道发送的所有消息都将被拒绝。大小超过 pallet_bridge_messages::Config::MaximalOutboundPayloadSize
的所有消息也将被拒绝。
为了能够奖励中继节点在消息投递中的贡献,我们会在目标链的运行时存储中保存一个映射,映射关系为消息非ces范围 => 投递该范围的节点标识符。如果一个中继节点投递多个连续的范围,它们会被合并成一个条目。因此,对于同一个中继节点可能会有多个条目。最终,这个整个映射必须返回到源链以确认投递并支付奖励。因此,为了确保我们能构建这个确认事务,我们需要:(1) 保持这个映射的大小低于一定限制,(2) 确保处理这个映射的权重低于一定限制。大小和权重主要取决于条目数量。条目数量受到 pallet_bridge_messages::Config::BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX
参数的限制。处理权重也取决于正在确认的总消息数量,因为每个确认的消息都需要读取。因此,还有一个 pallet_bridge_messages::Config::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX
参数。
在选择这些参数的值时,您还必须考虑到,如果您的方案中的证明基于头的最终性(对于具有最终性概念的基于Substrate的链来说,这是最明显的选择),那么对这些参数选择过小的值可能会导致消息投递的显著延迟。这是因为在此方案中有太多的参与者:1) 需要最终化目标链头的权威方需要最终化非空映射的头;2) 中继节点随后需要向源链提交这个头和其最终性证明;3) 消息中继节点随后需要向源链发送确认事务(这个映射的存储证明);4) 当确认事务在某头被挖矿时,源链的权威方必须最终化这个头;5) 头的中继节点随后需要向目标链提交这个头和其最终性证明;6) 只有现在,消息中继节点才能提交来自源链到目标链的新消息并从映射中删除条目。
投递事务需要中继节点提供映射中的条目数量和总消息数量。这意味着模块永远不会为投递映射收取额外费用 - 中继节点只需为其投递的条目和消息数量支付准确的费用。因此,这些参数的最佳猜测值将是占据 N
百分比的最大事务大小和源链权重的配对。 N
应该足够大,以处理大映射,同时为未来源链的升级保留空间。
非必需功能
在每个运行时,可能有一个特殊账户,其中部署了消息模块。这个账户,称为“模块所有者”,类似于模块级别的sudo账户 - 他能够暂停和恢复所有模块操作,而无需进行运行时升级。与此账户相关的调用是
fn set_owner()
:当前模块所有者可以调用它将“所有权”转让给另一个账户;fn set_operating_mode()
:模块所有者(或sudo账户)可以调用此函数暂停/恢复pallet操作。所有者可以通过使用MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
参数调用此方法来停止pallet - 所有与消息相关的交易都将被拒绝。然后,所有者可以通过传递MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
参数来恢复pallet操作。还有一个MessagesOperatingMode::RejectingOutboundMessages
pallet模式,其中它仍然接受所有传入的消息,但拒绝所有传出消息。
如果未定义pallet所有者,可以使用治理来执行这些调用。
消息中继
我们有一个链外actor,它会监视新消息并将它们提交到桥接链。这是消息中继 - 你可以查看crate级别的文档和代码。
依赖关系
~19–34MB
~587K SLoC