Skip to content

Commit

Permalink
Merge pull request #31 from gnosis/feat/wormhole
Browse files Browse the repository at this point in the history
feat: Wormhole HeaderReporter, MessageRelay & Adapter
  • Loading branch information
allemanfredi authored Nov 23, 2023
2 parents 4093cee + 6b0c6c3 commit 39d6183
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 27 deletions.
35 changes: 19 additions & 16 deletions packages/evm/contracts/adapters/Wormhole/WormholeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,32 @@ import { OracleAdapter } from "../OracleAdapter.sol";
import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol";

contract WormholeAdapter is OracleAdapter, BlockHashOracleAdapter {
IWormhole public wormhole;
bytes32 public headerReporter;
IWormhole public immutable wormhole;
bytes32 public immutable reporter;
uint16 public immutable wormholeSourceChainId;
uint256 public immutable sourceChainId;

error InvalidMessage(address emitter, VM vm, string reason);
error InvalidChainId(address emitter, uint16 chainId);
error InvalidEmitterChainId(address emitter, uint16 chainId);
error InvalidReporter(address emitter, bytes32 reporter);

constructor(IWormhole _wormhole, bytes32 _headerReporter) {
wormhole = _wormhole;
headerReporter = _headerReporter;
constructor(IWormhole wormhole_, address reporter_, uint256 sourceChainId_, uint16 wormholeSourceChainId_) {
wormhole = wormhole_;
reporter = bytes32(uint256(uint160(reporter_)));
sourceChainId = sourceChainId_;
wormholeSourceChainId = wormholeSourceChainId_;
}

/// @dev Stores the block header for a given block.
/// @param blockNumber Identifier for the block for which to set the header.
/// @param chainId Identifier for the
/// @param vm Structured data reflecting the content of the Wormhole Verified Action Approval.
/// @notice Only callable by `wormhole` with a message passed from `headerReporter.
function storeBlockHeader(uint256 blockNumber, uint16 chainId, VM memory vm) public {
(VM memory _vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(abi.encode(vm));
/// @param encodedVM Encoded data reflecting the content of the Wormhole Verified Action Approval.
function storeHashesByEncodedVM(bytes calldata encodedVM) external {
(VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVM);
if (!valid) revert InvalidMessage(address(this), vm, reason);
if (_vm.emitterChainId != chainId) revert InvalidChainId(address(this), _vm.emitterChainId);
if (_vm.emitterAddress != headerReporter) revert InvalidReporter(address(this), _vm.emitterAddress);
bytes32 newHash = abi.decode(_vm.payload, (bytes32));
_storeHash(uint256(chainId), blockNumber, newHash);
if (vm.emitterChainId != wormholeSourceChainId) revert InvalidEmitterChainId(address(this), vm.emitterChainId);
if (vm.emitterAddress != reporter) revert InvalidReporter(address(this), vm.emitterAddress);
(uint256[] memory ids, bytes32[] memory _hashes) = abi.decode(vm.payload, (uint256[], bytes32[]));
for (uint256 i = 0; i < ids.length; i++) {
_storeHash(sourceChainId, ids[i], _hashes[i]);
}
}
}
24 changes: 13 additions & 11 deletions packages/evm/contracts/adapters/Wormhole/WormholeHeaderReporter.sol
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { HeaderStorage } from "../../utils/HeaderStorage.sol";
import { IHeaderStorage } from "../../interfaces/IHeaderStorage.sol";
import { IWormhole } from "./IWormhole.sol";

contract WormholeHeaderReporter {
IWormhole public immutable wormhole;
address public oracleAdapter;
HeaderStorage public immutable headerStorage;
IHeaderStorage public immutable headerStorage;

constructor(IWormhole _wormhole, HeaderStorage _headerStorage) {
wormhole = _wormhole;
headerStorage = _headerStorage;
constructor(IWormhole wormhole_, IHeaderStorage headerStorage_) {
wormhole = wormhole_;
headerStorage = headerStorage_;
}

/// @dev Reports the given block header to the oracleAdapter via the Wormhole.
/// @param blockNumber Uint256 block number to pass over the Wormhole.
/// @dev Reports the given block header to the adapter via the Wormhole.
/// @param blockNumbers Uint256 array of block numbers to pass over the Wormhole.
/// @param sequence Uint64 value used to retrive generated VAA from the wormhole network.
function reportHeader(uint256 blockNumber) public returns (uint64 sequence) {
bytes32 blockHeader = headerStorage.storeBlockHeader(blockNumber);
bytes memory payload = abi.encodeWithSignature("storeBlockHeader(uint256,bytes32)", blockNumber, blockHeader);
function reportHeaders(uint256[] calldata blockNumbers) external returns (uint64 sequence) {
bytes32[] memory blockHeaders = new bytes32[](blockNumbers.length);
for (uint256 i = 0; i < blockNumbers.length; i++) {
blockHeaders[i] = headerStorage.storeBlockHeader(blockNumbers[i]);
}
bytes memory payload = abi.encode(blockNumbers, blockHeaders);
uint32 nonce = 0;
uint8 consistencyLevel = 201;
sequence = wormhole.publishMessage(nonce, payload, consistencyLevel);
Expand Down
30 changes: 30 additions & 0 deletions packages/evm/contracts/adapters/Wormhole/WormholeMessageRelay.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

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

contract WormholeMessageRelay {
IWormhole public immutable wormhole;
IYaho public immutable yaho;

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

constructor(IWormhole wormhole_, IYaho yaho_) {
wormhole = wormhole_;
yaho = yaho_;
}

function relayMessages(uint256[] memory messageIds, address) external payable returns (bytes32) {
bytes32[] memory hashes = new bytes32[](messageIds.length);
for (uint256 i = 0; i < messageIds.length; i++) {
hashes[i] = yaho.hashes(messageIds[i]);
emit MessageRelayed(address(this), messageIds[i]);
}
bytes memory payload = abi.encode(messageIds, hashes);
uint32 nonce = 0;
uint8 consistencyLevel = 201;
uint64 sequence = wormhole.publishMessage(nonce, payload, consistencyLevel);
return bytes32(abi.encode(sequence));
}
}
1 change: 1 addition & 0 deletions packages/evm/tasks/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"
import "./hashi"
import "./optimism"
import "./replay"
import "./wormhole"

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const verify = async (hre: HardhatRuntimeEnvironment, contract: Contract, constructorArguments: any = []) => {
Expand Down
78 changes: 78 additions & 0 deletions packages/evm/tasks/deploy/wormhole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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 { WormholeAdapter } from "../../types/contracts/adapters/Wormhole/WormholeAdapter"
import type { WormholeHeaderReporter } from "../../types/contracts/adapters/Wormhole/WormholeHeaderReporter"
import type { WormholeMessageRelay } from "../../types/contracts/adapters/Wormhole/WormholeMessageRelay"
import type { WormholeAdapter__factory } from "../../types/factories/contracts/adapters/Wormhole/WormholeAdapter__factory"
import type { WormholeHeaderReporter__factory } from "../../types/factories/contracts/adapters/Wormhole/WormholeHeaderReporter__factory"
import type { WormholeMessageRelay__factory } from "../../types/factories/contracts/adapters/Wormhole/WormholeMessageRelay__factory"

// Deploy on destination chain
task("deploy:Wormhole:Adapter")
.addParam("wormhole", "address of the Wormhole contract", undefined, types.string)
.addParam("reporter", "address of the hash reporter", undefined, types.string)
.addParam("chainId", "source chain id", undefined, types.string)
.addParam("wormholeChainId", "wormhole source chain id", undefined, types.string)
.addFlag("verify", "whether to verify the contract on Etherscan")
.setAction(async function (taskArguments: TaskArguments, hre) {
console.log("Deploying WormholeAdapter...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const wormholeAdapterFactory: WormholeAdapter__factory = <WormholeAdapter__factory>(
await hre.ethers.getContractFactory("WormholeAdapter")
)
const constructorArguments = [
taskArguments.wormhole,
taskArguments.reporter,
taskArguments.chainId,
taskArguments.wormholeChainId,
] as const
const wormholeAdapter: WormholeAdapter = <WormholeAdapter>(
await wormholeAdapterFactory.connect(signers[0]).deploy(...constructorArguments)
)
await wormholeAdapter.deployed()
console.log("WormholeAdapter deployed to:", wormholeAdapter.address)
if (taskArguments.verify) await verify(hre, wormholeAdapter, constructorArguments)
})

// Deploy source chain
task("deploy:Wormhole:HeaderReporter")
.addParam("wormhole", "address of the Wormhole 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 WormholeHeaderReporter...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const wormholeHeaderReporterFactory: WormholeHeaderReporter__factory = <WormholeHeaderReporter__factory>(
await hre.ethers.getContractFactory("WormholeHeaderReporter")
)
const constructorArguments = [taskArguments.wormhole, taskArguments.headerStorage] as const
const wormholeHeaderReporter: WormholeHeaderReporter = <WormholeHeaderReporter>(
await wormholeHeaderReporterFactory.connect(signers[0]).deploy(...constructorArguments)
)
await wormholeHeaderReporter.deployed()
console.log("WormholeHeaderReporter deployed to:", wormholeHeaderReporter.address)
if (taskArguments.verify) await verify(hre, wormholeHeaderReporter, constructorArguments)
})

// Deploy source chain
task("deploy:Wormhole:MessageRelay")
.addParam("wormhole", "address of the Wormhole 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 WormholeMessageRelay...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const wormholeMessageRelayFactory: WormholeMessageRelay__factory = <WormholeMessageRelay__factory>(
await hre.ethers.getContractFactory("WormholeMessageRelay")
)
const constructorArguments = [taskArguments.wormhole, taskArguments.yaho] as const
const wormholeMessageRelay: WormholeMessageRelay = <WormholeMessageRelay>(
await wormholeMessageRelayFactory.connect(signers[0]).deploy(...constructorArguments)
)
await wormholeMessageRelay.deployed()
console.log("WormholeMessageRelay deployed to:", wormholeMessageRelay.address)
if (taskArguments.verify) await verify(hre, wormholeMessageRelay, constructorArguments)
})

0 comments on commit 39d6183

Please sign in to comment.