4. 跨链中继器模拟合约

任务说明

本案例实现一个模拟跨链中继器合约,核心功能包括:接收来自源链的跨链消息、验证消息来源与序号、转发消息至目标链,并抵御重放攻击。重放攻击防护通过记录已处理消息的唯一哈希(由源链ID、消息序号与负载共同生成)实现,同一消息仅可被处理一次。

合约实现

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract RelayerMock {
    address public owner;
    mapping(uint256 => bool) public processedSourceChains; // 已授权的源链ID
    mapping(bytes32 => bool) public processedHashes;        // 已处理消息哈希

    event MessageRelayed(uint256 indexed sourceChain, uint64 indexed sequence, bytes payload);
    event MessageRejected(bytes32 reason);

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    constructor() {
        owner = msg.sender;
    }

    // 授权某条源链(仅owner可调用)
    function authorizeSourceChain(uint256 chainId) external onlyOwner {
        processedSourceChains[chainId] = true;
    }

    // 撤销源链授权
    function revokeSourceChain(uint256 chainId) external onlyOwner {
        delete processedSourceChains[chainId];
    }

    // 核心:接收并转发跨链消息
    function relayMessage(
        uint256 sourceChain,
        uint64 sequence,
        bytes calldata payload
    ) external returns (bool) {
        // 1. 来源校验
        require(processedSourceChains[sourceChain], "unauthorized source");

        // 2. 计算消息唯一哈希(防重放核心)
        bytes32 msgHash = keccak256(abi.encodePacked(sourceChain, sequence, payload));

        // 3. 重放检测
        if (processedHashes[msgHash]) {
            emit MessageRejected("duplicate message");
            return false;
        }

        // 4. 标记已处理(防止重放)
        processedHashes[msgHash] = true;

        // 5. 转发消息(模拟:此处仅记录事件,实际可调用目标链合约)
        emit MessageRelayed(sourceChain, sequence, payload);

        return true;
    }

    // 查询某消息是否已被处理
    function isProcessed(uint256 sourceChain, uint64 sequence, bytes calldata payload) external view returns (bool) {
        bytes32 msgHash = keccak256(abi.encodePacked(sourceChain, sequence, payload));
        return processedHashes[msgHash];
    }
}

关键设计说明

消息唯一性由sourceChain + sequence + payload三者共同决定,任何字段不同均视为不同消息。合约通过processedHashes映射记录已处理消息,重复提交相同消息时返回false并触发MessageRejected事件。源链授权机制确保只有被信任的链ID才能发送消息,防止伪造来源。

SVG:消息处理与防重放流程

跨链中继器消息处理与防重放状态流 源链 (sourceChain) sequence: uint64 payload: bytes 提交 RelayerMock 合约 ① 校验源链授权 ② 计算 msgHash ③ 查重 processedHashes ④ 标记已处理 + 转发 已存在? msgHash 是 拒绝: 重放攻击 emit MessageRejected 否 转发成功 emit MessageRelayed 目标链 接收转发消息 图例: 中继器逻辑 拒绝路径 成功路径 外部链

部署与验证

环境要求:Solidity ^0.8.19,可使用Remix IDE或Hardhat部署。

// 部署后交互示例(Remix控制台或脚本)
// 1. 部署 RelayerMock
// 2. 授权源链
relayer.authorizeSourceChain(1);
// 3. 发送消息(源链ID=1, 序号=100, 负载="Hello")
relayer.relayMessage(1, 100, "Hello");
// 返回 true,事件 MessageRelayed 触发

// 4. 重放相同消息
relayer.relayMessage(1, 100, "Hello");
// 返回 false,事件 MessageRejected 触发

// 5. 验证查询
relayer.isProcessed(1, 100, "Hello");
// 返回 true

判定标准:

  • 首次提交相同消息返回true,第二次提交相同消息返回false,证明重放防护生效;
  • 未授权源链(如sourceChain=2)调用时抛出异常"unauthorized source";
  • 修改序号或负载后再次提交,返回true,证明消息唯一性判定正确。