From db1a6af9b7cb4f0d5142f402a79f8b418ee1dbba Mon Sep 17 00:00:00 2001 From: pcw109550 Date: Wed, 14 Feb 2024 15:12:34 +0900 Subject: [PATCH] Fetch PreimageOracle from upstream --- .gitmodules | 3 + rvsol/foundry.toml | 14 ++ rvsol/lib/optimism | 1 + rvsol/src/PreimageKeyLib.sol | 59 --------- rvsol/src/PreimageOracle.sol | 107 --------------- rvsol/src/Step.sol | 5 +- rvsol/src/interfaces/IPreimageOracle.sol | 50 ------- rvsol/test/PreimageOracle.t.sol | 161 ----------------------- 8 files changed, 21 insertions(+), 379 deletions(-) create mode 160000 rvsol/lib/optimism delete mode 100644 rvsol/src/PreimageKeyLib.sol delete mode 100644 rvsol/src/PreimageOracle.sol delete mode 100644 rvsol/src/interfaces/IPreimageOracle.sol delete mode 100644 rvsol/test/PreimageOracle.t.sol diff --git a/.gitmodules b/.gitmodules index dc7f6495..4f11e89f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "rvsol/lib/forge-std"] path = rvsol/lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "rvsol/lib/optimism"] + path = rvsol/lib/optimism + url = https://github.com/ethereum-optimism/optimism diff --git a/rvsol/foundry.toml b/rvsol/foundry.toml index f49d8c9e..739a754b 100644 --- a/rvsol/foundry.toml +++ b/rvsol/foundry.toml @@ -4,5 +4,19 @@ out = 'out' libs = ['lib'] optimizer = true optimizer_runs = 999999 +remappings = [ + '@optimism/packages/contracts-bedrock=lib/optimism/packages/contracts-bedrock', + '@openzeppelin/contracts-upgradeable/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts-upgradeable/contracts', + '@openzeppelin/contracts/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts/contracts', + '@rari-capital/solmate/=lib/optimism/packages/contracts-bedrock/lib/solmate', + '@cwia/=lib/optimism/packages/contracts-bedrock/lib/clones-with-immutable-args/src', + '@lib-keccak/=lib/optimism/packages/contracts-bedrock/lib/lib-keccak/contracts/lib', + 'safe-contracts/=lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts', + 'kontrol-cheatcodes/=lib/optimism/packages/contracts-bedrock/lib/kontrol-cheatcodes/src', + 'solady/=lib/optimism/packages/contracts-bedrock/lib/solady/src', + + 'forge-std/=lib/forge-std/src', + 'ds-test/=lib/forge-std/lib/ds-test/src', +] # See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/rvsol/lib/optimism b/rvsol/lib/optimism new file mode 160000 index 00000000..c9340197 --- /dev/null +++ b/rvsol/lib/optimism @@ -0,0 +1 @@ +Subproject commit c934019745c98c749af986e75b2ec72c9c406723 diff --git a/rvsol/src/PreimageKeyLib.sol b/rvsol/src/PreimageKeyLib.sol deleted file mode 100644 index c3f36cb8..00000000 --- a/rvsol/src/PreimageKeyLib.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -/// @title PreimageKeyLib -/// @notice Shared utilities for localizing local keys in the preimage oracle. -library PreimageKeyLib { - /// @notice Generates a context-specific local key for the given local data identifier. - /// @dev See `localize` for a description of the localization operation. - /// @param _ident The identifier of the local data. [0, 32) bytes in size. - /// @param _localContext The local context for the key. - /// @return key_ The context-specific local key. - function localizeIdent(uint256 _ident, bytes32 _localContext) internal view returns (bytes32 key_) { - assembly { - // Set the type byte in the given identifier to `1` (Local). We only care about - // the [1, 32) bytes in this value. - key_ := or(shl(248, 1), and(_ident, not(shl(248, 0xFF)))) - } - // Localize the key with the given local context. - key_ = localize(key_, _localContext); - } - - /// @notice Localizes a given local data key for the caller's context. - /// @dev The localization operation is defined as: - /// localize(k) = H(k .. sender .. local_context) & ~(0xFF << 248) | (0x01 << 248) - /// where H is the Keccak-256 hash function. - /// @param _key The local data key to localize. - /// @param _localContext The local context for the key. - /// @return localizedKey_ The localized local data key. - function localize(bytes32 _key, bytes32 _localContext) internal view returns (bytes32 localizedKey_) { - assembly { - // Grab the current free memory pointer to restore later. - let ptr := mload(0x40) - // Store the local data key and caller next to each other in memory for hashing. - mstore(0, _key) - mstore(0x20, caller()) - mstore(0x40, _localContext) - // Localize the key with the above `localize` operation. - localizedKey_ := or(and(keccak256(0, 0x60), not(shl(248, 0xFF))), shl(248, 1)) - // Restore the free memory pointer. - mstore(0x40, ptr) - } - } - - /// @notice Computes and returns the key for a global keccak pre-image. - /// @param _preimage The pre-image. - /// @return key_ The pre-image key. - function keccak256PreimageKey(bytes memory _preimage) internal pure returns (bytes32 key_) { - assembly { - // Grab the size of the `_preimage` - let size := mload(_preimage) - - // Compute the pre-image keccak256 hash (aka the pre-image key) - let h := keccak256(add(_preimage, 0x20), size) - - // Mask out prefix byte, replace with type 2 byte - key_ := or(and(h, not(shl(248, 0xFF))), shl(248, 2)) - } - } -} diff --git a/rvsol/src/PreimageOracle.sol b/rvsol/src/PreimageOracle.sol deleted file mode 100644 index bd8fdf5b..00000000 --- a/rvsol/src/PreimageOracle.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import {IPreimageOracle} from "./interfaces/IPreimageOracle.sol"; -import {PreimageKeyLib} from "./PreimageKeyLib.sol"; - -contract PreimageOracle is IPreimageOracle { - mapping(bytes32 => uint256) public preimageLengths; - mapping(bytes32 => mapping(uint256 => bytes32)) public preimageParts; - mapping(bytes32 => mapping(uint256 => bool)) public preimagePartOk; - - function readPreimage(bytes32 _key, uint256 _offset) external view returns (bytes32 dat_, uint256 datLen_) { - require(preimagePartOk[_key][_offset], "pre-image must exist"); - - // Calculate the length of the pre-image data - // Add 8 for the length-prefix part - datLen_ = 32; - uint256 length = preimageLengths[_key]; - if (_offset + 32 >= length + 8) { - datLen_ = length + 8 - _offset; - } - - // Retrieve the pre-image data - dat_ = preimageParts[_key][_offset]; - } - - function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset) - external - returns (bytes32 key_) - { - // Compute the localized key from the given local identifier. - key_ = PreimageKeyLib.localizeIdent(_ident, _localContext); - - // Revert if the given part offset is not within bounds. - if (_partOffset > _size + 8 || _size > 32) { - // Revert with "PartOffsetOOB()" - assembly { - // Store "PartOffsetOOB()" - mstore(0, 0xfe254987) - // Revert with "PartOffsetOOB()" - revert(0x1c, 4) - } - // TODO: remove with revert PartOffsetOOB(); - } - - // Prepare the local data part at the given offset - bytes32 part; - assembly { - // Clean the memory in [0x20, 0x40) - mstore(0x20, 0x00) - - // Store the full local data in scratch space. - mstore(0x00, shl(192, _size)) - mstore(0x08, _word) - - // Prepare the local data part at the requested offset. - part := mload(_partOffset) - } - - // Store the first part with `_partOffset`. - preimagePartOk[key_][_partOffset] = true; - preimageParts[key_][_partOffset] = part; - // Assign the length of the preimage at the localized key. - preimageLengths[key_] = _size; - } - - // loadKeccak256PreimagePart prepares the pre-image to be read by keccak256 key, - // starting at the given offset, up to 32 bytes (clipped at preimage length, if out of data). - function loadKeccak256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external { - uint256 size; - bytes32 key; - bytes32 part; - assembly { - // len(sig) + len(partOffset) + len(preimage offset) = 4 + 32 + 32 = 0x44 - size := calldataload(0x44) - - // revert if part offset >= size+8 (i.e. parts must be within bounds) - if iszero(lt(_partOffset, add(size, 8))) { - // Store "PartOffsetOOB()" - mstore(0, 0xfe254987) - // Revert with "PartOffsetOOB()" - revert(0x1c, 4) - } - // we leave solidity slots 0x40 and 0x60 untouched, - // and everything after as scratch-memory. - let ptr := 0x80 - // put size as big-endian uint64 at start of pre-image - mstore(ptr, shl(192, size)) - ptr := add(ptr, 8) - // copy preimage payload into memory so we can hash and read it. - calldatacopy(ptr, _preimage.offset, size) - // Note that it includes the 8-byte big-endian uint64 length prefix. - // this will be zero-padded at the end, since memory at end is clean. - part := mload(add(sub(ptr, 8), _partOffset)) - let h := keccak256(ptr, size) // compute preimage keccak256 hash - // mask out prefix byte, replace with type 2 byte - key := or(and(h, not(shl(248, 0xFF))), shl(248, 2)) - } - preimagePartOk[key][_partOffset] = true; - preimageParts[key][_partOffset] = part; - preimageLengths[key] = size; - } - - function loadSha256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external { - // TODO: fetch diff from cannon. Currently for only satisfying interface - } -} diff --git a/rvsol/src/Step.sol b/rvsol/src/Step.sol index 5b12cd12..b3c9ae0c 100644 --- a/rvsol/src/Step.sol +++ b/rvsol/src/Step.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; +import {IPreimageOracle} from "@optimism/packages/contracts-bedrock/src/cannon/interfaces/IPreimageOracle.sol"; contract Step { - address public preimageOracle; + IPreimageOracle public preimageOracle; - constructor(address _preimageOracle) { + constructor(IPreimageOracle _preimageOracle) { preimageOracle = _preimageOracle; } diff --git a/rvsol/src/interfaces/IPreimageOracle.sol b/rvsol/src/interfaces/IPreimageOracle.sol deleted file mode 100644 index 7bffe384..00000000 --- a/rvsol/src/interfaces/IPreimageOracle.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -/// @title IPreimageOracle -/// @notice Interface for a preimage oracle. -interface IPreimageOracle { - /// @notice Reads a preimage from the oracle. - /// @param _key The key of the preimage to read. - /// @param _offset The offset of the preimage to read. - /// @return dat_ The preimage data. - /// @return datLen_ The length of the preimage data. - function readPreimage(bytes32 _key, uint256 _offset) external view returns (bytes32 dat_, uint256 datLen_); - - /// @notice Loads of local data part into the preimage oracle. - /// @param _ident The identifier of the local data. - /// @param _localContext The local key context for the preimage oracle. Optionally, can be set as a constant - /// if the caller only requires one set of local keys. - /// @param _word The local data word. - /// @param _size The number of bytes in `_word` to load. - /// @param _partOffset The offset of the local data part to write to the oracle. - /// @dev The local data parts are loaded into the preimage oracle under the context - /// of the caller - no other account can write to the caller's context - /// specific data. - /// - /// There are 5 local data identifiers: - /// ┌────────────┬────────────────────────┐ - /// │ Identifier │ Data │ - /// ├────────────┼────────────────────────┤ - /// │ 1 │ L1 Head Hash (bytes32) │ - /// │ 2 │ Output Root (bytes32) │ - /// │ 3 │ Root Claim (bytes32) │ - /// │ 4 │ L2 Block Number (u64) │ - /// │ 5 │ Chain ID (u64) │ - /// └────────────┴────────────────────────┘ - function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset) - external - returns (bytes32 key_); - - /// @notice Prepares a preimage to be read by keccak256 key, starting at the given offset and up to 32 bytes - /// (clipped at preimage length, if out of data). - /// @param _partOffset The offset of the preimage to read. - /// @param _preimage The preimage data. - function loadKeccak256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external; - - /// @notice Prepares a preimage to be read by sha256 key, starting at the given offset and up to 32 bytes - /// (clipped at preimage length, if out of data). - /// @param _partOffset The offset of the preimage to read. - /// @param _preimage The preimage data. - function loadSha256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external; -} diff --git a/rvsol/test/PreimageOracle.t.sol b/rvsol/test/PreimageOracle.t.sol deleted file mode 100644 index 7b825152..00000000 --- a/rvsol/test/PreimageOracle.t.sol +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import {Test, console2 as console} from "forge-std/Test.sol"; - -import {PreimageOracle} from "../src/PreimageOracle.sol"; -import {PreimageKeyLib} from "../src/PreimageKeyLib.sol"; - -contract PreimageOracle_Test is Test { - PreimageOracle oracle; - - /// @notice Sets up the testing suite. - function setUp() public { - oracle = new PreimageOracle(); - vm.label(address(oracle), "PreimageOracle"); - } - - /// @notice Test the pre-image key computation with a known pre-image. - function test_keccak256PreimageKey_succeeds() public { - bytes memory preimage = hex"deadbeef"; - bytes32 key = PreimageKeyLib.keccak256PreimageKey(preimage); - bytes32 known = 0x02fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1; - assertEq(key, known); - } - - /// @notice Tests that context-specific data [0, 24] bytes in length can be loaded correctly. - function test_loadLocalData_onePart_succeeds() public { - uint256 ident = 1; - bytes32 word = bytes32(uint256(0xdeadbeef) << 224); - uint8 size = 4; - uint8 partOffset = 0; - - // Load the local data into the preimage oracle under the test contract's context. - bytes32 contextKey = oracle.loadLocalData(ident, 0, word, size, partOffset); - - // Validate that the pre-image part is set - bool ok = oracle.preimagePartOk(contextKey, partOffset); - assertTrue(ok); - - // Validate the local data part - bytes32 expectedPart = 0x0000000000000004deadbeef0000000000000000000000000000000000000000; - assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart); - - // Validate the local data length - uint256 length = oracle.preimageLengths(contextKey); - assertEq(length, size); - } - - /// @notice Tests that multiple local key contexts can be used by the same address for the - /// same local data identifier. - function test_loadLocalData_multipleContexts_succeeds() public { - uint256 ident = 1; - uint8 size = 4; - uint8 partOffset = 0; - - // Form the words we'll be storing - bytes32[2] memory words = [bytes32(uint256(0xdeadbeef) << 224), bytes32(uint256(0xbeefbabe) << 224)]; - - for (uint256 i; i < words.length; i++) { - // Load the local data into the preimage oracle under the test contract's context - // and the given local context. - bytes32 contextKey = oracle.loadLocalData(ident, bytes32(i), words[i], size, partOffset); - - // Validate that the pre-image part is set - bool ok = oracle.preimagePartOk(contextKey, partOffset); - assertTrue(ok); - - // Validate the local data part - bytes32 expectedPart = bytes32(uint256(words[i] >> 64) | uint256(size) << 192); - assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart); - - // Validate the local data length - uint256 length = oracle.preimageLengths(contextKey); - assertEq(length, size); - } - } - - /// @notice Tests that context-specific data [0, 32] bytes in length can be loaded correctly. - function testFuzz_loadLocalData_varyingLength_succeeds( - uint256 ident, - bytes32 localContext, - bytes32 word, - uint256 size, - uint256 partOffset - ) public { - // Bound the size to [0, 32] - size = bound(size, 0, 32); - // Bound the part offset to [0, size + 8] - partOffset = bound(partOffset, 0, size + 8); - - // Load the local data into the preimage oracle under the test contract's context. - bytes32 contextKey = oracle.loadLocalData(ident, localContext, word, uint8(size), uint8(partOffset)); - - // Validate that the first local data part is set - bool ok = oracle.preimagePartOk(contextKey, partOffset); - assertTrue(ok); - // Validate the first local data part - bytes32 expectedPart; - assembly { - mstore(0x20, 0x00) - - mstore(0x00, shl(192, size)) - mstore(0x08, word) - - expectedPart := mload(partOffset) - } - assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart); - - // Validate the local data length - uint256 length = oracle.preimageLengths(contextKey); - assertEq(length, size); - } - - /// @notice Tests that a pre-image is correctly set. - function test_loadKeccak256PreimagePart_succeeds() public { - // Set the pre-image - bytes memory preimage = hex"deadbeef"; - bytes32 key = PreimageKeyLib.keccak256PreimageKey(preimage); - uint256 offset = 0; - oracle.loadKeccak256PreimagePart(offset, preimage); - - // Validate the pre-image part - bytes32 part = oracle.preimageParts(key, offset); - bytes32 expectedPart = 0x0000000000000004deadbeef0000000000000000000000000000000000000000; - assertEq(part, expectedPart); - - // Validate the pre-image length - uint256 length = oracle.preimageLengths(key); - assertEq(length, preimage.length); - - // Validate that the pre-image part is set - bool ok = oracle.preimagePartOk(key, offset); - assertTrue(ok); - } - - /// @notice Tests that adding a global keccak256 pre-image at the part boundary reverts. - function test_loadKeccak256PreimagePart_partBoundary_reverts() public { - bytes memory preimage = hex"deadbeef"; - uint256 offset = preimage.length + 8; - - // TODO: remove magic errors - vm.expectRevert(0xfe254987); - oracle.loadKeccak256PreimagePart(offset, preimage); - } - - /// @notice Tests that a pre-image cannot be set with an out-of-bounds offset. - function test_loadLocalData_outOfBoundsOffset_reverts() public { - bytes32 preimage = bytes32(uint256(0xdeadbeef)); - uint256 offset = preimage.length + 9; - - // TODO: remove magic errors - vm.expectRevert(0xfe254987); - oracle.loadLocalData(1, 0, preimage, 32, offset); - } - - /// @notice Reading a pre-image part that has not been set should revert. - function testFuzz_readPreimage_missingPreimage_reverts(bytes32 key, uint256 offset) public { - vm.expectRevert("pre-image must exist"); - oracle.readPreimage(key, offset); - } -}