Skip to content

Commit

Permalink
Make scroll verifier working
Browse files Browse the repository at this point in the history
  • Loading branch information
makoto committed May 6, 2024
1 parent fa73e0e commit 372ec98
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 212 deletions.
2 changes: 1 addition & 1 deletion arb-gateway/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async function fetch(request: CFWRequest, env: Env) {
await tracker.trackEvent(request, 'request', { props }, true);
return app
.handle(request)
.then(tracker.logResult.bind(null, propsDecoder, request));
.then(tracker.logResult.bind(tracker, propsDecoder, request));
}

export default {
Expand Down
11 changes: 3 additions & 8 deletions arb-verifier/contracts/ArbVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {StateProof, EVMProofHelper} from '@ensdomains/evm-verifier/contracts/EVM
import {IEVMVerifier} from '@ensdomains/evm-verifier/contracts/IEVMVerifier.sol';
import {Node, IRollupCore} from '@arbitrum/nitro-contracts/src/rollup/IRollupCore.sol';
import {RLPReader} from '@eth-optimism/contracts-bedrock/src/libraries/rlp/RLPReader.sol';
import {SecureMerkleTrie} from '@ensdomains/evm-verifier/contracts/SecureMerkleTrie.sol';

struct ArbWitnessData {
bytes32 version;
Expand Down Expand Up @@ -63,14 +64,8 @@ contract ArbVerifier is IEVMVerifier {

//Now that we know that the block is valid, we can get the state root from the block.
bytes32 stateRoot = getStateRootFromBlock(arbData.rlpEncodedBlock);

values = EVMProofHelper.getStorageValues(
target,
commands,
constants,
stateRoot,
stateProof
);
bytes32 storageRoot = SecureMerkleTrie.getStorageRoot(stateRoot, target, stateProof.stateTrieWitness);
return EVMProofHelper.getStorageValues(target, SecureMerkleTrie.getTrieProof, commands, constants, storageRoot, stateProof.storageProofs);
}

/*
Expand Down
72 changes: 33 additions & 39 deletions evm-verifier/contracts/EVMProofHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,48 +21,35 @@ library EVMProofHelper {
error UnknownOpcode(uint8);
error InvalidSlotSize(uint256 size);

/**
* @notice Get the storage root for the provided merkle proof
* @param stateRoot The state root the witness was generated against
* @param target The address we are fetching a storage root for
* @param witness A witness proving the value of the storage root for `target`.
* @return The storage root retrieved from the provided state root
*/
function getStorageRoot(bytes32 stateRoot, address target, bytes[] memory witness) private pure returns (bytes32) {
(bool exists, bytes memory encodedResolverAccount) = SecureMerkleTrie.get(
abi.encodePacked(target),
witness,
stateRoot
);
if(!exists) {
revert AccountNotFound(target);
}
RLPReader.RLPItem[] memory accountState = RLPReader.readList(encodedResolverAccount);
return bytes32(RLPReader.readBytes(accountState[2]));
}

/**
* @notice Prove whether the provided storage slot is part of the storageRoot
* @param getter function to verify the storage proof
* @param storageRoot the storage root for the account that contains the storage slot
* @param slot The storage key we are fetching the value of
* @param witness the StorageProof struct containing the necessary proof data
* @return The retrieved storage proof value or 0x if the storage slot is empty
*/
function getSingleStorageProof(bytes32 storageRoot, uint256 slot, bytes[] memory witness) private pure returns (bytes memory) {
(bool exists, bytes memory retrievedValue) = SecureMerkleTrie.get(
abi.encodePacked(slot),
function getSingleStorageProof(
address target,
function(address,uint256,bytes[] memory, bytes32) internal view returns(bytes memory) getter,
bytes32 storageRoot,
uint256 slot,
bytes[] memory witness
) private view returns (bytes memory) {
return getter(
target,
slot,
witness,
storageRoot
);
if(!exists) {
// Nonexistent values are treated as zero.
return "";
}
return RLPReader.readBytes(retrievedValue);
}

function getFixedValue(bytes32 storageRoot, uint256 slot, bytes[] memory witness) private pure returns(bytes32) {
bytes memory value = getSingleStorageProof(storageRoot, slot, witness);
function getFixedValue(
address target,
function(address,uint256,bytes[] memory, bytes32) internal view returns(bytes memory) getter,
bytes32 storageRoot, uint256 slot, bytes[] memory witness
) private view returns(bytes32) {
bytes memory value = getSingleStorageProof(target, getter, storageRoot, slot, witness);
// RLP encoded storage slots are stored without leading 0 bytes.
// Casting to bytes32 appends trailing 0 bytes, so we have to bit shift to get the
// original fixed-length representation back.
Expand All @@ -82,7 +69,7 @@ library EVMProofHelper {
}
}

function computeFirstSlot(bytes32 command, bytes[] memory constants, bytes[] memory values) private pure returns(bool isDynamic, uint256 slot) {
function computeFirstSlot(bytes32 command, bytes[] memory constants, bytes[] memory values) internal pure returns(bool isDynamic, uint256 slot) {
uint8 flags = uint8(command[0]);
isDynamic = (flags & FLAG_DYNAMIC) != 0;

Expand All @@ -96,8 +83,12 @@ library EVMProofHelper {
}
}

function getDynamicValue(bytes32 storageRoot, uint256 slot, StateProof memory proof, uint256 proofIdx) private pure returns(bytes memory value, uint256 newProofIdx) {
uint256 firstValue = uint256(getFixedValue(storageRoot, slot, proof.storageProofs[proofIdx++]));
function getDynamicValue(
address target,
function(address,uint256,bytes[] memory, bytes32) internal view returns(bytes memory) getter,
bytes32 storageRoot, uint256 slot, bytes[][] memory proof, uint256 proofIdx) private view returns(bytes memory value, uint256 newProofIdx
) {
uint256 firstValue = uint256(getFixedValue(target, getter,storageRoot, slot, proof[proofIdx++]));
if(firstValue & 0x01 == 0x01) {
// Long value: first slot is `length * 2 + 1`, following slots are data.
uint256 length = (firstValue - 1) / 2;
Expand All @@ -107,10 +98,10 @@ library EVMProofHelper {
// all at once, but we're trying to avoid writing new library code.
while(length > 0) {
if(length < 32) {
value = bytes.concat(value, getSingleStorageProof(storageRoot, slot++, proof.storageProofs[proofIdx++]).slice(0, length));
value = bytes.concat(value, getSingleStorageProof(target, getter, storageRoot, slot++, proof[proofIdx++]).slice(0, length));
length = 0;
} else {
value = bytes.concat(value, getSingleStorageProof(storageRoot, slot++, proof.storageProofs[proofIdx++]));
value = bytes.concat(value, getSingleStorageProof(target, getter, storageRoot, slot++, proof[proofIdx++]));
length -= 32;
}
}
Expand All @@ -122,20 +113,23 @@ library EVMProofHelper {
}
}

function getStorageValues(address target, bytes32[] memory commands, bytes[] memory constants, bytes32 stateRoot, StateProof memory proof) internal pure returns(bytes[] memory values) {
bytes32 storageRoot = getStorageRoot(stateRoot, target, proof.stateTrieWitness);
function getStorageValues(
address target,
function(address,uint256,bytes[] memory, bytes32) internal view returns(bytes memory) getter,
bytes32[] memory commands, bytes[] memory constants, bytes32 storageRoot, bytes[][] memory proof) internal view returns(bytes[] memory values
) {
uint256 proofIdx = 0;
values = new bytes[](commands.length);
for(uint256 i = 0; i < commands.length; i++) {
bytes32 command = commands[i];
(bool isDynamic, uint256 slot) = computeFirstSlot(command, constants, values);
if(!isDynamic) {
values[i] = abi.encode(getFixedValue(storageRoot, slot, proof.storageProofs[proofIdx++]));
values[i] = abi.encode(getFixedValue(target, getter, storageRoot, slot, proof[proofIdx++]));
if(values[i].length > 32) {
revert InvalidSlotSize(values[i].length);
}
} else {
(values[i], proofIdx) = getDynamicValue(storageRoot, slot, proof, proofIdx);
(values[i], proofIdx) = getDynamicValue(target, getter, storageRoot, slot, proof, proofIdx);
}
}
}
Expand Down
44 changes: 44 additions & 0 deletions evm-verifier/contracts/SecureMerkleTrie.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,57 @@ pragma solidity ^0.8.0;

/* Library Imports */
import { MerkleTrie } from "./MerkleTrie.sol";
import {RLPReader} from '@eth-optimism/contracts-bedrock/src/libraries/rlp/RLPReader.sol';

/**
* @title SecureMerkleTrie
* @notice SecureMerkleTrie is a thin wrapper around the MerkleTrie library that hashes the input
* keys. Ethereum's state trie hashes input keys before storing them.
*/
library SecureMerkleTrie {
error AccountNotFound(address);

/*
* @notice Get the storage value for the provided merkle proof
* @param target The address we are fetching a storage root for
* @param witness A witness proving the value of the storage root for `target`.
* @param root The state root the witness was generated against
* @return The storage value
*/

function getTrieProof(address, uint256 slot, bytes[] memory witness, bytes32 root) internal pure returns(bytes memory){
(bool exists, bytes memory retrievedValue) = get(
abi.encodePacked(slot),
witness,
root
);
if(!exists) {
// Nonexistent values are treated as zero.
return "";
}
return RLPReader.readBytes(retrievedValue);
}

/**
* @notice Get the storage root for the provided merkle proof
* @param stateRoot The state root the witness was generated against
* @param target The address we are fetching a storage root for
* @param witness A witness proving the value of the storage root for `target`.
* @return The storage root retrieved from the provided state root
*/
function getStorageRoot(bytes32 stateRoot, address target, bytes[] memory witness) internal view returns (bytes32) {
(bool exists, bytes memory encodedResolverAccount) = get(
abi.encodePacked(target),
witness,
stateRoot
);
if(!exists) {
revert AccountNotFound(target);
}
RLPReader.RLPItem[] memory accountState = RLPReader.readList(encodedResolverAccount);
return bytes32(RLPReader.readBytes(accountState[2]));
}

/**
* @notice Verifies a proof that a given key/value pair is present in the Merkle trie.
*
Expand Down
2 changes: 1 addition & 1 deletion op-gateway/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ async function fetch(request: CFWRequest, env: Env) {
await tracker.trackEvent(request, 'request', { props }, true);
return app
.handle(request)
.then(tracker.logResult.bind(null, propsDecoder, request));
.then(tracker.logResult.bind(tracker, propsDecoder, request));
}

export default {
Expand Down
4 changes: 3 additions & 1 deletion op-verifier/contracts/OPVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { RLPReader } from "@eth-optimism/contracts-bedrock/src/libraries/rlp/RLP
import { StateProof, EVMProofHelper } from "@ensdomains/evm-verifier/contracts/EVMProofHelper.sol";
import { Types } from "@eth-optimism/contracts-bedrock/src/libraries/Types.sol";
import { Hashing } from "@eth-optimism/contracts-bedrock/src/libraries/Hashing.sol";
import {SecureMerkleTrie} from '@ensdomains/evm-verifier/contracts/SecureMerkleTrie.sol';

struct OPWitnessData {
uint256 l2OutputIndex;
Expand Down Expand Up @@ -38,6 +39,7 @@ contract OPVerifier is IEVMVerifier {
if(l2out.outputRoot != expectedRoot) {
revert OutputRootMismatch(opData.l2OutputIndex, expectedRoot, l2out.outputRoot);
}
return EVMProofHelper.getStorageValues(target, commands, constants, opData.outputRootProof.stateRoot, stateProof);
bytes32 storageRoot = SecureMerkleTrie.getStorageRoot(opData.outputRootProof.stateRoot, target, stateProof.stateTrieWitness);
return EVMProofHelper.getStorageValues(target, SecureMerkleTrie.getTrieProof, commands, constants, storageRoot, stateProof.storageProofs);
}
}
143 changes: 0 additions & 143 deletions scroll-verifier/contracts/EVMProofHelper2.sol

This file was deleted.

Loading

0 comments on commit 372ec98

Please sign in to comment.