generated from PaulRBerg/hardhat-template
-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #85 from gnosis/feat/sp1-helios
feat: SP1 Helios Adapter
- Loading branch information
Showing
6 changed files
with
415 additions
and
0 deletions.
There are no files selected for viewing
96 changes: 96 additions & 0 deletions
96
packages/evm/contracts/adapters/SP1Helios/SP1HeliosAdapter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity ^0.8.20; | ||
|
||
import { ISP1LightClient } from "./interfaces/ISP1LightClient.sol"; | ||
import { SSZ } from "../Telepathy/libraries/SimpleSerialize.sol"; | ||
import { Merkle } from "../Spectre/lib/Merkle.sol"; | ||
import { Receipt } from "../Electron/lib/Receipt.sol"; | ||
import { BlockHashAdapter } from "../BlockHashAdapter.sol"; | ||
|
||
contract SP1HeliosAdapter is BlockHashAdapter { | ||
string public constant PROVIDER = "sp1-helios"; | ||
bytes32 internal constant MESSAGE_DISPATCHED_EVENT_SIG = | ||
0x218247aabc759e65b5bb92ccc074f9d62cd187259f2a0984c3c9cf91f67ff7cf; // keccak256("MessageDispatched(uint256,(uint256,uint256,uint256,address,address,bytes,address[],address[]))"); | ||
|
||
address public immutable SP1_HELIOS_ADDRESS; | ||
address public immutable SOURCE_YAHO; | ||
uint256 public immutable SOURCE_CHAIN_ID; | ||
|
||
error HeaderNotAvailable(); | ||
error InvalidBlockNumberProof(); | ||
error InvalidBlockHashProof(); | ||
error InvalidReceiptsRoot(); | ||
error ErrorParseReceipt(); | ||
error InvalidEventSignature(); | ||
error InvalidEventSource(); | ||
|
||
constructor(address sp1HeliosAddress, uint256 sourceChainId, address sourceYaho) { | ||
SP1_HELIOS_ADDRESS = sp1HeliosAddress; | ||
SOURCE_CHAIN_ID = sourceChainId; | ||
SOURCE_YAHO = sourceYaho; | ||
} | ||
|
||
function storeBlockHeader( | ||
uint256 slot, | ||
uint256 blockNumber, | ||
bytes32[] calldata blockNumberProof, | ||
bytes32 blockHash, | ||
bytes32[] calldata blockHashProof | ||
) external { | ||
bytes32 header = _getHeader(slot); | ||
|
||
if (!SSZ.verifyBlockNumber(blockNumber, blockNumberProof, header)) { | ||
revert InvalidBlockNumberProof(); | ||
} | ||
|
||
if (!SSZ.verifyBlockHash(blockHash, blockHashProof, header)) { | ||
revert InvalidBlockHashProof(); | ||
} | ||
|
||
_storeHash(SOURCE_CHAIN_ID, blockNumber, blockHash); | ||
} | ||
|
||
function verifyAndStoreDispatchedMessage( | ||
uint64 headerSlot, | ||
uint64 txSlot, | ||
bytes32[] memory receiptsRootProof, | ||
bytes32 receiptsRoot, | ||
bytes[] memory receiptProof, | ||
bytes memory txIndexRLPEncoded, | ||
uint256 logIndex | ||
) external { | ||
bytes32 header = _getHeader(headerSlot); | ||
|
||
bool isValidReceiptsRoot = Merkle.verifyReceiptsRoot( | ||
receiptsRootProof, | ||
receiptsRoot, | ||
headerSlot, | ||
txSlot, | ||
header | ||
); | ||
if (!isValidReceiptsRoot) revert InvalidReceiptsRoot(); | ||
|
||
Receipt.ParsedReceipt memory parsedReceipt = Receipt.parseReceipt( | ||
receiptsRoot, | ||
receiptProof, | ||
txIndexRLPEncoded, | ||
logIndex | ||
); | ||
if (!parsedReceipt.isValid) revert ErrorParseReceipt(); | ||
if (bytes32(parsedReceipt.topics[0]) != MESSAGE_DISPATCHED_EVENT_SIG) revert InvalidEventSignature(); | ||
if (parsedReceipt.eventSource != SOURCE_YAHO) revert InvalidEventSource(); | ||
|
||
uint256 messageId = uint256(parsedReceipt.topics[1]); | ||
bytes32 messageHash = keccak256(parsedReceipt.data); | ||
|
||
_storeHash(SOURCE_CHAIN_ID, messageId, messageHash); | ||
} | ||
|
||
function _getHeader(uint256 slot) internal view returns (bytes32) { | ||
bytes32 header = ISP1LightClient(SP1_HELIOS_ADDRESS).headers(slot); | ||
if (header == bytes32(0)) { | ||
revert HeaderNotAvailable(); | ||
} | ||
return header; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
packages/evm/contracts/adapters/SP1Helios/interfaces/ISP1LightClient.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.20; | ||
|
||
interface ISP1LightClient { | ||
function head() external view returns (uint256); | ||
function headers(uint256) external view returns (bytes32); | ||
} |
21 changes: 21 additions & 0 deletions
21
packages/evm/contracts/adapters/SP1Helios/mock/MockSP1Helios.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.20; | ||
|
||
/// @title MockSP1Helios | ||
/// @notice A mock Ethereum beacon chain light client, built with SP1 and Helios. | ||
contract MockSP1Helios { | ||
/// @notice The latest slot the light client has a finalized header for. | ||
uint256 public head = 0; | ||
|
||
/// @notice Maps from a slot to a beacon block header root. | ||
mapping(uint256 => bytes32) public headers; | ||
|
||
event HeadUpdate(uint256 indexed slot, bytes32 indexed root); | ||
|
||
function setHeader(uint256 slot, bytes32 header) external { | ||
head = slot; | ||
headers[slot] = header; | ||
|
||
emit HeadUpdate(slot, header); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,3 +18,4 @@ import "./telepathy" | |
import "./vea" | ||
import "./wormhole" | ||
import "./zetachain" | ||
import "./sp1helios" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" | ||
import { task } from "hardhat/config" | ||
import type { TaskArguments } from "hardhat/types" | ||
|
||
import type { SP1HeliosAdapter } from "../../../types/contracts/adapters/SP1Helios/SP1HeliosAdapter" | ||
import type { SP1HeliosAdapter__factory } from "../../../types/factories/contracts/adapters/SP1Helios/SP1HeliosAdapter__factory" | ||
import { verify } from "../index" | ||
|
||
const MerklePatriciaAddresses = { | ||
10200: "0x777662E6A65411e0A425E59C496A7D1C0635A935", | ||
100: "0xff07C59F7D882D1799e1CABd1D17faaDE7694fe0", | ||
11155111: "0x1b19Dfd5e1986A0d524644F081AcB14d51159818", | ||
4201: "0xC82e50cc90C84DC492B4Beb6792DEeB496d52424", | ||
42: "0x10Da7e0e9eBc8BFE0021698F557F418889b9b4D2", | ||
} | ||
|
||
task("deploy:SP1HeliosAdapter") | ||
.addParam("sp1helios") | ||
.addParam("sourceChainId") | ||
.addParam("sourceYaho") | ||
.addFlag("verify", "whether to verify the contract on Etherscan") | ||
.setAction(async function (taskArguments: TaskArguments, hre) { | ||
console.log("Deploying SP1HeliosAdapter...") | ||
const merklePatriciaAddress = MerklePatriciaAddresses[hre.network.config.chainId] | ||
if (!merklePatriciaAddress) { | ||
throw new Error("MerklePatricia Not Found") | ||
} | ||
const signers: SignerWithAddress[] = await hre.ethers.getSigners() | ||
const SP1HeliosAdapterFactory: SP1HeliosAdapter__factory = <SP1HeliosAdapter__factory>( | ||
await hre.ethers.getContractFactory("SP1HeliosAdapter", { | ||
libraries: { | ||
MerklePatricia: merklePatriciaAddress, | ||
}, | ||
}) | ||
) | ||
const constructorArguments = [ | ||
taskArguments.sp1helios, | ||
taskArguments.sourceChainId, | ||
taskArguments.sourceYaho, | ||
] as const | ||
const SP1HeliosAdapter: SP1HeliosAdapter = <SP1HeliosAdapter>( | ||
await SP1HeliosAdapterFactory.connect(signers[0]).deploy(...constructorArguments) | ||
) | ||
await SP1HeliosAdapter.deployed() | ||
console.log("SP1HeliosAdapter deployed to:", SP1HeliosAdapter.address) | ||
if (taskArguments.verify) await verify(hre, SP1HeliosAdapter, constructorArguments) | ||
}) |
Oops, something went wrong.