Skip to content

Commit

Permalink
Merge pull request #85 from gnosis/feat/sp1-helios
Browse files Browse the repository at this point in the history
feat: SP1 Helios Adapter
  • Loading branch information
allemanfredi authored Jan 9, 2025
2 parents 32606b3 + 3aeec78 commit c337496
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 0 deletions.
96 changes: 96 additions & 0 deletions packages/evm/contracts/adapters/SP1Helios/SP1HeliosAdapter.sol
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;
}
}
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 packages/evm/contracts/adapters/SP1Helios/mock/MockSP1Helios.sol
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);
}
}
1 change: 1 addition & 0 deletions packages/evm/tasks/deploy/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ import "./telepathy"
import "./vea"
import "./wormhole"
import "./zetachain"
import "./sp1helios"
47 changes: 47 additions & 0 deletions packages/evm/tasks/deploy/adapters/sp1helios.ts
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)
})
Loading

0 comments on commit c337496

Please sign in to comment.