Skip to content

Commit

Permalink
Merge branch 'main' into feat/wormhole
Browse files Browse the repository at this point in the history
  • Loading branch information
allemanfredi committed Nov 23, 2023
2 parents 20d6eb7 + 4093cee commit eed99b2
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 12 deletions.
10 changes: 3 additions & 7 deletions packages/evm/contracts/Yaho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@
pragma solidity ^0.8.17;

import { IMessageRelay } from "./interfaces/IMessageRelay.sol";
import { IMessageDispatcher, Message } from "./interfaces/IMessageDispatcher.sol";
import { Message } from "./interfaces/IMessageDispatcher.sol";
import { IYaho } from "./interfaces/IYaho.sol";
import { MessageHashCalculator } from "./utils/MessageHashCalculator.sol";

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

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

/// @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.
/// @return messageIds An array of message IDs corresponding to the dispatched messages.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

interface ICrossDomainMessenger {
function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external payable;

function xDomainMessageSender() external view returns (address);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";
import { IHeaderStorage } from "../../interfaces/IHeaderStorage.sol";

contract L1CrossDomainMessengerHeaderReporter {
// The first 1.92 million gas on L2 is free. See here:
// https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions
uint32 internal constant GAS_LIMIT = 1_920_000;

ICrossDomainMessenger public immutable l1CrossDomainMessenger;
IHeaderStorage public immutable headerStorage;

event HeaderReported(address indexed emitter, uint256 indexed blockNumber, bytes32 indexed blockHeader);

constructor(ICrossDomainMessenger l1CrossDomainMessenger_, IHeaderStorage headerStorage_) {
l1CrossDomainMessenger = l1CrossDomainMessenger_;
headerStorage = headerStorage_;
}

/// @dev Reports the given block headers to the oracleAdapter via the L1CrossDomainMessenger.
/// @param blockNumbers Uint256 array of block number to pass over the L1CrossDomainMessenger.
/// @param adapter address of L2CrossDomainMessengerAdapter on the destination chain.
function reportHeaders(uint256[] memory blockNumbers, address adapter) external payable {
bytes32[] memory blockHeaders = headerStorage.storeBlockHeaders(blockNumbers);
bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", blockNumbers, blockHeaders);
l1CrossDomainMessenger.sendMessage(adapter, message, GAS_LIMIT);
for (uint256 i = 0; i < blockNumbers.length; i++) {
emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";
import { IYaho } from "../../interfaces/IYaho.sol";

contract L1CrossDomainMessengerMessageRelay {
// The first 1.92 million gas on L2 is free. See here:
// https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions
uint32 internal constant GAS_LIMIT = 1_920_000;

ICrossDomainMessenger public immutable l1CrossDomainMessenger;
IYaho public immutable yaho;

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

constructor(ICrossDomainMessenger l1CrossDomainMessenger_, IYaho yaho_) {
l1CrossDomainMessenger = l1CrossDomainMessenger_;
yaho = yaho_;
}

/// @dev Reports the given messages to the adapter via the L1CrossDomainMessenger.
/// @param messageIds Uint256 array message ids to pass over the L1CrossDomainMessenger.
/// @param adapter address of L2CrossDomainMessengerAdapter on the destination chain.
function relayMessages(uint256[] memory messageIds, address adapter) external 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);
emit MessageRelayed(address(this), messageIds[i]);
}
bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", messageIds, hashes);
l1CrossDomainMessenger.sendMessage{ value: msg.value }(adapter, message, GAS_LIMIT);
return keccak256(abi.encode(true));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";
import { OracleAdapter } from "../OracleAdapter.sol";
import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol";

contract L2CrossDomainMessengerAdapter is OracleAdapter, BlockHashOracleAdapter {
ICrossDomainMessenger public immutable l2CrossDomainMessenger;
address public immutable reporter;
uint256 public immutable chainId;

error ArrayLengthMissmatch(address emitter);
error UnauthorizedHashReporter(address emitter, address reporter);
error UnauthorizedL2CrossDomainMessenger(address emitter, address sender);

constructor(ICrossDomainMessenger l2CrossDomainMessenger_, address reporter_, uint256 chainId_) {
l2CrossDomainMessenger = l2CrossDomainMessenger_;
reporter = reporter_;
chainId = chainId_;
}

/// @dev Check that the l2CrossDomainMessenger and xDomainMessageSender are valid.
modifier onlyValid() {
if (msg.sender != address(l2CrossDomainMessenger))
revert UnauthorizedL2CrossDomainMessenger(address(this), msg.sender);
if (l2CrossDomainMessenger.xDomainMessageSender() != reporter)
revert UnauthorizedHashReporter(address(this), reporter);
_;
}

/// @dev Stores the hashes for a given array of ids.
/// @param ids Array of ids number for which to set the hashes.
/// @param hashes Array of hashes to set for the given ids.
/// @notice Only callable by `l2CrossDomainMessenger` with a message passed from `reporter`.
/// @notice Will revert if given array lengths do not match.
function storeHashes(uint256[] memory ids, bytes32[] memory hashes) external onlyValid {
if (ids.length != hashes.length) revert ArrayLengthMissmatch(address(this));
for (uint256 i = 0; i < ids.length; i++) {
_storeHash(chainId, ids[i], hashes[i]);
}
}
}
12 changes: 12 additions & 0 deletions packages/evm/contracts/interfaces/IHeaderStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

interface IHeaderStorage {
event HeaderStored(uint256 indexed blockNumber, bytes32 indexed blockHeader);

error HeaderOutOfRange(address emitter, uint256 blockNumber);

function storeBlockHeader(uint256 blockNumber) external returns (bytes32 blockHeader);

function storeBlockHeaders(uint256[] memory blockNumbers) external returns (bytes32[] memory);
}
27 changes: 27 additions & 0 deletions packages/evm/contracts/interfaces/IYaho.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { IMessageDispatcher, Message } from "./IMessageDispatcher.sol";

interface IYaho is IMessageDispatcher {
error NoMessagesGiven(address emitter);
error NoMessageIdsGiven(address emitter);
error NoAdaptersGiven(address emitter);
error UnequalArrayLengths(address emitter);

function dispatchMessages(Message[] memory messages) external payable returns (bytes32[] memory);

function relayMessagesToAdapters(
uint256[] memory messageIds,
address[] memory adapters,
address[] memory destinationAdapters
) external payable returns (bytes32[] memory);

function dispatchMessagesToAdapters(
Message[] memory messages,
address[] memory adapters,
address[] memory destinationAdapters
) external payable returns (bytes32[] memory messageIds, bytes32[] memory);

function hashes(uint256) external view returns (bytes32);
}
8 changes: 3 additions & 5 deletions packages/evm/contracts/utils/HeaderStorage.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

contract HeaderStorage {
mapping(uint256 => bytes32) public headers;

event HeaderStored(uint256 indexed blockNumber, bytes32 indexed blockHeader);
import { IHeaderStorage } from "../interfaces/IHeaderStorage.sol";

error HeaderOutOfRange(address emitter, uint256 blockNumber);
contract HeaderStorage is IHeaderStorage {
mapping(uint256 => bytes32) public headers;

/// @dev Stores and returns the header for the given block.
/// @param blockNumber Block number.
Expand Down
1 change: 1 addition & 0 deletions packages/evm/tasks/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Contract } from "ethers"
import { HardhatRuntimeEnvironment } from "hardhat/types"

import "./hashi"
import "./optimism"
import "./replay"
import "./wormhole"

Expand Down
76 changes: 76 additions & 0 deletions packages/evm/tasks/deploy/optimism.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"
import { task, types } from "hardhat/config"
import type { TaskArguments } from "hardhat/types"

import { verify } from "."
import type { L1CrossDomainMessengerHeaderReporter } from "../../types/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter"
import type { L1CrossDomainMessengerMessageRelay } from "../../types/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay"
import type { L2CrossDomainMessengerAdapter } from "../../types/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter"
import { L1CrossDomainMessengerHeaderReporter__factory } from "../../types/factories/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter__factory"
import { L1CrossDomainMessengerMessageRelay__factory } from "../../types/factories/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay__factory"
import { L2CrossDomainMessengerAdapter__factory } from "../../types/factories/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter__factory"

// Deploy on destination chain
task("deploy:Optimism:Adapter")
.addParam("l2CrossDomainMessenger", "address of the L2CrossDomainMessenger contract", undefined, types.string)
.addParam("reporter", "address of the hash reporter", undefined, types.string)
.addParam("chainId", "chainId of the source chain", undefined, types.int)
.addFlag("verify", "whether to verify the contract on Etherscan")
.setAction(async function (taskArguments: TaskArguments, hre) {
console.log("Deploying L2CrossDomainMessengerAdapter...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const l2CrossDomainMessengerAdapterFactory: L2CrossDomainMessengerAdapter__factory = <
L2CrossDomainMessengerAdapter__factory
>await hre.ethers.getContractFactory("L2CrossDomainMessengerAdapter")
const constructorArguments = [
taskArguments.l2CrossDomainMessenger,
taskArguments.reporter,
taskArguments.chainId,
] as const
const l2CrossDomainMessengerAdapter: L2CrossDomainMessengerAdapter = <L2CrossDomainMessengerAdapter>(
await l2CrossDomainMessengerAdapterFactory.connect(signers[0]).deploy(...constructorArguments)
)
await l2CrossDomainMessengerAdapter.deployed()
console.log("L2CrossDomainMessengerAdapter deployed to:", l2CrossDomainMessengerAdapter.address)
if (taskArguments.verify) await verify(hre, l2CrossDomainMessengerAdapter, constructorArguments)
})

// Deploy source chain
task("deploy:Optimism:HeaderReporter")
.addParam("l1CrossDomainMessenger", "address of the L1CrossDomainMessenger contract", undefined, types.string)
.addParam("headerStorage", "address of the header storage contract", undefined, types.string)
.addFlag("verify", "whether to verify the contract on Etherscan")
.setAction(async function (taskArguments: TaskArguments, hre) {
console.log("Deploying L1CrossDomainMessengerHeaderReporter...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const l1CrossDomainMessengerHeaderReporterFactory: L1CrossDomainMessengerHeaderReporter__factory = <
L1CrossDomainMessengerHeaderReporter__factory
>await hre.ethers.getContractFactory("L1CrossDomainMessengerHeaderReporter")
const constructorArguments = [taskArguments.l1CrossDomainMessenger, taskArguments.headerStorage] as const
const l1CrossDomainMessengerHeaderReporter: L1CrossDomainMessengerHeaderReporter = <
L1CrossDomainMessengerHeaderReporter
>await l1CrossDomainMessengerHeaderReporterFactory.connect(signers[0]).deploy(...constructorArguments)
await l1CrossDomainMessengerHeaderReporter.deployed()
console.log("L1CrossDomainMessengerHeaderReporter deployed to:", l1CrossDomainMessengerHeaderReporter.address)
if (taskArguments.verify) await verify(hre, l1CrossDomainMessengerHeaderReporter, constructorArguments)
})

// Deploy source chain
task("deploy:Optimism:MessageRelay")
.addParam("l1CrossDomainMessenger", "address of the L1CrossDomainMessenger contract", undefined, types.string)
.addParam("yaho", "address of the Yaho contract", undefined, types.string)
.addFlag("verify", "whether to verify the contract on Etherscan")
.setAction(async function (taskArguments: TaskArguments, hre) {
console.log("Deploying L1CrossDomainMessengerMessageRelay...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const l1CrossDomainMessengerMessageRelayFactory: L1CrossDomainMessengerMessageRelay__factory = <
L1CrossDomainMessengerMessageRelay__factory
>await hre.ethers.getContractFactory("L1CrossDomainMessengerMessageRelay")
const constructorArguments = [taskArguments.l1CrossDomainMessenger, taskArguments.yaho] as const
const l1CrossDomainMessengerMessageRelay: L1CrossDomainMessengerMessageRelay = <L1CrossDomainMessengerMessageRelay>(
await l1CrossDomainMessengerMessageRelayFactory.connect(signers[0]).deploy(...constructorArguments)
)
await l1CrossDomainMessengerMessageRelay.deployed()
console.log("L1CrossDomainMessengerMessageRelay deployed to:", l1CrossDomainMessengerMessageRelay.address)
if (taskArguments.verify) await verify(hre, l1CrossDomainMessengerMessageRelay, constructorArguments)
})

0 comments on commit eed99b2

Please sign in to comment.