> For the complete documentation index, see [llms.txt](https://cll-devrel.gitbook.io/ccip-bootcamp/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cll-devrel.gitbook.io/ccip-bootcamp/mandarin-ccip-bootcamp/di-1-tian/lian-xi-1-shi-yong-fang-yu-xing-shi-li-mo-shi-jin-xing-ke-bian-cheng-dai-bi-zhuan-yi/fang-yu-xing-shi-li-mo-shi.md).

# 防御性示例模式

我们将要实现的另一个最佳实践称为防御性示例（Defensive Example），这是一种模式，它允许我们在不强制原始交易失败的情况下重新处理失败的消息。让我们解释一下它的工作原理。

**接收和处理消息**

当目标区块链接收到消息时，CCIP路由器会调用`ccipReceive`函数。此函数作为合约处理传入CCIP消息的入口点，通过`onlyRouter`和`onlyAllowlisted`修饰符实施关键的安全检查。

以下是该过程的逐步分解：

1. 通过`ccipReceive`进入：
   * `ccipReceive`函数被调用，传入一个包含待处理消息的`Any2EVMMessage`结构体。
   * 安全检查确保调用来自授权的路由器、已允许的源链和已允许的发送者。
2. 处理消息：
   * `ccipReceive`调用`processMessage`函数，该函数是外部的，以利用Solidity的try/catch错误处理机制。**注意**：`onlySelf`修饰符确保只有合约本身可以调用此函数。
   * 在`processMessage`内部，使用`s_simRevert`状态变量检查模拟回滚条件。这个模拟由只有合约所有者可调用的`setSimRevert`函数来切换。
   * 如果`s_simRevert`为假，`processMessage`调用`_ccipReceive`函数进行进一步的消息处理。
3. `_ccipReceive`中的消息处理：
   * `_ccipReceive`从消息中提取并存储各种信息，如`messageId`、解码后的`sender`地址、代币数量和数据。
   * 然后发出一个`MessageReceived`事件，表示消息处理成功。
4. 错误处理：
   * 如果在处理过程中发生错误（或触发了模拟回滚），`ccipReceive`内的catch块将被执行。
   * 失败消息的`messageId`被添加到`s_failedMessages`中，消息内容被存储在`s_messageContents`中。
   * 发出一个`MessageFailed`事件，允许以后识别和重处理失败的消息。

**失败消息的重新处理**

`retryFailedMessage`函数提供了一种机制，如果CCIP消息处理失败，可以用来恢复资产。它专门设计用来处理消息数据问题导致整个处理无法进行，但允许代币恢复的场景：

1. 初始化：
   * 只有合约所有者可以调用此函数，提供失败消息的`messageId`和代币恢复用的`tokenReceiver`地址。
2. 验证：
   * 使用`s_failedMessages.get(messageId)`检查消息是否已失败。如果没有，就回滚交易。
3. 状态更新：
   * 将消息的错误代码更新为`RESOLVED`，以防止重复进入和多次重试。
4. 代币恢复：
   * 使用`s_messageContents[messageId]`检索失败的消息内容。
   * 将与失败消息关联的锁定代币转移到指定的`tokenReceiver`，作为一个逃生舱口，而无需再次处理整个消息。
5. **事件发出**：
   * 发出一个`MessageRecovered`事件，以信号代币已成功恢复。

这个函数展示了一个优雅的资产恢复解决方案，即使在消息处理遇到问题时也能保护用户的价值。
