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:消息处理与防重放流程
部署与验证
环境要求: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,证明消息唯一性判定正确。