Skip to content

Commit

Permalink
feat(evm): starts adding support to EIP-5164
Browse files Browse the repository at this point in the history
  • Loading branch information
allemanfredi committed Nov 1, 2023
1 parent c1fc772 commit 195a2c0
Show file tree
Hide file tree
Showing 37 changed files with 260 additions and 161 deletions.
6 changes: 3 additions & 3 deletions packages/evm/contracts/Hashi.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ contract Hashi {
function getHashFromOracle(
IOracleAdapter oracleAdapter,
uint256 domain,
uint256 id
bytes32 id
) public view returns (bytes32 hash) {
hash = oracleAdapter.getHashFromOracle(domain, id);
}
Expand All @@ -61,7 +61,7 @@ contract Hashi {
function getHashesFromOracles(
IOracleAdapter[] memory oracleAdapters,
uint256 domain,
uint256 id
bytes32 id
) public view returns (bytes32[] memory) {
if (oracleAdapters.length == 0) revert NoOracleAdaptersGiven(address(this));
bytes32[] memory hashes = new bytes32[](oracleAdapters.length);
Expand All @@ -79,7 +79,7 @@ contract Hashi {
/// @notice MUST revert if oracles disagree on the hash or if an oracle does not report.
function getHash(
uint256 domain,
uint256 id,
bytes32 id,
IOracleAdapter[] memory oracleAdapters
) public view returns (bytes32 hash) {
if (oracleAdapters.length == 0) revert NoOracleAdaptersGiven(address(this));
Expand Down
94 changes: 71 additions & 23 deletions packages/evm/contracts/Yaho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,77 @@ pragma solidity ^0.8.17;
import { IMessageRelay } from "./interfaces/IMessageRelay.sol";
import { IMessageDispatcher, Message } from "./interfaces/IMessageDispatcher.sol";
import { MessageHashCalculator } from "./utils/MessageHashCalculator.sol";
import { MessageIdCalculator } from "./utils/MessageIdCalculator.sol";

contract Yaho is IMessageDispatcher, MessageHashCalculator {
mapping(uint256 => bytes32) public hashes;
uint256 private count;
contract Yaho is IMessageDispatcher, MessageHashCalculator, MessageIdCalculator {
mapping(bytes32 => bytes32) public hashes;
uint256 private messageNonce;

error NoMessagesGiven(address emitter);
error NoMessageIdsGiven(address emitter);
error NoAdaptersGiven(address emitter);
error UnequalArrayLengths(address emitter);

/// @dev Dispatches a message using the EIP-5164 standard, putting their into storage and emitting their contents as an event.
/// @param toChainId The destination chain id.
/// @param to The target contract.
/// @param data The message data.
/// @return messageId A message ID corresponding to the dispatched message.
function dispatchMessage(
uint256 toChainId,
address to,
bytes calldata data
) external payable returns (bytes32 messageId) {
unchecked {
Message memory message = Message({
from: msg.sender,
to: to,
fromChainId: block.chainid,
toChainId: toChainId,
data: data
});
messageId = calculateMessageId(block.chainid, address(this), messageNonce);
hashes[messageId] = calculateMessageHash(messageId, message);
emit MessageDispatched(messageId, msg.sender, toChainId, to, data);
++messageNonce;
}
}

/// @dev Dispatches a batch of messages, putting their into storage and emitting their contents as an event.
/// @param messages An array of Messages to be dispatched.
/// @param toChainIds The destination chain ids.
/// @param tos The target contracts.
/// @param data The message data for each message.
/// @return messageIds An array of message IDs corresponding to the dispatched messages.
function dispatchMessages(Message[] memory messages) public payable returns (bytes32[] memory) {
if (messages.length == 0) revert NoMessagesGiven(address(this));
bytes32[] memory messageIds = new bytes32[](messages.length);
for (uint256 i = 0; i < messages.length; i++) {
uint256 id = count;
hashes[id] = calculateHash(block.chainid, id, address(this), msg.sender, messages[i]);
messageIds[i] = bytes32(id);
emit MessageDispatched(bytes32(id), msg.sender, messages[i].toChainId, messages[i].to, messages[i].data);
count++;
function dispatchMessages(
uint256[] calldata toChainIds,
address[] calldata tos,
bytes[] calldata data
) public payable returns (bytes32[] memory) {
if (toChainIds.length != tos.length || toChainIds.length != data.length)
revert UnequalArrayLengths(address(this));

bytes32[] memory messageIds = new bytes32[](toChainIds.length);

uint256 currentMessageNonce = messageNonce;
for (uint256 i = 0; i < toChainIds.length; ) {
unchecked {
Message memory message = Message({
from: msg.sender,
to: tos[i],
fromChainId: block.chainid,
toChainId: toChainIds[i],
data: data[i]
});

bytes32 messageId = calculateMessageId(block.chainid, address(this), currentMessageNonce);
hashes[messageId] = calculateMessageHash(messageId, message);
messageIds[i] = messageId;
emit MessageDispatched(messageId, msg.sender, message.toChainId, message.to, message.data);
++currentMessageNonce;
++i;
}
}

messageNonce = currentMessageNonce;
return messageIds;
}

Expand All @@ -36,7 +84,7 @@ contract Yaho is IMessageDispatcher, MessageHashCalculator {
/// @param destinationAdapters Array of oracle adapter addresses to receive hashes.
/// @return adapterReciepts Reciepts from each of the relay adapters.
function relayMessagesToAdapters(
uint256[] memory messageIds,
bytes32[] memory messageIds,
address[] memory adapters,
address[] memory destinationAdapters
) external payable returns (bytes32[] memory) {
Expand All @@ -51,25 +99,25 @@ contract Yaho is IMessageDispatcher, MessageHashCalculator {
}

/// @dev Dispatches an array of messages and relays their hashes to an array of relay adapters.
/// @param messages An array of Messages to be dispatched.
/// @param toChainIds The destination chain ids.
/// @param tos The target contracts.
/// @param data The message data for each message.
/// @param adapters Array of relay adapter addresses to which hashes should be relayed.
/// @param destinationAdapters Array of oracle adapter addresses to receive hashes.
/// @return messageIds An array of message IDs corresponding to the dispatched messages.
/// @return adapterReciepts Reciepts from each of the relay adapters.
function dispatchMessagesToAdapters(
Message[] memory messages,
uint256[] calldata toChainIds,
address[] calldata tos,
bytes[] calldata data,
address[] memory adapters,
address[] memory destinationAdapters
) external payable returns (bytes32[] memory messageIds, bytes32[] memory) {
if (adapters.length == 0) revert NoAdaptersGiven(address(this));
messageIds = dispatchMessages(messages);
uint256[] memory uintIds = new uint256[](messageIds.length);
for (uint256 i = 0; i < messageIds.length; i++) {
uintIds[i] = uint256(messageIds[i]);
}
messageIds = dispatchMessages(toChainIds, tos, data);
bytes32[] memory adapterReciepts = new bytes32[](adapters.length);
for (uint256 i = 0; i < adapters.length; i++) {
adapterReciepts[i] = IMessageRelay(adapters[i]).relayMessages(uintIds, destinationAdapters[i]);
adapterReciepts[i] = IMessageRelay(adapters[i]).relayMessages(messageIds, destinationAdapters[i]);
}
return (messageIds, adapterReciepts);
}
Expand Down
64 changes: 27 additions & 37 deletions packages/evm/contracts/Yaru.sol
Original file line number Diff line number Diff line change
@@ -1,69 +1,59 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { IHashi, IOracleAdapter } from "./interfaces/IHashi.sol";
import { Message } from "./interfaces/IMessage.sol";
import { IMessageExecutor } from "./interfaces/IMessageExecutor.sol";
import { MessageHashCalculator } from "./utils/MessageHashCalculator.sol";
import { IHashiReceiver } from "./interfaces/IHashiReceiver.sol";

contract Yaru is IMessageExecutor, MessageHashCalculator {
contract Yaru is IMessageExecutor, MessageHashCalculator, ReentrancyGuard {
IHashi public immutable hashi;
address public immutable yaho;
uint256 public immutable chainId;
mapping(uint256 => bool) public executed;
address public sender;
mapping(bytes32 => bool) public executed;

error ReentranceNotAllowed(address);
error UnequalArrayLengths(address emitter);
error AlreadyExecuted(address emitter, uint256 id);
error InvalidSender(address emitter, address sender);
error HashMismatch(address emitter, uint256 id, bytes32 reportedHash, bytes32 calculatedHash);
error CallFailed(address emitter, uint256 id);
error MessageIdAlreadyExecuted(bytes32 messageId);
error MessageFailure(bytes32 messageId, bytes errorData);

/// @param _hashi Address of the Hashi contract.
/// @param _yaho Address of the Yaho contract
constructor(IHashi _hashi, address _yaho, uint256 _chainId) {
constructor(IHashi _hashi) {
hashi = _hashi;
yaho = _yaho;
chainId = _chainId;
}

/// @dev Executes messages validated by a given set of oracle adapters
/// @param messages Array of messages to execute.
/// @param messageIds Array of corresponding messageIds to query for hashes from the given oracle adapters.
/// @param senders Array of addresses that sent the corresponding messages.
/// @param oracleAdapters Array of oracle adapters to query.
/// @return returnDatas Array of data returned from each executed message.
function executeMessages(
Message[] memory messages,
uint256[] memory messageIds,
address[] memory senders,
bytes32[] memory messageIds,
IOracleAdapter[] memory oracleAdapters
) public returns (bytes[] memory) {
if (sender != address(0)) revert ReentranceNotAllowed(address(0));
if (messages.length != senders.length || messages.length != messageIds.length)
revert UnequalArrayLengths(address(this));
) external nonReentrant returns (bytes[] memory) {
if (messages.length != messageIds.length) revert UnequalArrayLengths(address(this));
bytes[] memory returnDatas = new bytes[](messages.length);
for (uint256 i = 0; i < messages.length; i++) {
uint256 id = messageIds[i];
bytes32 messageId = messageIds[i];

if (executed[id]) revert AlreadyExecuted(address(this), id);
if (senders[i] == address(0)) revert InvalidSender(address(this), senders[i]);

executed[id] = true;
if (executed[messageId]) revert MessageIdAlreadyExecuted(messageId);
executed[messageId] = true;

Message memory message = messages[i];
sender = senders[i];
bytes32 reportedHash = hashi.getHash(chainId, id, oracleAdapters);
bytes32 calculatedHash = calculateHash(chainId, id, yaho, sender, message);
if (reportedHash != calculatedHash) revert HashMismatch(address(this), id, reportedHash, calculatedHash);

(bool success, bytes memory returnData) = address(message.to).call(message.data);
if (!success) revert CallFailed(address(this), id);

delete sender;
returnDatas[i] = returnData;
emit MessageIdExecuted(message.toChainId, bytes32(id));
bytes32 reportedHash = hashi.getHash(message.fromChainId, messageId, oracleAdapters);
bytes32 calculatedHash = calculateMessageHash(messageId, message);
if (reportedHash != calculatedHash)
revert MessageFailure(messageId, abi.encode(reportedHash, calculatedHash));

try
IHashiReceiver(message.to).onMessage(message.data, messageId, message.fromChainId, message.from)
returns (bytes memory returnData) {
returnDatas[i] = returnData;
} catch {
revert MessageFailure(messageId, abi.encode(0));
}

emit MessageIdExecuted(message.fromChainId, messageId);
}
return returnDatas;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/evm/contracts/adapters/AMB/AMBAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ contract AMBAdapter is OracleAdapter, BlockHashOracleAdapter {
}

/// @dev Stores the hashes for a given array of idss.
/// @param ids Array of ids number for which to set the hashes.
/// @param ids Array of ids for which to set the hashes.
/// @param _hashes Array of hashes to set for the given ids.
/// @notice Only callable by `amb` with a message passed from `reporter.
/// @notice Will revert if given array lengths do not match.
function storeHashes(uint256[] memory ids, bytes32[] memory _hashes) public onlyValid {
function storeHashes(bytes32[] memory ids, bytes32[] memory _hashes) public onlyValid {
if (ids.length != _hashes.length) revert ArrayLengthMissmatch(address(this));
for (uint256 i = 0; i < ids.length; i++) {
_storeHash(uint256(chainId), ids[i], _hashes[i]);
Expand Down
10 changes: 9 additions & 1 deletion packages/evm/contracts/adapters/AMB/AMBHeaderReporter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ contract AMBHeaderReporter {
uint256 gas
) public returns (bytes32 receipt) {
bytes32[] memory blockHeaders = headerStorage.storeBlockHeaders(blockNumbers);
bytes memory data = abi.encodeCall(AMBAdapter.storeHashes, (blockNumbers, blockHeaders));
bytes32[] memory bBlockNumbers = new bytes32[](blockNumbers.length);
for (uint256 i = 0; i < blockNumbers.length; ) {
bBlockNumbers[i] = bytes32(blockNumbers[i]);
unchecked {
++i;
}
}

bytes memory data = abi.encodeCall(AMBAdapter.storeHashes, (bBlockNumbers, blockHeaders));
receipt = amb.requireToPassMessage(ambAdapter, data, gas);
for (uint256 i = 0; i < blockNumbers.length; i++) {
emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]);
Expand Down
7 changes: 3 additions & 4 deletions packages/evm/contracts/adapters/AMB/AMBMessageRelayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ contract AMBMessageRelay is IMessageRelay {
IAMB public immutable amb;
Yaho public immutable yaho;

event MessageRelayed(address indexed emitter, uint256 indexed messageId);
event MessageRelayed(address indexed emitter, bytes32 indexed messageId);

constructor(IAMB _amb, Yaho _yaho) {
amb = _amb;
yaho = _yaho;
}

function relayMessages(uint256[] memory messageIds, address ambAdapter) public payable returns (bytes32 receipt) {
function relayMessages(bytes32[] memory messageIds, address ambAdapter) public payable returns (bytes32 receipt) {
bytes32[] memory hashes = new bytes32[](messageIds.length);
for (uint256 i = 0; i < messageIds.length; i++) {
uint256 id = messageIds[i];
hashes[i] = yaho.hashes(id);
hashes[i] = yaho.hashes(messageIds[i]);
emit MessageRelayed(address(this), messageIds[i]);
}
bytes memory data = abi.encodeCall(AMBAdapter.storeHashes, (messageIds, hashes));
Expand Down
4 changes: 2 additions & 2 deletions packages/evm/contracts/adapters/BlockHashOracleAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ abstract contract BlockHashOracleAdapter is OracleAdapter {
uint256 blockNumber = uint256(blockHeaderContent[8].toUint());

bytes32 reportedBlockHash = keccak256(blockHeaders[i]);
bytes32 storedBlockHash = hashes[chainId][blockNumber];
bytes32 storedBlockHash = hashes[chainId][bytes32(blockNumber)];

if (reportedBlockHash != storedBlockHash)
revert ConflictingBlockHeader(blockNumber, reportedBlockHash, storedBlockHash);

_storeHash(chainId, blockNumber - 1, blockParent);
_storeHash(chainId, bytes32(blockNumber - 1), blockParent);
}
}
}
2 changes: 1 addition & 1 deletion packages/evm/contracts/adapters/Connext/ConnextAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ contract ConnextAdapter is OracleAdapter, BlockHashOracleAdapter, IXReceiver {
bytes memory _callData
) external onlySource(_originSender, _origin) returns (bytes memory) {
// Unpack the _callData
(uint256 blockNumber, bytes32 newBlockHeader) = abi.decode(_callData, (uint256, bytes32));
(bytes32 blockNumber, bytes32 newBlockHeader) = abi.decode(_callData, (bytes32, bytes32));
_storeHash(uint256(domainToChainId[_origin]), blockNumber, newBlockHeader);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract ConnextHeaderReporter {
/// @param transferId Uint64 value used to retrive transfer from the Connext network.
function reportHeader(uint256 blockNumber) public returns (bytes32 transferId) {
bytes32 blockHeader = headerStorage.storeBlockHeader(blockNumber);
bytes memory callData = abi.encode(blockNumber, blockHeader);
bytes memory callData = abi.encode(bytes32(blockNumber), blockHeader);
transferId = connext.xcall{ value: 0 }(
destinationDomain, // _destination: Domain ID of the destination chain
target, // _to: address of the target contract
Expand Down
4 changes: 2 additions & 2 deletions packages/evm/contracts/adapters/DendrETH/DendrETHAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract DendrETHAdapter is BlockHashOracleAdapter {
revert InvalidBlockHashProof();
}

_storeHash(uint256(_chainId), _blockNumber, _blockHash);
_storeHash(uint256(_chainId), bytes32(_blockNumber), _blockHash);
}

/// @notice Updates DendrETH Light client and stores the given block
Expand Down Expand Up @@ -90,6 +90,6 @@ contract DendrETHAdapter is BlockHashOracleAdapter {
revert InvalidBlockHashProof();
}

_storeHash(uint256(_chainId), _blockNumber, _blockHash);
_storeHash(uint256(_chainId), bytes32(_blockNumber), _blockHash);
}
}
6 changes: 3 additions & 3 deletions packages/evm/contracts/adapters/OracleAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ pragma solidity ^0.8.17;
import { IOracleAdapter } from "../interfaces/IOracleAdapter.sol";

abstract contract OracleAdapter is IOracleAdapter {
mapping(uint256 => mapping(uint256 => bytes32)) public hashes;
mapping(uint256 => mapping(bytes32 => bytes32)) public hashes;

/// @dev Returns the hash for a given domain and ID, as reported by the oracle.
/// @param domain Identifier for the domain to query.
/// @param id Identifier for the ID to query.
/// @return hash Bytes32 hash reported by the oracle for the given ID on the given domain.
/// @notice MUST return bytes32(0) if the oracle has not yet reported a hash for the given ID.
function getHashFromOracle(uint256 domain, uint256 id) external view returns (bytes32 hash) {
function getHashFromOracle(uint256 domain, bytes32 id) external view returns (bytes32 hash) {
hash = hashes[domain][id];
}

/// @dev Stores a hash for a given domain and ID.
/// @param domain Identifier for the domain.
/// @param id Identifier for the ID of the hash.
/// @param hash Bytes32 hash value to store.
function _storeHash(uint256 domain, uint256 id, bytes32 hash) internal {
function _storeHash(uint256 domain, bytes32 id, bytes32 hash) internal {
bytes32 currentHash = hashes[domain][id];
if (currentHash != hash) {
hashes[domain][id] = hash;
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/contracts/adapters/Sygma/SygmaAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ contract SygmaAdapter is AccessControl, OracleAdapter, BlockHashOracleAdapter {
@notice Only callable by `_handler` with a message passed from an authorized reporter.
@notice Will revert if array lengths do not match.
*/
function storeHashes(address reporterAddress, uint256[] calldata ids, bytes32[] calldata hashes) public {
function storeHashes(address reporterAddress, bytes32[] calldata ids, bytes32[] calldata hashes) public {
if (ids.length != hashes.length) revert ArrayLengthMismatch();
if (msg.sender != _handler) revert InvalidHandler(msg.sender);

Expand Down
Loading

0 comments on commit 195a2c0

Please sign in to comment.