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: HashiProverUpgradeable #82

Merged
merged 6 commits into from
Nov 25, 2024
Merged
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
44 changes: 0 additions & 44 deletions packages/evm/contracts/interfaces/IHashiProver.sol

This file was deleted.

139 changes: 6 additions & 133 deletions packages/evm/contracts/prover/HashiProver.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.0;

import { SecureMerkleTrie } from "@eth-optimism/contracts-bedrock/src/libraries/trie/SecureMerkleTrie.sol";
import { MerkleTrie } from "@eth-optimism/contracts-bedrock/src/libraries/trie/MerkleTrie.sol";
import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol";
import { IHashiProver } from "../interfaces/IHashiProver.sol";
import { IShoyuBashi } from "../interfaces/IShoyuBashi.sol";

contract HashiProver is IHashiProver {
using RLPReader for RLPReader.RLPItem;
using RLPReader for bytes;
import { HashiProverLib } from "./HashiProverLib.sol";
import { AccountAndStorageProof, ReceiptProof } from "./HashiProverStructs.sol";

contract HashiProver {
/// @notice Stores the address of the ShoyuBashi contract.
address public immutable SHOYU_BASHI;

constructor(address shoyuBashi) {
Expand All @@ -33,23 +28,7 @@ contract HashiProver is IHashiProver {
* @return bytes The RLP-encoded event corresponding to the specified `logIndex`.
*/
function verifyForeignEvent(ReceiptProof calldata proof) internal view returns (bytes memory) {
bytes memory blockHeader = _checkBlockHeaderAgainstHashi(
proof.chainId,
proof.blockNumber,
proof.blockHeader,
proof.ancestralBlockNumber,
proof.ancestralBlockHeaders
);
RLPReader.RLPItem[] memory blockHeaderFields = blockHeader.toRlpItem().toList();
bytes32 receiptsRoot = bytes32(blockHeaderFields[5].toUint());

bytes memory value = MerkleTrie.get(proof.transactionIndex, proof.receiptProof, receiptsRoot);
RLPReader.RLPItem[] memory receiptFields = _extractReceiptFields(value);
if (receiptFields.length != 4) revert InvalidReceipt();

RLPReader.RLPItem[] memory logs = receiptFields[3].toList();
if (proof.logIndex >= logs.length) revert InvalidLogIndex();
return logs[proof.logIndex].toRlpBytes();
return HashiProverLib.verifyForeignEvent(proof, SHOYU_BASHI);
}

/**
Expand All @@ -63,118 +42,12 @@ contract HashiProver is IHashiProver {
* - ancestralBlockHeaders: Array of block headers proving ancestry of the specified block.
* - account: The account address whose storage is being verified.
* - accountProof: Proof data for locating the account in the state trie.
* - storageHash: Expected hash of the storage root for the account.
* - storageKeys: Array of storage keys for which data is being verified.
* - storageProof: Proof data for locating the storage values in the storage trie.
*
* @return bytes[] An array of storage values corresponding to the specified `storageKeys`.
*/
function verifyForeignStorage(AccountAndStorageProof calldata proof) internal view returns (bytes[] memory) {
bytes memory blockHeader = _checkBlockHeaderAgainstHashi(
proof.chainId,
proof.blockNumber,
proof.blockHeader,
proof.ancestralBlockNumber,
proof.ancestralBlockHeaders
);
RLPReader.RLPItem[] memory blockHeaderFields = blockHeader.toRlpItem().toList();
bytes32 stateRoot = bytes32(blockHeaderFields[3].toUint());
(, , bytes32 expectedStorageHash, ) = _verifyAccountProof(proof.account, stateRoot, proof.accountProof);
if (proof.storageHash != expectedStorageHash) revert InvalidStorageHash();
return _verifyStorageProof(proof.storageHash, proof.storageKeys, proof.storageProof);
}

function _checkBlockHeaderAgainstHashi(
uint256 chainId,
uint256 blockNumber,
bytes memory blockHeader,
uint256 ancestralBlockNumber,
bytes[] memory ancestralBlockHeaders
) private view returns (bytes memory) {
bytes32 blockHeaderHash = keccak256(blockHeader);
bytes32 currentBlockHeaderHash = IShoyuBashi(SHOYU_BASHI).getThresholdHash(chainId, blockNumber);
if (currentBlockHeaderHash == blockHeaderHash && ancestralBlockHeaders.length == 0) return blockHeader;

for (uint256 i = 0; i < ancestralBlockHeaders.length; i++) {
RLPReader.RLPItem memory ancestralBlockHeaderRLP = RLPReader.toRlpItem(ancestralBlockHeaders[i]);
RLPReader.RLPItem[] memory ancestralBlockHeaderContent = ancestralBlockHeaderRLP.toList();

bytes32 blockParentHash = bytes32(ancestralBlockHeaderContent[0].toUint());
uint256 currentAncestralBlockNumber = uint256(ancestralBlockHeaderContent[8].toUint());

bytes32 ancestralBlockHeaderHash = keccak256(ancestralBlockHeaders[i]);
if (ancestralBlockHeaderHash != currentBlockHeaderHash)
revert ConflictingBlockHeader(
currentAncestralBlockNumber,
ancestralBlockHeaderHash,
currentBlockHeaderHash
);

if (ancestralBlockNumber == currentAncestralBlockNumber) {
return ancestralBlockHeaders[i];
} else {
currentBlockHeaderHash = blockParentHash;
}
}

revert BlockHeaderNotFound();
}

function _extractReceiptFields(bytes memory value) private pure returns (RLPReader.RLPItem[] memory) {
uint256 offset;
if (value[0] == 0x01 || value[0] == 0x02 || value[0] == 0x03 || value[0] == 0x7e) {
offset = 1;
} else if (value[0] >= 0xc0) {
offset = 0;
} else {
revert UnsupportedTxType();
}

uint256 memPtr;
assembly {
memPtr := add(value, add(0x20, mul(0x01, offset)))
}

return RLPReader.RLPItem(value.length - offset, memPtr).toList();
}

function _verifyAccountProof(
address account,
bytes32 stateRoot,
bytes[] memory proof
) private pure returns (uint256, uint256, bytes32, bytes32) {
bytes memory accountRlp = SecureMerkleTrie.get(abi.encodePacked(account), proof, stateRoot);

bytes32 accountStorageRoot = bytes32(accountRlp.toRlpItem().toList()[2].toUint());
if (accountStorageRoot.length == 0) revert InvalidStorageHash();
RLPReader.RLPItem[] memory accountFields = accountRlp.toRlpItem().toList();
if (accountFields.length != 4) revert InvalidAccount();
// [nonce, balance, storageHash, codeHash]
return (
accountFields[0].toUint(),
accountFields[1].toUint(),
bytes32(accountFields[2].toUint()),
bytes32(accountFields[3].toUint())
);
}

function _verifyStorageProof(
bytes32 storageHash,
bytes32[] memory storageKeys,
bytes[][] memory proof
) private pure returns (bytes[] memory) {
bytes[] memory results = new bytes[](proof.length);
if (storageKeys.length == 0 || proof.length == 0 || storageKeys.length != proof.length)
revert InvalidStorageProofParams();
for (uint256 i = 0; i < proof.length; ) {
RLPReader.RLPItem memory item = RLPReader.toRlpItem(
SecureMerkleTrie.get(abi.encode(storageKeys[i]), proof[i], storageHash)
);
results[i] = item.toBytes();
unchecked {
++i;
}
}
return results;
return HashiProverLib.verifyForeignStorage(proof, SHOYU_BASHI);
}
}
Loading
Loading