-
Notifications
You must be signed in to change notification settings - Fork 0
Add proofcast adapter #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/v0.2.0
Are you sure you want to change the base?
Changes from all commits
245d3ad
7e61434
a9b3670
86a3c5a
e926469
3d6f712
0b03bde
85dfb0d
e56155e
96dd261
7244cd9
7dd36f0
41ff0b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| // SPDX-License-Identifier: LGPL-3.0-only | ||
| pragma solidity ^0.8.20; | ||
|
|
||
| import { Message } from "../../interfaces/IMessage.sol"; | ||
| import { MessageIdCalculator } from "../../utils/MessageIdCalculator.sol"; | ||
| import { MessageHashCalculator } from "../../utils/MessageHashCalculator.sol"; | ||
| import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
| import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
| import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol"; | ||
| import { IERC777Recipient } from "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; | ||
| import { IERC1820RegistryUpgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused, can be remove. |
||
| import { BlockHashAdapter } from "../BlockHashAdapter.sol"; | ||
|
|
||
| contract ProofcastAdapter is BlockHashAdapter, MessageIdCalculator, MessageHashCalculator, Ownable { | ||
| string public constant PROVIDER = "proofcast"; | ||
|
|
||
| // MessageDispatched(uint256 indexed messageId, Message message) | ||
| bytes32 public constant MESSAGE_DISPATCHED_EVENT_TOPIC = | ||
| 0x218247aabc759e65b5bb92ccc074f9d62cd187259f2a0984c3c9cf91f67ff7cf; | ||
| uint256 public constant TEE_ADDRESS_CHANGE_GRACE_PERIOD = 48 hours; | ||
|
|
||
| address public teeAddress; | ||
| address public teeAddressNew; | ||
| uint256 public teeAddressChangeGraceThreshold; | ||
| mapping(uint256 => address) public yahos; | ||
|
|
||
| error NotUsableYaho(); | ||
| error InvalidEventRLP(); | ||
| error InvalidTeeSigner(); | ||
| error InvalidSignature(); | ||
| error InvalidYahoAddress(); | ||
| error InvalidEventContentLength(uint256 length); | ||
| error UnsupportedProtocolId(bytes1 protocolId); | ||
| error UnsupportedChainId(uint256 chainId); | ||
| error UnexpectedEventTopic(bytes32 topic); | ||
| error InvalidSender(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused, can be remove. |
||
| error InvalidMessageId(uint256 actual, uint256 expected); | ||
| error InvalidDestinationChainId(uint256 chainId); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused, can be remove. |
||
| error GracePeriodNotElapsed(); | ||
| error InvalidNewTeeSigner(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Out of completeness you could include a test for this new error type as well, although it is not necessary, as |
||
|
|
||
| event TeeSignerChanged(address newAddress); | ||
| event TeeSignerPendingChange(address newAddress, bytes attestation, uint256 gracePeriod); | ||
| event YahoInitialized(uint256 chainId, address yaho); | ||
|
|
||
| function initYaho(uint256 chainId, address yaho_) public onlyOwner { | ||
| if (chainId == block.chainid) { | ||
| revert NotUsableYaho(); | ||
| } | ||
| yahos[chainId] = yaho_; | ||
| emit YahoInitialized(chainId, yaho_); | ||
| } | ||
|
|
||
| function setTeeSigner(bytes calldata pubKey, bytes memory attestation) public onlyOwner { | ||
| if (teeAddress == address(0)) { | ||
| // Setting the teeAddress the first time | ||
| teeAddress = _getAddressFromPublicKey(pubKey); | ||
| emit TeeSignerPendingChange(teeAddress, attestation, block.timestamp); | ||
| emit TeeSignerChanged(teeAddress); | ||
| } else { | ||
| // The new address will be set after a grace period of 48 hours | ||
| teeAddressNew = _getAddressFromPublicKey(pubKey); | ||
| teeAddressChangeGraceThreshold = block.timestamp + TEE_ADDRESS_CHANGE_GRACE_PERIOD; | ||
| emit TeeSignerPendingChange(teeAddressNew, attestation, teeAddressChangeGraceThreshold); | ||
| } | ||
| } | ||
|
|
||
| function applyNewTeeSigner() external { | ||
| if (block.timestamp < teeAddressChangeGraceThreshold) revert GracePeriodNotElapsed(); | ||
| if (teeAddressNew == address(0)) revert InvalidNewTeeSigner(); | ||
|
|
||
| teeAddress = teeAddressNew; | ||
| teeAddressNew = address(0); | ||
|
|
||
| emit TeeSignerChanged(teeAddress); | ||
| } | ||
|
|
||
| function verifyEventAndStoreHash(bytes calldata statement, bytes memory signature) external { | ||
| if (teeAddress == address(0)) revert InvalidTeeSigner(); | ||
| if (ECDSA.recover(sha256(statement), signature) != teeAddress) revert InvalidSignature(); | ||
|
|
||
| // Supports only EVM events | ||
| bytes1 protocolId = statement[1]; | ||
| if (protocolId != 0x01) revert UnsupportedProtocolId(protocolId); | ||
|
|
||
| (uint256 domain, uint256[] memory ids, bytes32[] memory hashes) = _checkEventAndDecodeData(statement); | ||
| _storeHashes(domain, ids, hashes); | ||
| } | ||
|
|
||
| function _checkEventAndDecodeData( | ||
| bytes calldata statement | ||
| ) internal view returns (uint256 domain, uint256[] memory ids, bytes32[] memory hashes) { | ||
| // Statement format: | ||
| // | version | protocol | protocol_chain_id | event id | event_bytes | | ||
| // | (1 byte) | (1 byte) | (32 bytes) | (32 bytes) | varlen | | ||
|
|
||
| uint16 offset = 2; // skip version, protocolId | ||
| domain = uint256(bytes32(statement[offset:(offset += 32)])); | ||
|
|
||
| if (yahos[domain] == address(0)) revert UnsupportedChainId(domain); | ||
|
|
||
| offset += 32; // skip the event id (32 bytes) | ||
| bytes memory eventBytes = statement[offset:]; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the statement format, you should have the event id at this offset.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't it skipped on line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My bad, I overlooked |
||
| RLPReader.RLPItem memory eventRLP = RLPReader.toRlpItem(eventBytes); | ||
| if (!RLPReader.isList(eventRLP)) revert InvalidEventRLP(); | ||
|
|
||
| RLPReader.RLPItem[] memory eventContent = RLPReader.toList(eventRLP); | ||
|
|
||
| // Event must contain address, logs and data | ||
| if (eventContent.length != 3) revert InvalidEventContentLength(eventContent.length); | ||
ubordignon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // MessageDispatched event parsing | ||
| address yahoAddress = RLPReader.toAddress(eventContent[0]); | ||
| if (yahoAddress != yahos[domain]) revert InvalidYahoAddress(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no test for this revert. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is my last comment, everything else LGTM!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, thank you, I'll sort it out in the afternoon |
||
|
|
||
| RLPReader.RLPItem[] memory logs = RLPReader.toList(eventContent[1]); | ||
|
|
||
| bytes32 topic = bytes32(RLPReader.toBytes(logs[0])); | ||
| if (topic != MESSAGE_DISPATCHED_EVENT_TOPIC) revert UnexpectedEventTopic(topic); | ||
|
|
||
| bytes memory messageBytes = RLPReader.toBytes(eventContent[2]); | ||
| Message memory message = abi.decode(messageBytes, (Message)); | ||
| bytes32 messageHash = calculateMessageHash(message); | ||
| uint256 expectedMessageId = calculateMessageId(domain, yahoAddress, messageHash); | ||
|
|
||
| uint256 messageId = uint256(bytes32(RLPReader.toBytes(logs[1]))); | ||
| if (messageId != expectedMessageId) revert InvalidMessageId(messageId, expectedMessageId); | ||
| ids = new uint256[](1); | ||
| hashes = new bytes32[](1); | ||
| ids[0] = messageId; | ||
| hashes[0] = messageHash; | ||
| } | ||
|
|
||
| function _getAddressFromPublicKey(bytes calldata pubKey) internal pure returns (address) { | ||
| return address(uint160(uint256(keccak256(pubKey[1:])))); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { adapters } from "@hyperlane-xyz/core/dist/contracts/middleware/liquidity-layer" | ||
| import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" | ||
| import { task } from "hardhat/config" | ||
| import type { TaskArguments } from "hardhat/types" | ||
|
|
||
| import type { ProofcastAdapter } from "../../../types/contracts/adapters/Proofcast/ProofcastAdapter" | ||
| import type { ProofcastAdapter__factory } from "../../../types/factories/contracts/adapters/Proofcast/ProofcastAdapter__factory" | ||
| import { verify } from "../index" | ||
|
|
||
| task("deploy:adapter:ProofcastAdapter") | ||
| .addFlag("verify", "whether to verify the contract on Etherscan") | ||
| .setAction(async function (taskArguments: TaskArguments, hre) { | ||
| console.log("Deploying ProofcastAdapter...") | ||
| const signers: SignerWithAddress[] = await hre.ethers.getSigners() | ||
| const ProofcastAdapterFactory: ProofcastAdapter__factory = <ProofcastAdapter__factory>( | ||
| await hre.ethers.getContractFactory("ProofcastAdapter") | ||
| ) | ||
| const constructorArguments = [ | ||
| taskArguments.yaho, | ||
| taskArguments.chainId, | ||
| taskArguments.publicKey, | ||
| taskArguments.attestation, | ||
| ] as const | ||
| const ProofcastAdapter: ProofcastAdapter = <ProofcastAdapter>( | ||
| await ProofcastAdapterFactory.connect(signers[0]).deploy() | ||
| ) | ||
| await ProofcastAdapter.deployed() | ||
| console.log("ProofcastAdapter deployed to:", ProofcastAdapter.address) | ||
|
|
||
| if (taskArguments.verify) await verify(hre, ProofcastAdapter, constructorArguments) | ||
| }) | ||
|
|
||
| task("deploy:adapter:ProofcastAdapter:initYaho") | ||
| .addParam("adapter", "Proofcast adapter address") | ||
| .addParam("yaho", "address of the Yaho contract") | ||
| .addParam("chainId", "chain id of the Yaho contract") | ||
| .setAction(async function (taskArguments: TaskArguments, hre) { | ||
| console.log("Initializing Yaho...") | ||
| const signers: SignerWithAddress[] = await hre.ethers.getSigners() | ||
| const ProofcastAdapterFactory: ProofcastAdapter__factory = <ProofcastAdapter__factory>( | ||
| await hre.ethers.getContractFactory("ProofcastAdapter") | ||
| ) | ||
| const proofcastAdapter = ProofcastAdapterFactory.attach(taskArguments.adapter) | ||
|
|
||
| const tx = await proofcastAdapter.connect(signers[0]).initYaho(taskArguments.chainId, taskArguments.yaho) | ||
| const receipt = await tx.wait(1) | ||
|
|
||
| console.log("Yaho initialized @", receipt.transactionHash) | ||
| }) | ||
|
|
||
| task("deploy:adapter:ProofcastAdapter:setTeeSigner") | ||
| .addParam("adapter", "Proofcast adapter address") | ||
| .addParam("publicKey", "event attestator public key") | ||
| .addParam("attestation", "event attestator attestation payload") | ||
| .setAction(async function (taskArguments: TaskArguments, hre) { | ||
| console.log("Setting tee signer...") | ||
| const signers: SignerWithAddress[] = await hre.ethers.getSigners() | ||
| const ProofcastAdapterFactory: ProofcastAdapter__factory = <ProofcastAdapter__factory>( | ||
| await hre.ethers.getContractFactory("ProofcastAdapter") | ||
| ) | ||
| const proofcastAdapter = ProofcastAdapterFactory.attach(taskArguments.adapter) | ||
|
|
||
| const tx = await proofcastAdapter | ||
| .connect(signers[0]) | ||
| .setTeeSigner(taskArguments.publicKey, taskArguments.attestation) | ||
| const receipt = await tx.wait(1) | ||
|
|
||
| console.log("Tee signer set @", receipt.transactionHash) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused, can be remove.