Skip to content

Commit

Permalink
Merge pull request #82 from gnosis/feat/upgradeable-hashi-prover
Browse files Browse the repository at this point in the history
feat: HashiProverUpgradeable
  • Loading branch information
allemanfredi authored Nov 25, 2024
2 parents eca1dae + f3ceea3 commit 76ae42a
Show file tree
Hide file tree
Showing 14 changed files with 821 additions and 334 deletions.
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

0 comments on commit 76ae42a

Please sign in to comment.