Skip to content
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

feat(evm): Make dendreth adapter work with finalized header #23

Open
wants to merge 4 commits into
base: feat/v0.2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions packages/evm/contracts/adapters/DendrETH/DendrETHAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@ contract DendrETHAdapter is BlockHashAdapter {
address public immutable SOURCE_YAHO;
uint256 public immutable SOURCE_CHAIN_ID;

error InvalidUpdate();
error BlockHeaderNotAvailable(uint256 slot);
error FinalizedBlockHeaderNotAvailable(bytes32 finalizedHeader);
error InvalidSlot();
error InvalidBlockNumberProof();
error InvalidBlockHashProof();
error InvalidReceiptsRoot();
error ErrorParseReceipt();
error InvalidEventSignature();
error InvalidEventSource();

modifier onlyFinalizedHeaderIsInLightClient(bytes32 finalizedBlockHeader) {
_checkFinalizedHeaderIsInLightClient(finalizedBlockHeader);
_;
}

constructor(address dendrETHAddress, uint256 sourceChainId, address sourceYaho) {
DENDRETH_ADDRESS = dendrETHAddress;
SOURCE_CHAIN_ID = sourceChainId;
Expand All @@ -33,18 +38,17 @@ contract DendrETHAdapter is BlockHashAdapter {
/// @notice Stores the block header for a given block only if it exists
// in the DendrETH Light Client for the SOURCE_CHAIN_ID.
function storeBlockHeader(
uint64 slot,
bytes32 finalizedBlockHeader,
uint256 blockNumber,
bytes32[] calldata blockNumberProof,
bytes32 blockHash,
bytes32[] calldata blockHashProof
) external {
ILightClient lightClient = ILightClient(DENDRETH_ADDRESS);
bytes32 blockHeaderRoot = lightClient.optimisticHeaders(_getIndex(slot, lightClient));
if (!SSZ.verifyBlockNumber(blockNumber, blockNumberProof, blockHeaderRoot)) {
) external onlyFinalizedHeaderIsInLightClient(finalizedBlockHeader) {
if (!SSZ.verifyBlockNumber(blockNumber, blockNumberProof, finalizedBlockHeader)) {
revert InvalidBlockNumberProof();
}
if (!SSZ.verifyBlockHash(blockHash, blockHashProof, blockHeaderRoot)) {

if (!SSZ.verifyBlockHash(blockHash, blockHashProof, finalizedBlockHeader)) {
revert InvalidBlockHashProof();
}

Expand All @@ -54,49 +58,52 @@ contract DendrETHAdapter is BlockHashAdapter {
/// @notice Updates DendrETH Light client and stores the given block
// for the update
function storeBlockHeader(
uint64 slot,
uint256 blockNumber,
bytes32[] calldata blockNumberProof,
bytes32 blockHash,
bytes32[] calldata blockHashProof,
LightClientUpdate calldata update
) external {
ILightClient lightClient = ILightClient(DENDRETH_ADDRESS);
lightClient.light_client_update(update);
if (lightClient.optimisticHeaderSlot() != slot) {
revert InvalidUpdate();
}

bytes32 blockHeaderRoot = lightClient.optimisticHeaderRoot();
if (!SSZ.verifyBlockNumber(blockNumber, blockNumberProof, blockHeaderRoot)) {
lightClient.lightClientUpdate(update);

bytes32 finalizedHeaderRoot = lightClient.finalizedHeaderRoot();

if (!SSZ.verifyBlockNumber(blockNumber, blockNumberProof, finalizedHeaderRoot)) {
revert InvalidBlockNumberProof();
}
if (!SSZ.verifyBlockHash(blockHash, blockHashProof, blockHeaderRoot)) {

if (!SSZ.verifyBlockHash(blockHash, blockHashProof, finalizedHeaderRoot)) {
revert InvalidBlockHashProof();
}

_storeHash(SOURCE_CHAIN_ID, blockNumber, blockHash);
}

function verifyAndStoreDispatchedMessage(
bytes32 srcFinalizedHeader,
uint64 srcSlot,
bytes32[] calldata slotProof,
uint64 txSlot,
bytes32[] memory receiptsRootProof,
bytes32 receiptsRoot,
bytes[] memory receiptProof,
bytes memory txIndexRLPEncoded,
uint256 logIndex
) external {
ILightClient lightClient = ILightClient(DENDRETH_ADDRESS);
bytes32 blockHeaderRoot = lightClient.optimisticHeaders(_getIndex(srcSlot, lightClient));
) external onlyFinalizedHeaderIsInLightClient(srcFinalizedHeader) {
if (!SSZ.verifySlot(srcSlot, slotProof, srcFinalizedHeader)) {
revert InvalidSlot();
}

bool isValidReceiptsRoot = Merkle.verifyReceiptsRoot(
receiptsRootProof,
receiptsRoot,
srcSlot,
txSlot,
blockHeaderRoot
srcFinalizedHeader
);

if (!isValidReceiptsRoot) revert InvalidReceiptsRoot();

Receipt.ParsedReceipt memory parsedReceipt = Receipt.parseReceipt(
Expand All @@ -116,13 +123,15 @@ contract DendrETHAdapter is BlockHashAdapter {
_storeHash(SOURCE_CHAIN_ID, messageId, messageHash);
}

function _getIndex(uint64 slot, ILightClient lightClient) internal view returns (uint256) {
function _checkFinalizedHeaderIsInLightClient(bytes32 finalizedBlockHeader) internal view {
ILightClient lightClient = ILightClient(DENDRETH_ADDRESS);

uint256 currentIndex = lightClient.currentIndex();
uint256 i = currentIndex;
bool found = false;

do {
if (slot == lightClient.optimisticSlots(i)) {
if (finalizedBlockHeader == lightClient.finalizedHeaders(i)) {
found = true;
break;
}
Expand All @@ -133,8 +142,7 @@ contract DendrETHAdapter is BlockHashAdapter {
} while (i != currentIndex);

if (!found) {
revert BlockHeaderNotAvailable(slot);
revert FinalizedBlockHeaderNotAvailable(finalizedBlockHeader);
}
return i;
}
}
20 changes: 14 additions & 6 deletions packages/evm/contracts/adapters/DendrETH/interfaces/IDendrETH.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.20;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

struct LightClientUpdate {
bytes32 attestedHeaderRoot;
Expand All @@ -14,13 +14,21 @@ struct LightClientUpdate {
interface ILightClient {
function currentIndex() external view returns (uint256);

function optimisticHeaders(uint256 index) external view returns (bytes32);

function optimisticHeaderRoot() external view returns (bytes32);

function optimisticHeaderSlot() external view returns (uint256);

function finalizedHeaderRoot() external view returns (bytes32);

function executionStateRoot() external view returns (bytes32);

function optimisticHeaders(uint256 index) external view returns (bytes32);

function optimisticSlots(uint256 index) external view returns (uint256);

function optimisticHeaderSlot() external view returns (uint256);
function finalizedHeaders(uint256 index) external view returns (bytes32);

function executionStateRoots(uint256 index) external view returns (bytes32);

function light_client_update(LightClientUpdate calldata update) external;
function lightClientUpdate(LightClientUpdate calldata update) external payable;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ pragma solidity ^0.8.20;

library SSZ {
// G-indicies for the BeaconBlockHeader -> bodyRoot -> executionPayload -> {blockNumber, blockHash}
uint256 internal constant EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX = 3222;
uint256 internal constant EXECUTION_PAYLOAD_BLOCK_HASH_INDEX = 3228;
uint256 internal constant EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX = 6438;
uint256 internal constant EXECUTION_PAYLOAD_BLOCK_HASH_INDEX = 6444;

// G-index for the BeaconBlockHeader -> slot
uint256 internal constant SLOT_INDEX = 8;

function toLittleEndian(uint256 _v) internal pure returns (bytes32) {
_v =
Expand Down Expand Up @@ -74,4 +77,8 @@ library SSZ {
) internal pure returns (bool) {
return isValidMerkleBranch(_blockHash, EXECUTION_PAYLOAD_BLOCK_HASH_INDEX, _blockHashProof, _headerRoot);
}

function verifySlot(uint256 _slot, bytes32[] memory _slotProof, bytes32 _headerRoot) internal pure returns (bool) {
return isValidMerkleBranch(toLittleEndian(_slot), SLOT_INDEX, _slotProof, _headerRoot);
}
}
5 changes: 5 additions & 0 deletions packages/evm/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const chainIds = {
"polygon-mainnet": 137,
"polygon-mumbai": 80001,
sepolia: 11155111,
"lukso-testnet": 4201,
}

function getChainConfig(chain: keyof typeof chainIds): NetworkUserConfig {
Expand All @@ -57,6 +58,9 @@ function getChainConfig(chain: keyof typeof chainIds): NetworkUserConfig {
case "chiado":
jsonRpcUrl = "https://rpc.chiadochain.net/"
break
case "lukso-testnet":
jsonRpcUrl = "https://rpc.testnet.lukso.network"
break
default:
jsonRpcUrl = `https://${chain}.infura.io/v3/${infuraApiKey}`
}
Expand Down Expand Up @@ -109,6 +113,7 @@ const config: HardhatUserConfig = {
bsc: getChainConfig("bsc"),
gnosis: getChainConfig("gnosis"),
chiado: getChainConfig("chiado"),
"lukso-testnet": getChainConfig("lukso-testnet"),
mainnet: getChainConfig("mainnet"),
optimism: getChainConfig("optimism-mainnet"),
"polygon-mainnet": getChainConfig("polygon-mainnet"),
Expand Down
2 changes: 2 additions & 0 deletions packages/evm/tasks/deploy/adapters/dendreth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { verify } from "../index"

const MerklePatriciaAddresses = {
10200: "0x777662E6A65411e0A425E59C496A7D1C0635A935",
11155111: "0x1b19Dfd5e1986A0d524644F081AcB14d51159818",
4201: "0xC82e50cc90C84DC492B4Beb6792DEeB496d52424",
}

task("deploy:adapter:DendrETHAdapter")
Expand Down