Skip to content

Commit

Permalink
Fix issues (#158)
Browse files Browse the repository at this point in the history
* rm merkle tree

* rm merkle tree

* simply

* rename messageCount to count

* fmt

* change hash lookup key
  • Loading branch information
hujw77 authored Apr 18, 2024
1 parent 54ed001 commit 0d4a84c
Show file tree
Hide file tree
Showing 16 changed files with 83 additions and 482 deletions.
64 changes: 16 additions & 48 deletions src/Channel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,52 +19,39 @@ pragma solidity 0.8.17;

import "./UserConfig.sol";
import "./interfaces/IVerifier.sol";
import "./imt/IncrementalMerkleTree.sol";

/// @title Channel
/// @notice A channel is a logical connection over cross-chain network.
/// It used for cross-chain message transfer.
/// - Accepts messages to be dispatched to destination chains,
/// constructs a Merkle tree of the messages.
/// - Dispatches verified messages from source chains.
/// @dev Messages live in an incremental merkle tree (imt)
/// > A Merkle tree is a binary and complete tree decorated with
/// > the Merkle (hash) attribute.
contract Channel is UserConfig {
using IncrementalMerkleTree for IncrementalMerkleTree.Tree;

/// @dev Incremental merkle tree root which all message hashes live in leafs.
bytes32 public root;
/// @dev Incremental merkle tree.
IncrementalMerkleTree.Tree private _imt;
/// @dev msgHash => isDispathed.
mapping(bytes32 => bool) public dones;

/// @dev message count.
uint256 public count;

/// @dev Self contract address cache.
address private immutable __self = address(this);

/// @dev Notifies an observer that the message has been accepted.
/// @param msgHash Hash of the message.
/// @param root New incremental merkle tree root after a new message inserted.
/// @param message Accepted message info.
event MessageAccepted(bytes32 indexed msgHash, bytes32 root, Message message);
event MessageAccepted(bytes32 indexed msgHash, Message message);
/// @dev Notifies an observer that the message has been dispatched.
/// @param msgHash Hash of the message.
/// @param dispatchResult The message dispatch result.
event MessageDispatched(bytes32 indexed msgHash, bool dispatchResult);

/// @dev Init code.
constructor(address dao) UserConfig(dao) {
// init with empty tree
root = 0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757;
}
constructor(address dao) UserConfig(dao) {}

/// @dev Fetch local chain id.
/// @return chainId Local chain id.
function LOCAL_CHAINID() public view returns (uint256 chainId) {
assembly {
chainId := chainid()
}
function LOCAL_CHAINID() public view returns (uint256) {
return block.chainid;
}

/// @dev Send message.
Expand All @@ -79,12 +66,10 @@ contract Channel is UserConfig {
{
// only cross-chain message
require(toChainId != LOCAL_CHAINID(), "!cross-chain");
// get this message leaf index.
uint256 index = messageCount();
// constuct message object.
Message memory message = Message({
channel: __self,
index: index,
index: count,
fromChainId: LOCAL_CHAINID(),
from: from,
toChainId: toChainId,
Expand All @@ -94,13 +79,12 @@ contract Channel is UserConfig {
});
// hash the message.
bytes32 msgHash = hash(message);
// insert msg hash to imt.
_imt.insert(msgHash);
// update new imt.root to root storage.
root = _imt.root();

// emit accepted message event.
emit MessageAccepted(msgHash, root, message);
emit MessageAccepted(msgHash, message);

// increase message count
count = count + 1;

// return this message hash.
return msgHash;
Expand All @@ -115,34 +99,18 @@ contract Channel is UserConfig {
UC memory uc = getAppConfig(message.to);
// only the config relayer could relay this message.
require(uc.relayer == msg.sender, "!auth");

// hash the message.
bytes32 msgHash = hash(message);
// verify message by the config oracle.
require(IVerifier(uc.oracle).verifyMessageProof(message.fromChainId, msgHash, proof), "!proof");

require(IVerifier(uc.oracle).verifyMessageProof(message, proof), "!proof");
// check destination chain id is correct.
require(LOCAL_CHAINID() == message.toChainId, "!toChainId");
// hash the message.
bytes32 msgHash = hash(message);
// check the message is not dispatched.
require(dones[msgHash] == false, "done");

// set the message is dispatched.
dones[msgHash] = true;

return msgHash;
}

/// @dev Fetch the messages count of incremental merkle tree.
function messageCount() public view returns (uint256) {
return _imt.count;
}

/// @dev Fetch the branch of incremental merkle tree.
function imtBranch() public view returns (bytes32[32] memory) {
return _imt.branch;
}

/// @dev Fetch the latest message proof
function prove() public view returns (bytes32[32] memory) {
return _imt.prove();
}
}
8 changes: 0 additions & 8 deletions src/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ struct Message {
bytes encoded; /*(abi.encodePacked(SELECTOR, PARAMS))*/
}

/// @dev User application custom configuration.
/// @param oracle Oracle contract address.
/// @param relayer Relayer contract address.
struct UC {
address oracle;
address relayer;
}

/// @dev Hash of the message.
function hash(Message memory message) pure returns (bytes32) {
return keccak256(abi.encode(message));
Expand Down
13 changes: 6 additions & 7 deletions src/ORMP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ contract ORMP is ReentrancyGuard, Channel {
event MessageAssigned(
bytes32 indexed msgHash, address indexed oracle, address indexed relayer, uint256 oracleFee, uint256 relayerFee
);
event HashImported(uint256 indexed srcChainId, address indexed oracle, bytes32 indexed lookupKey, bytes32 hash);
event HashImported(address indexed oracle, bytes32 indexed lookupKey, bytes32 indexed hash);

/// oracle => srcChainId => lookupKey => hash
mapping(address => mapping(uint256 => mapping(bytes32 => bytes32))) public hashLookup;
/// oracle => lookupKey => hash
mapping(address => mapping(bytes32 => bytes32)) public hashLookup;

constructor(address dao) Channel(dao) {}

Expand Down Expand Up @@ -73,12 +73,11 @@ contract ORMP is ReentrancyGuard, Channel {
/// @dev Import hash by any oracle address.
/// @notice Hash is an abstract of the proof system, it can be a block hash or a message root hash,
/// specifically provided by oracles.
/// @param srcChainId The source chain Id.
/// @param lookupKey The key for loop up hash.
/// @param hash_ The hash to import.
function importHash(uint256 srcChainId, bytes32 lookupKey, bytes32 hash_) external {
hashLookup[msg.sender][srcChainId][lookupKey] = hash_;
emit HashImported(srcChainId, msg.sender, lookupKey, hash_);
function importHash(bytes32 lookupKey, bytes32 hash_) external {
hashLookup[msg.sender][lookupKey] = hash_;
emit HashImported(msg.sender, lookupKey, hash_);
}

function _handleFee(
Expand Down
8 changes: 7 additions & 1 deletion src/UserConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@

pragma solidity 0.8.17;

import "./Common.sol";
/// @dev User application custom configuration.
/// @param oracle Oracle contract address.
/// @param relayer Relayer contract address.
struct UC {
address oracle;
address relayer;
}

/// @title UserConfig
/// @notice User config could select their own relayer and oracle.
Expand Down
37 changes: 9 additions & 28 deletions src/Verifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,18 @@
pragma solidity 0.8.17;

import "./interfaces/IVerifier.sol";
import "./imt/IncrementalMerkleTree.sol";

abstract contract Verifier is IVerifier {
/// @notice Message proof.
/// @param blockNumber The block number corresponding to the proof.
/// @param messageIndex Leaf index of the message hash in incremental merkle tree.
/// @param messageProof Merkle proof of the message hash.
struct Proof {
uint256 blockNumber;
uint256 messageIndex;
bytes32[32] messageProof;
}

/// @inheritdoc IVerifier
function merkleRoot(uint256 chainId, uint256 blockNumber) public view virtual returns (bytes32);
/// @notice Fetch message hash.
/// @param chainId The source chain id.
/// @param channel The message channel.
/// @param msgIndex The Message index.
/// @return Message hash in source chain.
function hashOf(uint256 chainId, address channel, uint256 msgIndex) public view virtual returns (bytes32);

/// @inheritdoc IVerifier
function verifyMessageProof(uint256 fromChainId, bytes32 msgHash, bytes calldata proof)
external
view
returns (bool)
{
// decode proof
Proof memory p = abi.decode(proof, (Proof));

// fetch message root in block number from chain
bytes32 imtRootOracle = merkleRoot(fromChainId, p.blockNumber);
// calculate the expected root based on the proof
bytes32 imtRootProof = IncrementalMerkleTree.branchRoot(msgHash, p.messageProof, p.messageIndex);

// check oracle's merkle root equal relayer's merkle root
return imtRootOracle == imtRootProof;
function verifyMessageProof(Message calldata message, bytes calldata) external view returns (bool) {
// check oracle's message hash equal relayer's message hash
return hashOf(message.fromChainId, message.channel, message.index) == hash(message);
}
}
25 changes: 16 additions & 9 deletions src/eco/Oracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,23 @@ contract Oracle is Verifier {
}

/// @dev Only could be called by owner.
/// @notice Each channel has a corresponding oracle, and the message root should match with it.
/// @param chainId The source chain id.
/// @param blockNumber The source chain block number.
/// @param messageRoot The source chain message root corresponding to the channel.
function importMessageRoot(uint256 chainId, uint256 blockNumber, bytes32 messageRoot) external onlyOwner {
IORMP(PROTOCOL).importHash(chainId, bytes32(blockNumber), messageRoot);
/// @param channel The message channel.
/// @param msgIndex The source chain message index.
/// @param msgHash The source chain message hash corresponding to the channel.
function importMessageHash(uint256 chainId, address channel, uint256 msgIndex, bytes32 msgHash)
external
onlyOwner
{
IORMP(PROTOCOL).importHash(_lookupkey(chainId, channel, msgIndex), msgHash);
}

function hashOf(uint256 chainId, address channel, uint256 msgIndex) public view override returns (bytes32) {
return IORMP(PROTOCOL).hashLookup(address(this), _lookupkey(chainId, channel, msgIndex));
}

function _lookupkey(uint256 chainId, address channel, uint256 msgIndex) internal pure returns (bytes32) {
return keccak256(abi.encode(chainId, channel, msgIndex));
}

function changeOwner(address newOwner) external onlyOwner {
Expand Down Expand Up @@ -95,8 +106,4 @@ contract Oracle is Verifier {
require(f != 0, "!fee");
return f;
}

function merkleRoot(uint256 chainId, uint256 blockNumber) public view override returns (bytes32) {
return IORMP(PROTOCOL).hashLookup(address(this), chainId, bytes32(blockNumber));
}
}
Loading

0 comments on commit 0d4a84c

Please sign in to comment.