防御性示例模式
我们将要实现的另一个最佳实践称为防御性示例(Defensive Example),这是一种模式,它允许我们在不强制原始交易失败的情况下重新处理失败的消息。让我们解释一下它的工作原理。
接收和处理消息
当目标区块链接收到消息时,CCIP路由器会调用ccipReceive函数。此函数作为合约处理传入CCIP消息的入口点,通过onlyRouter和onlyAllowlisted修饰符实施关键的安全检查。
以下是该过程的逐步分解:
通过
ccipReceive进入:ccipReceive函数被调用,传入一个包含待处理消息的Any2EVMMessage结构体。安全检查确保调用来自授权的路由器、已允许的源链和已允许的发送者。
处理消息:
ccipReceive调用processMessage函数,该函数是外部的,以利用Solidity的try/catch错误处理机制。注意:onlySelf修饰符确保只有合约本身可以调用此函数。在
processMessage内部,使用s_simRevert状态变量检查模拟回滚条件。这个模拟由只有合约所有者可调用的setSimRevert函数来切换。如果
s_simRevert为假,processMessage调用_ccipReceive函数进行进一步的消息处理。
_ccipReceive中的消息处理:_ccipReceive从消息中提取并存储各种信息,如messageId、解码后的sender地址、代币数量和数据。然后发出一个
MessageReceived事件,表示消息处理成功。
错误处理:
如果在处理过程中发生错误(或触发了模拟回滚),
ccipReceive内的catch块将被执行。失败消息的
messageId被添加到s_failedMessages中,消息内容被存储在s_messageContents中。发出一个
MessageFailed事件,允许以后识别和重处理失败的消息。
失败消息的重新处理
retryFailedMessage函数提供了一种机制,如果CCIP消息处理失败,可以用来恢复资产。它专门设计用来处理消息数据问题导致整个处理无法进行,但允许代币恢复的场景:
初始化:
只有合约所有者可以调用此函数,提供失败消息的
messageId和代币恢复用的tokenReceiver地址。
验证:
使用
s_failedMessages.get(messageId)检查消息是否已失败。如果没有,就回滚交易。
状态更新:
将消息的错误代码更新为
RESOLVED,以防止重复进入和多次重试。
代币恢复:
使用
s_messageContents[messageId]检索失败的消息内容。将与失败消息关联的锁定代币转移到指定的
tokenReceiver,作为一个逃生舱口,而无需再次处理整个消息。
事件发出:
发出一个
MessageRecovered事件,以信号代币已成功恢复。
这个函数展示了一个优雅的资产恢复解决方案,即使在消息处理遇到问题时也能保护用户的价值。
Last updated