<100 subscribers
Share Dialog
Share Dialog
翻译: julei
原文:
https://docs.starknet.io/docs/L1-L2%20Communication/messaging-mechanism
L2 上的合约可以通过 L2→L1 消息传递协议与 L1 上的合约进行异步交互。
在执行一个交易时,L2合约可通过send_message_to_L1()系统调用发送 L2→L1 消息。之后,Starknet系统就会将消息参数(包含 L1 上的接收者合约和相关数据)附加到L2的世界状态更新中。
代码示例:
let (message_payload : felt*) = alloc()
// 消息Payload。可增加。like msg_payload[1] ...
assert message_payload[0] = <payload_parameter>
assert_lt_felt(to_address, ETH_ADDRESS_BOUND)
assert_not_zero(to_address)
send_message_to_l1(to_address=to_address, payload_size=1, payload=message_payload)
在包含了此交易的L2世界状态更新后,并且在L1验证后,此消息的哈希会被存储在 StarkNet Core Contract ( L1) 中(引用计数+1),并发送一个LogMessageTo的L1事件(包含消息参数)。
稍后,L1 上的收件人可以消费这条消息。这是通过调用StarkNet 核心合约的consumeMessageFromL2 ()方法来完成的。 Starknet核心合约会验证Hash值是否对应于存储的消息,并且调用者确实是 L1 上的接收者。之后,StarkNet Core Contract 中消息哈希的引用计数减1。
上述流程如下图所示:
L2 → L1 消息的结构由下式给出:
// L2 → L1 消息
struct L2L1Message {
felt fromAddress;
felt toAddress;
Payload payload;
}
L2 → L1 消息的哈希值在 L1 上计算方法如下:
keccak256(
abi.encodePacked(
FromAddress,
ToAddress,
Payload.length,
Payload
)
);
L1 上的合约可以通过 L1→L2 消息传递协议与 L2 上的合约进行异步交互。该协议包括以下阶段:
1. L1 合约向 StarkNet 上的 L2 合约发起消息。它通过sendMessageToL2使用消息参数调用 StarkNet Core Contract 上的函数来实现。 1.1 StarkNet Core Contract 对消息参数进行散列处理,并更新 L1→L2 消息映射,以表明确实发送了具有此散列的消息。事实上,L1 合约记录了发送方支付的费用。有关更多信息,请参阅L1 → L2 消息费用。
2. 然后将消息解码为 StarkNet 交易,该交易调用l1_handler目标合约上的装饰器注解的函数。L2 上的此类事务称为L1 处理程序事务。
2\.1. StarkNet Sequencer在看到发送消息的交易的足够 L1 确认后,启动相应的 L2 交易。
2\.2. L2 事务调用相关的 l1_handler。
3. 将上一步创建的 L1 Handler 交易添加到证明中。
4. Core 合约收到状态更新
5. 消息从核心合约的存储中清除。至此,消息被处理。 上述流程如下图所示:
一条 L1→L2 消息包括:
L1 发件人地址 StarkNet 上的收件人合约地址 功能选择器 调用数据数组 消息随机数 消息随机数 消息 nonce 在 L1 上的 StarkNet Core 合约上维护,并且每当消息发送到 L2 时都会被碰撞。它用于避免在 L1 上多次发送同一消息引起的不同 L1 处理程序事务之间的哈希冲突(见下文)。
想象一下用户将资产从 L1 转移到 L2 的场景。该流程从用户将资产发送到 StarkNet 网桥和相应的 L1→L2 消息生成开始。现在,假设 L2 消息消费不起作用(可能由于 dApp 的 Cairo 合约中的bug)。这可能导致用户永远失去对其eth资产。
为了降低这种风险,我们允许发起 L1→L2 消息的合约取消它——在声明意图并等待适当的时间之后。用户首先调用StarkNet Core Contract的startL1ToL2MessageCancellation() 方法来开始取消流程。然后,在延迟五天后,用户调用消cancelL1ToL2Message()来完成取消流程。延迟的原因是为了保护Sequencer免受以重复发送和取消消息形式的 DOS 攻击——在它包含在 L1 之前,使包含相应 L1 处理程序激活的 L2 块无效。请注意,此流程应仅用于边缘情况,例如第 2 层合同上的错误阻止消息消费。
L1 → L2 消息在 L2 上引发交易,与常规交易不同,该交易不是由账户发送的。这需要一种不同的机制来支付交易费用,否则定序器没有动力将 L1 处理程序交易包含在一个块中。
为了避免在发送消息时必须与 L1 和 L2 交互,L1 → L2 消息是在 L1 上支付的,通过调用sendMessageToL2StarkNet Core 合约上的支付函数发送 ETH。定序器收取这笔费用以换取处理消息。定序器在使用此消息更新 L1 状态时全额收取费用。
注意
在消息的整个生命周期中,相关gas费用被锁定在核心合约中。如果由于任何原因未处理该消息,则取消该消息也会将费用退还给发件人。
费用本身的计算方式与“常规”L2 交易相同。您可以使用CLI估算 L1 → L2 消息费用。
为了完整起见,我们给出 L1 上的消息和 L2 上的相关交易的精确结构定义。
struct L2L1Message {
EthereumAddress FromAddress;
felt ToAddress;
felt Selector;
feltList<felt> Payload;
felt Nonce;
};
struct L1HandlerTransaction {
felt Version;
felt ContractAddress;
felt Selector;
List<felt> Calldata;
felt Nonce;
};
在L1上,消息的哈希值x计算方法如下:
keccak256(
abi.encodePacked(
uint256(FromAddress),
ToAddress,
Nonce,
Selector,
Payload.length,
Payload
)
);
在L2上,使用下述方法计算L1 Handler Transaction的Hash:
l1_hander_tx_hash = h(
"l1_handler",version,
contract_address,
entry_point_selector,
h(calldata),
max_fee,
chain_id,
nonce)
其中:
“l1_handler” 是一个常量前缀,以字节 (ASCII) 编码,采用bigendian。
chain_id是一个常数值,它指定了这个交易被发送到的网络。请参阅链 ID。
h()是Pedersen Hash
Calldata中的发件人地址
在l1_handler交易中,calldata 的第一个元素始终是发送者的以太坊地址。
翻译: julei
原文:
https://docs.starknet.io/docs/L1-L2%20Communication/messaging-mechanism
L2 上的合约可以通过 L2→L1 消息传递协议与 L1 上的合约进行异步交互。
在执行一个交易时,L2合约可通过send_message_to_L1()系统调用发送 L2→L1 消息。之后,Starknet系统就会将消息参数(包含 L1 上的接收者合约和相关数据)附加到L2的世界状态更新中。
代码示例:
let (message_payload : felt*) = alloc()
// 消息Payload。可增加。like msg_payload[1] ...
assert message_payload[0] = <payload_parameter>
assert_lt_felt(to_address, ETH_ADDRESS_BOUND)
assert_not_zero(to_address)
send_message_to_l1(to_address=to_address, payload_size=1, payload=message_payload)
在包含了此交易的L2世界状态更新后,并且在L1验证后,此消息的哈希会被存储在 StarkNet Core Contract ( L1) 中(引用计数+1),并发送一个LogMessageTo的L1事件(包含消息参数)。
稍后,L1 上的收件人可以消费这条消息。这是通过调用StarkNet 核心合约的consumeMessageFromL2 ()方法来完成的。 Starknet核心合约会验证Hash值是否对应于存储的消息,并且调用者确实是 L1 上的接收者。之后,StarkNet Core Contract 中消息哈希的引用计数减1。
上述流程如下图所示:
L2 → L1 消息的结构由下式给出:
// L2 → L1 消息
struct L2L1Message {
felt fromAddress;
felt toAddress;
Payload payload;
}
L2 → L1 消息的哈希值在 L1 上计算方法如下:
keccak256(
abi.encodePacked(
FromAddress,
ToAddress,
Payload.length,
Payload
)
);
L1 上的合约可以通过 L1→L2 消息传递协议与 L2 上的合约进行异步交互。该协议包括以下阶段:
1. L1 合约向 StarkNet 上的 L2 合约发起消息。它通过sendMessageToL2使用消息参数调用 StarkNet Core Contract 上的函数来实现。 1.1 StarkNet Core Contract 对消息参数进行散列处理,并更新 L1→L2 消息映射,以表明确实发送了具有此散列的消息。事实上,L1 合约记录了发送方支付的费用。有关更多信息,请参阅L1 → L2 消息费用。
2. 然后将消息解码为 StarkNet 交易,该交易调用l1_handler目标合约上的装饰器注解的函数。L2 上的此类事务称为L1 处理程序事务。
2\.1. StarkNet Sequencer在看到发送消息的交易的足够 L1 确认后,启动相应的 L2 交易。
2\.2. L2 事务调用相关的 l1_handler。
3. 将上一步创建的 L1 Handler 交易添加到证明中。
4. Core 合约收到状态更新
5. 消息从核心合约的存储中清除。至此,消息被处理。 上述流程如下图所示:
一条 L1→L2 消息包括:
L1 发件人地址 StarkNet 上的收件人合约地址 功能选择器 调用数据数组 消息随机数 消息随机数 消息 nonce 在 L1 上的 StarkNet Core 合约上维护,并且每当消息发送到 L2 时都会被碰撞。它用于避免在 L1 上多次发送同一消息引起的不同 L1 处理程序事务之间的哈希冲突(见下文)。
想象一下用户将资产从 L1 转移到 L2 的场景。该流程从用户将资产发送到 StarkNet 网桥和相应的 L1→L2 消息生成开始。现在,假设 L2 消息消费不起作用(可能由于 dApp 的 Cairo 合约中的bug)。这可能导致用户永远失去对其eth资产。
为了降低这种风险,我们允许发起 L1→L2 消息的合约取消它——在声明意图并等待适当的时间之后。用户首先调用StarkNet Core Contract的startL1ToL2MessageCancellation() 方法来开始取消流程。然后,在延迟五天后,用户调用消cancelL1ToL2Message()来完成取消流程。延迟的原因是为了保护Sequencer免受以重复发送和取消消息形式的 DOS 攻击——在它包含在 L1 之前,使包含相应 L1 处理程序激活的 L2 块无效。请注意,此流程应仅用于边缘情况,例如第 2 层合同上的错误阻止消息消费。
L1 → L2 消息在 L2 上引发交易,与常规交易不同,该交易不是由账户发送的。这需要一种不同的机制来支付交易费用,否则定序器没有动力将 L1 处理程序交易包含在一个块中。
为了避免在发送消息时必须与 L1 和 L2 交互,L1 → L2 消息是在 L1 上支付的,通过调用sendMessageToL2StarkNet Core 合约上的支付函数发送 ETH。定序器收取这笔费用以换取处理消息。定序器在使用此消息更新 L1 状态时全额收取费用。
注意
在消息的整个生命周期中,相关gas费用被锁定在核心合约中。如果由于任何原因未处理该消息,则取消该消息也会将费用退还给发件人。
费用本身的计算方式与“常规”L2 交易相同。您可以使用CLI估算 L1 → L2 消息费用。
为了完整起见,我们给出 L1 上的消息和 L2 上的相关交易的精确结构定义。
struct L2L1Message {
EthereumAddress FromAddress;
felt ToAddress;
felt Selector;
feltList<felt> Payload;
felt Nonce;
};
struct L1HandlerTransaction {
felt Version;
felt ContractAddress;
felt Selector;
List<felt> Calldata;
felt Nonce;
};
在L1上,消息的哈希值x计算方法如下:
keccak256(
abi.encodePacked(
uint256(FromAddress),
ToAddress,
Nonce,
Selector,
Payload.length,
Payload
)
);
在L2上,使用下述方法计算L1 Handler Transaction的Hash:
l1_hander_tx_hash = h(
"l1_handler",version,
contract_address,
entry_point_selector,
h(calldata),
max_fee,
chain_id,
nonce)
其中:
“l1_handler” 是一个常量前缀,以字节 (ASCII) 编码,采用bigendian。
chain_id是一个常数值,它指定了这个交易被发送到的网络。请参阅链 ID。
h()是Pedersen Hash
Calldata中的发件人地址
在l1_handler交易中,calldata 的第一个元素始终是发送者的以太坊地址。
No comments yet