github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/cannon/PreimageOracle.t.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { Test, console2 as console } from "forge-std/Test.sol";
     5  
     6  import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
     7  import { PreimageKeyLib } from "src/cannon/PreimageKeyLib.sol";
     8  import { LibKeccak } from "@lib-keccak/LibKeccak.sol";
     9  import { Bytes } from "src/libraries/Bytes.sol";
    10  import "src/cannon/libraries/CannonErrors.sol";
    11  import "src/cannon/libraries/CannonTypes.sol";
    12  
    13  contract PreimageOracle_Test is Test {
    14      PreimageOracle oracle;
    15  
    16      /// @notice Sets up the testing suite.
    17      function setUp() public {
    18          oracle = new PreimageOracle(0, 0);
    19          vm.label(address(oracle), "PreimageOracle");
    20      }
    21  
    22      /// @notice Test the pre-image key computation with a known pre-image.
    23      function test_keccak256PreimageKey_succeeds() public {
    24          bytes memory preimage = hex"deadbeef";
    25          bytes32 key = PreimageKeyLib.keccak256PreimageKey(preimage);
    26          bytes32 known = 0x02fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1;
    27          assertEq(key, known);
    28      }
    29  
    30      /// @notice Tests that context-specific data [0, 24] bytes in length can be loaded correctly.
    31      function test_loadLocalData_onePart_succeeds() public {
    32          uint256 ident = 1;
    33          bytes32 word = bytes32(uint256(0xdeadbeef) << 224);
    34          uint8 size = 4;
    35          uint8 partOffset = 0;
    36  
    37          // Load the local data into the preimage oracle under the test contract's context.
    38          bytes32 contextKey = oracle.loadLocalData(ident, 0, word, size, partOffset);
    39  
    40          // Validate that the pre-image part is set
    41          bool ok = oracle.preimagePartOk(contextKey, partOffset);
    42          assertTrue(ok);
    43  
    44          // Validate the local data part
    45          bytes32 expectedPart = 0x0000000000000004deadbeef0000000000000000000000000000000000000000;
    46          assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart);
    47  
    48          // Validate the local data length
    49          uint256 length = oracle.preimageLengths(contextKey);
    50          assertEq(length, size);
    51      }
    52  
    53      /// @notice Tests that multiple local key contexts can be used by the same address for the
    54      ///         same local data identifier.
    55      function test_loadLocalData_multipleContexts_succeeds() public {
    56          uint256 ident = 1;
    57          uint8 size = 4;
    58          uint8 partOffset = 0;
    59  
    60          // Form the words we'll be storing
    61          bytes32[2] memory words = [bytes32(uint256(0xdeadbeef) << 224), bytes32(uint256(0xbeefbabe) << 224)];
    62  
    63          for (uint256 i; i < words.length; i++) {
    64              // Load the local data into the preimage oracle under the test contract's context
    65              // and the given local context.
    66              bytes32 contextKey = oracle.loadLocalData(ident, bytes32(i), words[i], size, partOffset);
    67  
    68              // Validate that the pre-image part is set
    69              bool ok = oracle.preimagePartOk(contextKey, partOffset);
    70              assertTrue(ok);
    71  
    72              // Validate the local data part
    73              bytes32 expectedPart = bytes32(uint256(words[i] >> 64) | uint256(size) << 192);
    74              assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart);
    75  
    76              // Validate the local data length
    77              uint256 length = oracle.preimageLengths(contextKey);
    78              assertEq(length, size);
    79          }
    80      }
    81  
    82      /// @notice Tests that context-specific data [0, 32] bytes in length can be loaded correctly.
    83      function testFuzz_loadLocalData_varyingLength_succeeds(
    84          uint256 ident,
    85          bytes32 localContext,
    86          bytes32 word,
    87          uint256 size,
    88          uint256 partOffset
    89      )
    90          public
    91      {
    92          // Bound the size to [0, 32]
    93          size = bound(size, 0, 32);
    94          // Bound the part offset to [0, size + 8]
    95          partOffset = bound(partOffset, 0, size + 8);
    96  
    97          // Load the local data into the preimage oracle under the test contract's context.
    98          bytes32 contextKey = oracle.loadLocalData(ident, localContext, word, uint8(size), uint8(partOffset));
    99  
   100          // Validate that the first local data part is set
   101          bool ok = oracle.preimagePartOk(contextKey, partOffset);
   102          assertTrue(ok);
   103          // Validate the first local data part
   104          bytes32 expectedPart;
   105          assembly {
   106              mstore(0x20, 0x00)
   107  
   108              mstore(0x00, shl(192, size))
   109              mstore(0x08, word)
   110  
   111              expectedPart := mload(partOffset)
   112          }
   113          assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart);
   114  
   115          // Validate the local data length
   116          uint256 length = oracle.preimageLengths(contextKey);
   117          assertEq(length, size);
   118      }
   119  
   120      /// @notice Tests that a pre-image is correctly set.
   121      function test_loadKeccak256PreimagePart_succeeds() public {
   122          // Set the pre-image
   123          bytes memory preimage = hex"deadbeef";
   124          bytes32 key = PreimageKeyLib.keccak256PreimageKey(preimage);
   125          uint256 offset = 0;
   126          oracle.loadKeccak256PreimagePart(offset, preimage);
   127  
   128          // Validate the pre-image part
   129          bytes32 part = oracle.preimageParts(key, offset);
   130          bytes32 expectedPart = 0x0000000000000004deadbeef0000000000000000000000000000000000000000;
   131          assertEq(part, expectedPart);
   132  
   133          // Validate the pre-image length
   134          uint256 length = oracle.preimageLengths(key);
   135          assertEq(length, preimage.length);
   136  
   137          // Validate that the pre-image part is set
   138          bool ok = oracle.preimagePartOk(key, offset);
   139          assertTrue(ok);
   140      }
   141  
   142      /// @notice Tests that adding a global keccak256 pre-image at the part boundary reverts.
   143      function test_loadKeccak256PreimagePart_partBoundary_reverts() public {
   144          bytes memory preimage = hex"deadbeef";
   145          uint256 offset = preimage.length + 8;
   146  
   147          vm.expectRevert(PartOffsetOOB.selector);
   148          oracle.loadKeccak256PreimagePart(offset, preimage);
   149      }
   150  
   151      /// @notice Tests that a pre-image cannot be set with an out-of-bounds offset.
   152      function test_loadLocalData_outOfBoundsOffset_reverts() public {
   153          bytes32 preimage = bytes32(uint256(0xdeadbeef));
   154          uint256 offset = preimage.length + 9;
   155  
   156          vm.expectRevert(PartOffsetOOB.selector);
   157          oracle.loadLocalData(1, 0, preimage, 32, offset);
   158      }
   159  
   160      /// @notice Tests that a pre-image cannot be set with an out-of-bounds offset.
   161      function test_loadKeccak256PreimagePart_outOfBoundsOffset_reverts() public {
   162          bytes memory preimage = hex"deadbeef";
   163          uint256 offset = preimage.length + 9;
   164  
   165          vm.expectRevert(PartOffsetOOB.selector);
   166          oracle.loadKeccak256PreimagePart(offset, preimage);
   167      }
   168  
   169      /// @notice Reading a pre-image part that has not been set should revert.
   170      function testFuzz_readPreimage_missingPreimage_reverts(bytes32 key, uint256 offset) public {
   171          vm.expectRevert("pre-image must exist");
   172          oracle.readPreimage(key, offset);
   173      }
   174  
   175      /// @notice Tests that a precompile pre-image result is correctly set.
   176      function test_loadPrecompilePreimagePart_succeeds() public {
   177          bytes memory input = hex"deadbeef";
   178          uint256 offset = 0;
   179          address precompile = address(bytes20(uint160(0x02))); // sha256
   180          bytes32 key = precompilePreimageKey(precompile, input);
   181          oracle.loadPrecompilePreimagePart(offset, precompile, input);
   182  
   183          bytes32 part = oracle.preimageParts(key, offset);
   184          // size prefix - 1-byte result + 32-byte sha return data
   185          assertEq(hex"0000000000000021", bytes8(part));
   186          // precompile result
   187          assertEq(bytes1(0x01), bytes1(part << 64));
   188          // precompile call return data
   189          assertEq(bytes23(sha256(input)), bytes23(part << 72));
   190  
   191          // Validate the local data length
   192          uint256 length = oracle.preimageLengths(key);
   193          assertEq(length, 1 + 32);
   194  
   195          // Validate that the first local data part is set
   196          bool ok = oracle.preimagePartOk(key, offset);
   197          assertTrue(ok);
   198      }
   199  
   200      /// @notice Tests that a precompile pre-image result is correctly set at its return data offset.
   201      function test_loadPrecompilePreimagePart_atReturnOffset_succeeds() public {
   202          bytes memory input = hex"deadbeef";
   203          uint256 offset = 9;
   204          address precompile = address(bytes20(uint160(0x02))); // sha256
   205          bytes32 key = precompilePreimageKey(precompile, input);
   206          oracle.loadPrecompilePreimagePart(offset, precompile, input);
   207  
   208          bytes32 part = oracle.preimageParts(key, offset);
   209          // 32-byte sha return data
   210          assertEq(sha256(input), part);
   211  
   212          // Validate the local data length
   213          uint256 length = oracle.preimageLengths(key);
   214          assertEq(length, 1 + 32);
   215  
   216          // Validate that the first local data part is set
   217          bool ok = oracle.preimagePartOk(key, offset);
   218          assertTrue(ok);
   219      }
   220  
   221      /// @notice Tests that a failed precompile call has a zero status byte in preimage
   222      function test_loadPrecompilePreimagePart_failedCall_succeeds() public {
   223          bytes memory input = new bytes(193); // invalid input to induce a failed precompile call
   224          uint256 offset = 0;
   225          address precompile = address(bytes20(uint160(0x08))); // bn256Pairing
   226          bytes32 key = precompilePreimageKey(precompile, input);
   227          oracle.loadPrecompilePreimagePart(offset, precompile, input);
   228  
   229          bytes32 part = oracle.preimageParts(key, offset);
   230          // size prefix - 1-byte result + 0-byte sha return data
   231          assertEq(hex"0000000000000001", bytes8(part));
   232          // precompile result
   233          assertEq(bytes1(0x00), bytes1(part << 64));
   234          // precompile call return data
   235          assertEq(bytes23(0), bytes23(part << 72));
   236  
   237          // Validate the local data length
   238          uint256 length = oracle.preimageLengths(key);
   239          assertEq(length, 1);
   240  
   241          // Validate that the first local data part is set
   242          bool ok = oracle.preimagePartOk(key, offset);
   243          assertTrue(ok);
   244      }
   245  
   246      /// @notice Tests that adding a global precompile result at the part boundary reverts.
   247      function test_loadPrecompilePreimagePart_partBoundary_reverts() public {
   248          bytes memory input = hex"deadbeef";
   249          uint256 offset = 41; // 8-byte prefix + 1-byte result + 32-byte sha return data
   250          address precompile = address(bytes20(uint160(0x02))); // sha256
   251          vm.expectRevert(PartOffsetOOB.selector);
   252          oracle.loadPrecompilePreimagePart(offset, precompile, input);
   253      }
   254  
   255      /// @notice Tests that a global precompile result cannot be set with an out-of-bounds offset.
   256      function test_loadPrecompilePreimagePart_outOfBoundsOffset_reverts() public {
   257          bytes memory input = hex"deadbeef";
   258          uint256 offset = 42;
   259          address precompile = address(bytes20(uint160(0x02))); // sha256
   260          vm.expectRevert(PartOffsetOOB.selector);
   261          oracle.loadPrecompilePreimagePart(offset, precompile, input);
   262      }
   263  }
   264  
   265  contract PreimageOracle_LargePreimageProposals_Test is Test {
   266      uint256 internal constant MIN_SIZE_BYTES = 0;
   267      uint256 internal constant CHALLENGE_PERIOD = 1 days;
   268      uint256 internal constant TEST_UUID = 0xFACADE;
   269  
   270      PreimageOracle internal oracle;
   271  
   272      /// @notice Sets up the testing suite.
   273      function setUp() public {
   274          oracle = new PreimageOracle({ _minProposalSize: MIN_SIZE_BYTES, _challengePeriod: CHALLENGE_PERIOD });
   275          vm.label(address(oracle), "PreimageOracle");
   276  
   277          // Set `tx.origin` and `msg.sender` to `address(this)` so that it may behave like an EOA for `addLeavesLPP`.
   278          vm.startPrank(address(this), address(this));
   279  
   280          // Give this address some ETH to work with.
   281          vm.deal(address(this), 100 ether);
   282      }
   283  
   284      /// @notice Tests that the `initLPP` function reverts when the part offset is out of bounds of the full preimage.
   285      function test_initLPP_partOffsetOOB_reverts() public {
   286          // Allocate the preimage data.
   287          bytes memory data = new bytes(136);
   288          for (uint256 i; i < data.length; i++) {
   289              data[i] = 0xFF;
   290          }
   291  
   292          // Initialize the proposal.
   293          uint256 bondSize = oracle.MIN_BOND_SIZE();
   294          vm.expectRevert(PartOffsetOOB.selector);
   295          oracle.initLPP{ value: bondSize }(TEST_UUID, 136 + 8, uint32(data.length));
   296      }
   297  
   298      /// @notice Tests that the `initLPP` function reverts when the part offset is out of bounds of the full preimage.
   299      function test_initLPP_sizeTooSmall_reverts() public {
   300          oracle = new PreimageOracle({ _minProposalSize: 1000, _challengePeriod: CHALLENGE_PERIOD });
   301  
   302          // Allocate the preimage data.
   303          bytes memory data = new bytes(136);
   304          for (uint256 i; i < data.length; i++) {
   305              data[i] = 0xFF;
   306          }
   307  
   308          // Initialize the proposal.
   309          uint256 bondSize = oracle.MIN_BOND_SIZE();
   310          vm.expectRevert(InvalidInputSize.selector);
   311          oracle.initLPP{ value: bondSize }(TEST_UUID, 0, uint32(data.length));
   312      }
   313  
   314      /// @notice Gas snapshot for `addLeaves`
   315      function test_addLeaves_gasSnapshot() public {
   316          // Allocate the preimage data.
   317          bytes memory data = new bytes(136 * 500);
   318          for (uint256 i; i < data.length; i++) {
   319              data[i] = 0xFF;
   320          }
   321  
   322          // Initialize the proposal.
   323          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   324  
   325          // Add the leaves to the tree (2 keccak blocks.)
   326          LibKeccak.StateMatrix memory stateMatrix;
   327          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   328  
   329          // Allocate the calldata so it isn't included in the gas measurement.
   330          bytes memory cd = abi.encodeCall(oracle.addLeavesLPP, (TEST_UUID, 0, data, stateCommitments, true));
   331  
   332          uint256 gas = gasleft();
   333          (bool success,) = address(oracle).call(cd);
   334          uint256 gasUsed = gas - gasleft();
   335          assertTrue(success);
   336  
   337          console.log("Gas used: %d", gasUsed);
   338          console.log("Gas per byte (%d bytes streamed): %d", data.length, gasUsed / data.length);
   339          console.log("Gas for 4MB: %d", (gasUsed / data.length) * 4000000);
   340      }
   341  
   342      /// @notice Tests that `addLeavesLPP` sets the proposal as countered when `_finalize = true` and the number of
   343      ///         bytes processed is less than the claimed size.
   344      function test_addLeaves_mismatchedSize_succeeds() public {
   345          // Allocate the preimage data.
   346          bytes memory data = new bytes(136);
   347          for (uint256 i; i < data.length; i++) {
   348              data[i] = 0xFF;
   349          }
   350  
   351          // Initialize the proposal.
   352          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length + 1));
   353  
   354          // Add the leaves to the tree (2 keccak blocks.)
   355          LibKeccak.StateMatrix memory stateMatrix;
   356          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   357  
   358          vm.expectRevert(InvalidInputSize.selector);
   359          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   360      }
   361  
   362      /// @notice Tests that the `addLeavesLPP` function may never be called when `tx.origin != msg.sender`
   363      function test_addLeaves_notEOA_reverts() public {
   364          // Allocate the preimage data.
   365          bytes memory data = new bytes(136 * 500);
   366  
   367          // Initialize the proposal.
   368          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   369  
   370          // Add the leaves to the tree (2 keccak blocks.)
   371          LibKeccak.StateMatrix memory stateMatrix;
   372          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   373  
   374          // Replace the global prank, set `tx.origin` to `address(0)`, and set `msg.sender` to `address(this)`.
   375          vm.stopPrank();
   376          vm.prank(address(0), address(this));
   377  
   378          vm.expectRevert(NotEOA.selector);
   379          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   380      }
   381  
   382      /// @notice Tests that the `addLeavesLPP` function reverts when the starting block index is not what is expected.
   383      function test_addLeaves_notContiguous_reverts() public {
   384          // Allocate the preimage data.
   385          bytes memory data = new bytes(136 * 500);
   386  
   387          // Initialize the proposal.
   388          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   389  
   390          // Add the leaves to the tree (2 keccak blocks.)
   391          LibKeccak.StateMatrix memory stateMatrix;
   392          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   393  
   394          vm.expectRevert(WrongStartingBlock.selector);
   395          oracle.addLeavesLPP(TEST_UUID, 1, data, stateCommitments, true);
   396      }
   397  
   398      /// @notice Tests that leaves can be added the large preimage proposal mapping and proven to be contained within
   399      ///         the computed merkle root.
   400      function test_addLeaves_multipleParts_succeeds() public {
   401          // Allocate the preimage data.
   402          bytes memory data = new bytes(136 * 3);
   403          for (uint256 i; i < data.length; i++) {
   404              data[i] = 0xFF;
   405          }
   406  
   407          // Initialize the proposal.
   408          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   409          // Ensure that the proposal keys are present in the array.
   410          (address claimant, uint256 uuid) = oracle.proposals(0);
   411          assertEq(oracle.proposalCount(), 1);
   412          assertEq(claimant, address(this));
   413          assertEq(uuid, TEST_UUID);
   414  
   415          // Add the leaves to the tree (2 keccak blocks.)
   416          LibKeccak.StateMatrix memory stateMatrix;
   417          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   418  
   419          uint256 midPoint = stateCommitments.length / 2;
   420          bytes32[] memory commitmentsA = new bytes32[](midPoint);
   421          bytes32[] memory commitmentsB = new bytes32[](midPoint);
   422          for (uint256 i = 0; i < midPoint; i++) {
   423              commitmentsA[i] = stateCommitments[i];
   424              commitmentsB[i] = stateCommitments[i + midPoint];
   425          }
   426  
   427          oracle.addLeavesLPP(TEST_UUID, 0, Bytes.slice(data, 0, 136 * 2), commitmentsA, false);
   428  
   429          // MetaData assertions
   430          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
   431          assertEq(metaData.timestamp(), 0);
   432          assertEq(metaData.partOffset(), 0);
   433          assertEq(metaData.claimedSize(), data.length);
   434          assertEq(metaData.blocksProcessed(), 2);
   435          assertEq(metaData.bytesProcessed(), 136 * 2);
   436          assertFalse(metaData.countered());
   437  
   438          // Move ahead one block.
   439          vm.roll(block.number + 1);
   440  
   441          oracle.addLeavesLPP(TEST_UUID, 2, Bytes.slice(data, 136 * 2, 136), commitmentsB, true);
   442  
   443          // MetaData assertions
   444          metaData = oracle.proposalMetadata(address(this), TEST_UUID);
   445          assertEq(metaData.timestamp(), 1);
   446          assertEq(metaData.partOffset(), 0);
   447          assertEq(metaData.claimedSize(), data.length);
   448          assertEq(metaData.blocksProcessed(), 4);
   449          assertEq(metaData.bytesProcessed(), data.length);
   450          assertFalse(metaData.countered());
   451  
   452          // Preimage part assertions
   453          bytes32 expectedPart = bytes32((~uint256(0) & ~(uint256(type(uint64).max) << 192)) | (data.length << 192));
   454          assertEq(oracle.proposalParts(address(this), TEST_UUID), expectedPart);
   455  
   456          assertEq(oracle.proposalBlocks(address(this), TEST_UUID, 0), block.number - 1);
   457          assertEq(oracle.proposalBlocks(address(this), TEST_UUID, 1), block.number);
   458  
   459          // Should revert if we try to add new leaves.
   460          vm.expectRevert(AlreadyFinalized.selector);
   461          oracle.addLeavesLPP(TEST_UUID, 4, data, stateCommitments, true);
   462      }
   463  
   464      /// @notice Tests that leaves cannot be added until the large preimage proposal has been initialized.
   465      function test_addLeaves_notInitialized_reverts() public {
   466          // Allocate the preimage data.
   467          bytes memory data = new bytes(136 * 500);
   468  
   469          // Add the leaves to the tree (2 keccak blocks.)
   470          LibKeccak.StateMatrix memory stateMatrix;
   471          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   472  
   473          // Allocate the calldata so it isn't included in the gas measurement.
   474          vm.expectRevert(NotInitialized.selector);
   475          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   476      }
   477  
   478      /// @notice Tests that leaves can be added the large preimage proposal mapping and finalized to be added to the
   479      ///         authorized mappings.
   480      function test_squeeze_challengePeriodPassed_succeeds() public {
   481          // Allocate the preimage data.
   482          bytes memory data = new bytes(136);
   483          for (uint256 i; i < data.length; i++) {
   484              data[i] = 0xFF;
   485          }
   486  
   487          // Initialize the proposal.
   488          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   489  
   490          // Add the leaves to the tree (2 keccak blocks.)
   491          LibKeccak.StateMatrix memory stateMatrix;
   492          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   493          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   494  
   495          // Construct the leaf preimage data for the blocks added.
   496          LibKeccak.StateMatrix memory matrix;
   497          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
   498  
   499          // Create a proof array with 16 elements.
   500          bytes32[] memory preProof = new bytes32[](16);
   501          preProof[0] = _hashLeaf(leaves[1]);
   502          bytes32[] memory postProof = new bytes32[](16);
   503          postProof[0] = _hashLeaf(leaves[0]);
   504          for (uint256 i = 1; i < preProof.length; i++) {
   505              bytes32 zeroHash = oracle.zeroHashes(i);
   506              preProof[i] = zeroHash;
   507              postProof[i] = zeroHash;
   508          }
   509  
   510          vm.warp(block.timestamp + oracle.challengePeriod() + 1 seconds);
   511  
   512          // Finalize the proposal.
   513          uint256 balanceBefore = address(this).balance;
   514          oracle.squeezeLPP({
   515              _claimant: address(this),
   516              _uuid: TEST_UUID,
   517              _stateMatrix: _stateMatrixAtBlockIndex(data, 1),
   518              _preState: leaves[0],
   519              _preStateProof: preProof,
   520              _postState: leaves[1],
   521              _postStateProof: postProof
   522          });
   523          assertEq(address(this).balance, balanceBefore + oracle.MIN_BOND_SIZE());
   524          assertEq(oracle.proposalBonds(address(this), TEST_UUID), 0);
   525  
   526          bytes32 finalDigest = _setStatusByte(keccak256(data), 2);
   527          bytes32 expectedPart = bytes32((~uint256(0) & ~(uint256(type(uint64).max) << 192)) | (data.length << 192));
   528          assertTrue(oracle.preimagePartOk(finalDigest, 0));
   529          assertEq(oracle.preimageLengths(finalDigest), data.length);
   530          assertEq(oracle.preimageParts(finalDigest, 0), expectedPart);
   531      }
   532  
   533      /// @notice Tests that a proposal cannot be finalized until it has passed the challenge period.
   534      function test_squeeze_proposalChallenged_reverts() public {
   535          // Allocate the preimage data.
   536          bytes memory data = new bytes(136);
   537          for (uint256 i; i < data.length; i++) {
   538              data[i] = 0xFF;
   539          }
   540          bytes memory phonyData = new bytes(136);
   541  
   542          // Initialize the proposal.
   543          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   544  
   545          // Add the leaves to the tree with mismatching state commitments.
   546          LibKeccak.StateMatrix memory stateMatrix;
   547          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   548          oracle.addLeavesLPP(TEST_UUID, 0, phonyData, stateCommitments, true);
   549  
   550          // Construct the leaf preimage data for the blocks added.
   551          LibKeccak.StateMatrix memory matrix;
   552          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, phonyData);
   553          leaves[0].stateCommitment = stateCommitments[0];
   554          leaves[1].stateCommitment = stateCommitments[1];
   555  
   556          // Create a proof array with 16 elements.
   557          bytes32[] memory preProof = new bytes32[](16);
   558          preProof[0] = _hashLeaf(leaves[1]);
   559          bytes32[] memory postProof = new bytes32[](16);
   560          postProof[0] = _hashLeaf(leaves[0]);
   561          for (uint256 i = 1; i < preProof.length; i++) {
   562              bytes32 zeroHash = oracle.zeroHashes(i);
   563              preProof[i] = zeroHash;
   564              postProof[i] = zeroHash;
   565          }
   566  
   567          // Should succeed since the commitment was wrong.
   568          oracle.challengeFirstLPP({
   569              _claimant: address(this),
   570              _uuid: TEST_UUID,
   571              _postState: leaves[0],
   572              _postStateProof: preProof
   573          });
   574  
   575          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
   576          assertTrue(metaData.countered());
   577  
   578          vm.warp(block.timestamp + oracle.challengePeriod() + 1 seconds);
   579  
   580          // Finalize the proposal.
   581          vm.expectRevert(BadProposal.selector);
   582          oracle.squeezeLPP({
   583              _claimant: address(this),
   584              _uuid: TEST_UUID,
   585              _stateMatrix: _stateMatrixAtBlockIndex(data, 1),
   586              _preState: leaves[0],
   587              _preStateProof: preProof,
   588              _postState: leaves[1],
   589              _postStateProof: postProof
   590          });
   591      }
   592  
   593      /// @notice Tests that a proposal cannot be finalized until it has passed the challenge period.
   594      function test_squeeze_challengePeriodActive_reverts() public {
   595          // Allocate the preimage data.
   596          bytes memory data = new bytes(136);
   597          for (uint256 i; i < data.length; i++) {
   598              data[i] = 0xFF;
   599          }
   600  
   601          // Initialize the proposal.
   602          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   603  
   604          // Add the leaves to the tree (2 keccak blocks.)
   605          LibKeccak.StateMatrix memory stateMatrix;
   606          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   607          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   608  
   609          // Construct the leaf preimage data for the blocks added.
   610          LibKeccak.StateMatrix memory matrix;
   611          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
   612  
   613          // Finalize the proposal.
   614          vm.expectRevert(ActiveProposal.selector);
   615          oracle.squeezeLPP({
   616              _claimant: address(this),
   617              _uuid: TEST_UUID,
   618              _stateMatrix: _stateMatrixAtBlockIndex(data, 1),
   619              _preState: leaves[0],
   620              _preStateProof: new bytes32[](16),
   621              _postState: leaves[1],
   622              _postStateProof: new bytes32[](16)
   623          });
   624      }
   625  
   626      /// @notice Tests that a proposal cannot be finalized until it has passed the challenge period.
   627      function test_squeeze_incompleteAbsorbtion_reverts() public {
   628          // Allocate the preimage data.
   629          bytes memory data = new bytes(136);
   630          for (uint256 i; i < data.length; i++) {
   631              data[i] = 0xFF;
   632          }
   633  
   634          // Initialize the proposal.
   635          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   636  
   637          // Construct the leaf preimage data for the blocks added.
   638          LibKeccak.StateMatrix memory matrix;
   639          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
   640  
   641          // Finalize the proposal.
   642          vm.expectRevert(ActiveProposal.selector);
   643          oracle.squeezeLPP({
   644              _claimant: address(this),
   645              _uuid: TEST_UUID,
   646              _stateMatrix: _stateMatrixAtBlockIndex(data, 1),
   647              _preState: leaves[0],
   648              _preStateProof: new bytes32[](16),
   649              _postState: leaves[1],
   650              _postStateProof: new bytes32[](16)
   651          });
   652      }
   653  
   654      /// @notice Tests that the `squeeze` function reverts when the passed states are not contiguous.
   655      function test_squeeze_statesNotContiguous_reverts() public {
   656          // Allocate the preimage data.
   657          bytes memory data = new bytes(136);
   658          for (uint256 i; i < data.length; i++) {
   659              data[i] = 0xFF;
   660          }
   661  
   662          // Initialize the proposal.
   663          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   664  
   665          // Add the leaves to the tree (2 keccak blocks.)
   666          LibKeccak.StateMatrix memory stateMatrix;
   667          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   668          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   669  
   670          // Construct the leaf preimage data for the blocks added.
   671          LibKeccak.StateMatrix memory matrix;
   672          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
   673  
   674          // Create a proof array with 16 elements.
   675          bytes32[] memory preProof = new bytes32[](16);
   676          preProof[0] = _hashLeaf(leaves[1]);
   677          bytes32[] memory postProof = new bytes32[](16);
   678          postProof[0] = _hashLeaf(leaves[0]);
   679          for (uint256 i = 1; i < preProof.length; i++) {
   680              bytes32 zeroHash = oracle.zeroHashes(i);
   681              preProof[i] = zeroHash;
   682              postProof[i] = zeroHash;
   683          }
   684  
   685          vm.warp(block.timestamp + oracle.challengePeriod() + 1 seconds);
   686  
   687          // Finalize the proposal.
   688          vm.expectRevert(StatesNotContiguous.selector);
   689          oracle.squeezeLPP({
   690              _claimant: address(this),
   691              _uuid: TEST_UUID,
   692              _stateMatrix: _stateMatrixAtBlockIndex(data, 2),
   693              _preState: leaves[1],
   694              _preStateProof: postProof,
   695              _postState: leaves[0],
   696              _postStateProof: preProof
   697          });
   698      }
   699  
   700      /// @notice Tests that the `squeeze` function reverts when the post state passed
   701      function test_squeeze_invalidPreimage_reverts() public {
   702          // Allocate the preimage data.
   703          bytes memory data = new bytes(136);
   704          for (uint256 i; i < data.length; i++) {
   705              data[i] = 0xFF;
   706          }
   707  
   708          // Initialize the proposal.
   709          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   710  
   711          // Add the leaves to the tree (2 keccak blocks.)
   712          LibKeccak.StateMatrix memory stateMatrix;
   713          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   714          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   715  
   716          // Construct the leaf preimage data for the blocks added.
   717          LibKeccak.StateMatrix memory matrix;
   718          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
   719  
   720          // Create a proof array with 16 elements.
   721          bytes32[] memory preProof = new bytes32[](16);
   722          preProof[0] = _hashLeaf(leaves[1]);
   723          bytes32[] memory postProof = new bytes32[](16);
   724          postProof[0] = _hashLeaf(leaves[0]);
   725          for (uint256 i = 1; i < preProof.length; i++) {
   726              bytes32 zeroHash = oracle.zeroHashes(i);
   727              preProof[i] = zeroHash;
   728              postProof[i] = zeroHash;
   729          }
   730  
   731          vm.warp(block.timestamp + oracle.challengePeriod() + 1 seconds);
   732  
   733          // Finalize the proposal.
   734          vm.expectRevert(InvalidPreimage.selector);
   735          oracle.squeezeLPP({
   736              _claimant: address(this),
   737              _uuid: TEST_UUID,
   738              _stateMatrix: _stateMatrixAtBlockIndex(data, 2),
   739              _preState: leaves[0],
   740              _preStateProof: preProof,
   741              _postState: leaves[1],
   742              _postStateProof: postProof
   743          });
   744      }
   745  
   746      /// @notice Tests that squeezing a large preimage proposal after the challenge period has passed always succeeds and
   747      ///         persists the correct data.
   748      function testFuzz_squeeze_succeeds(uint256 _numBlocks, uint32 _partOffset) public {
   749          _numBlocks = bound(_numBlocks, 1, 2 ** 8);
   750          _partOffset = uint32(bound(_partOffset, 0, _numBlocks * LibKeccak.BLOCK_SIZE_BYTES + 8 - 1));
   751  
   752          // Allocate the preimage data.
   753          bytes memory data = new bytes(136 * _numBlocks);
   754          for (uint256 i; i < data.length; i++) {
   755              data[i] = bytes1(uint8(i % 256));
   756          }
   757  
   758          // Propose and squeeze a large preimage.
   759          {
   760              // Initialize the proposal.
   761              oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, _partOffset, uint32(data.length));
   762  
   763              // Add the leaves to the tree with correct state commitments.
   764              LibKeccak.StateMatrix memory matrixA;
   765              bytes32[] memory stateCommitments = _generateStateCommitments(matrixA, data);
   766              oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   767  
   768              // Construct the leaf preimage data for the blocks added.
   769              LibKeccak.StateMatrix memory matrixB;
   770              PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrixB, data);
   771  
   772              // Fetch the merkle proofs for the pre/post state leaves in the proposal tree.
   773              bytes32 canonicalRoot = oracle.getTreeRootLPP(address(this), TEST_UUID);
   774              (bytes32 rootA, bytes32[] memory preProof) = _generateProof(leaves.length - 2, leaves);
   775              assertEq(rootA, canonicalRoot);
   776              (bytes32 rootB, bytes32[] memory postProof) = _generateProof(leaves.length - 1, leaves);
   777              assertEq(rootB, canonicalRoot);
   778  
   779              // Warp past the challenge period.
   780              vm.warp(block.timestamp + CHALLENGE_PERIOD + 1 seconds);
   781  
   782              // Squeeze the LPP.
   783              LibKeccak.StateMatrix memory preMatrix = _stateMatrixAtBlockIndex(data, leaves.length - 1);
   784              oracle.squeezeLPP({
   785                  _claimant: address(this),
   786                  _uuid: TEST_UUID,
   787                  _stateMatrix: preMatrix,
   788                  _preState: leaves[leaves.length - 2],
   789                  _preStateProof: preProof,
   790                  _postState: leaves[leaves.length - 1],
   791                  _postStateProof: postProof
   792              });
   793          }
   794  
   795          // Validate the preimage part
   796          {
   797              bytes32 finalDigest = _setStatusByte(keccak256(data), 2);
   798              bytes32 expectedPart;
   799              assembly {
   800                  switch lt(_partOffset, 0x08)
   801                  case true {
   802                      mstore(0x00, shl(192, mload(data)))
   803                      mstore(0x08, mload(add(data, 0x20)))
   804                      expectedPart := mload(_partOffset)
   805                  }
   806                  default {
   807                      // Clean the word after `data` so we don't get any dirty bits.
   808                      mstore(add(add(data, 0x20), mload(data)), 0x00)
   809                      expectedPart := mload(add(add(data, 0x20), sub(_partOffset, 0x08)))
   810                  }
   811              }
   812  
   813              assertTrue(oracle.preimagePartOk(finalDigest, _partOffset));
   814              assertEq(oracle.preimageLengths(finalDigest), data.length);
   815              assertEq(oracle.preimageParts(finalDigest, _partOffset), expectedPart);
   816          }
   817      }
   818  
   819      /// @notice Tests that a valid leaf cannot be countered with the `challengeFirst` function.
   820      function test_challengeFirst_validCommitment_reverts() public {
   821          // Allocate the preimage data.
   822          bytes memory data = new bytes(136);
   823          for (uint256 i; i < data.length; i++) {
   824              data[i] = 0xFF;
   825          }
   826  
   827          // Initialize the proposal.
   828          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   829  
   830          // Add the leaves to the tree with mismatching state commitments.
   831          LibKeccak.StateMatrix memory stateMatrix;
   832          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   833          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   834  
   835          // Construct the leaf preimage data for the blocks added.
   836          LibKeccak.StateMatrix memory matrix;
   837          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
   838  
   839          // Create a proof array with 16 elements.
   840          bytes32[] memory p = new bytes32[](16);
   841          p[0] = _hashLeaf(leaves[1]);
   842          for (uint256 i = 1; i < p.length; i++) {
   843              p[i] = oracle.zeroHashes(i);
   844          }
   845  
   846          vm.expectRevert(PostStateMatches.selector);
   847          oracle.challengeFirstLPP({
   848              _claimant: address(this),
   849              _uuid: TEST_UUID,
   850              _postState: leaves[0],
   851              _postStateProof: p
   852          });
   853  
   854          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
   855          assertFalse(metaData.countered());
   856      }
   857  
   858      /// @notice Tests that an invalid leaf cannot be countered with `challengeFirst` if it is not the first leaf.
   859      function test_challengeFirst_statesNotContiguous_reverts() public {
   860          // Allocate the preimage data.
   861          bytes memory data = new bytes(136);
   862          for (uint256 i; i < data.length; i++) {
   863              data[i] = 0xFF;
   864          }
   865          bytes memory phonyData = new bytes(136);
   866  
   867          // Initialize the proposal.
   868          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   869  
   870          // Add the leaves to the tree with mismatching state commitments.
   871          LibKeccak.StateMatrix memory stateMatrix;
   872          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   873          oracle.addLeavesLPP(TEST_UUID, 0, phonyData, stateCommitments, true);
   874  
   875          // Construct the leaf preimage data for the blocks added.
   876          LibKeccak.StateMatrix memory matrix;
   877          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, phonyData);
   878          leaves[0].stateCommitment = stateCommitments[0];
   879          leaves[1].stateCommitment = stateCommitments[1];
   880  
   881          // Create a proof array with 16 elements.
   882          bytes32[] memory p = new bytes32[](16);
   883          p[0] = _hashLeaf(leaves[0]);
   884          for (uint256 i = 1; i < p.length; i++) {
   885              p[i] = oracle.zeroHashes(i);
   886          }
   887  
   888          // Should succeed since the commitment was wrong.
   889          vm.expectRevert(StatesNotContiguous.selector);
   890          oracle.challengeFirstLPP({
   891              _claimant: address(this),
   892              _uuid: TEST_UUID,
   893              _postState: leaves[1],
   894              _postStateProof: p
   895          });
   896      }
   897  
   898      /// @notice Tests that an invalid leaf can be countered with the `challengeFirst` function.
   899      function test_challengeFirst_invalidCommitment_succeeds() public {
   900          // Allocate the preimage data.
   901          bytes memory data = new bytes(136);
   902          for (uint256 i; i < data.length; i++) {
   903              data[i] = 0xFF;
   904          }
   905          bytes memory phonyData = new bytes(136);
   906  
   907          // Initialize the proposal.
   908          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   909  
   910          // Add the leaves to the tree with mismatching state commitments.
   911          LibKeccak.StateMatrix memory stateMatrix;
   912          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
   913          oracle.addLeavesLPP(TEST_UUID, 0, phonyData, stateCommitments, true);
   914  
   915          // Construct the leaf preimage data for the blocks added.
   916          LibKeccak.StateMatrix memory matrix;
   917          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, phonyData);
   918          leaves[0].stateCommitment = stateCommitments[0];
   919          leaves[1].stateCommitment = stateCommitments[1];
   920  
   921          // Create a proof array with 16 elements.
   922          bytes32[] memory p = new bytes32[](16);
   923          p[0] = _hashLeaf(leaves[1]);
   924          for (uint256 i = 1; i < p.length; i++) {
   925              p[i] = oracle.zeroHashes(i);
   926          }
   927  
   928          // Should succeed since the commitment was wrong.
   929          uint256 balanceBefore = address(this).balance;
   930          oracle.challengeFirstLPP({
   931              _claimant: address(this),
   932              _uuid: TEST_UUID,
   933              _postState: leaves[0],
   934              _postStateProof: p
   935          });
   936          assertEq(address(this).balance, balanceBefore + oracle.MIN_BOND_SIZE());
   937          assertEq(oracle.proposalBonds(address(this), TEST_UUID), 0);
   938  
   939          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
   940          assertTrue(metaData.countered());
   941      }
   942  
   943      /// @notice Tests that challenging the first divergence in a large preimage proposal at an arbitrary location
   944      ///         in the leaf values always succeeds.
   945      function testFuzz_challenge_arbitraryLocation_succeeds(uint256 _lastCorrectLeafIdx, uint256 _numBlocks) public {
   946          _numBlocks = bound(_numBlocks, 1, 2 ** 8);
   947          _lastCorrectLeafIdx = bound(_lastCorrectLeafIdx, 0, _numBlocks - 1);
   948  
   949          // Allocate the preimage data.
   950          bytes memory data = new bytes(136 * _numBlocks);
   951  
   952          // Initialize the proposal.
   953          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
   954  
   955          // Add the leaves to the tree with corrupted state commitments.
   956          LibKeccak.StateMatrix memory matrixA;
   957          bytes32[] memory stateCommitments = _generateStateCommitments(matrixA, data);
   958          for (uint256 i = _lastCorrectLeafIdx + 1; i < stateCommitments.length; i++) {
   959              stateCommitments[i] = 0;
   960          }
   961          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
   962  
   963          // Construct the leaf preimage data for the blocks added and corrupt the state commitments.
   964          LibKeccak.StateMatrix memory matrixB;
   965          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrixB, data);
   966          for (uint256 i = _lastCorrectLeafIdx + 1; i < leaves.length; i++) {
   967              leaves[i].stateCommitment = 0;
   968          }
   969  
   970          // Avoid stack too deep
   971          uint256 agreedLeafIdx = _lastCorrectLeafIdx;
   972          uint256 disputedLeafIdx = agreedLeafIdx + 1;
   973  
   974          // Fetch the merkle proofs for the pre/post state leaves in the proposal tree.
   975          bytes32 canonicalRoot = oracle.getTreeRootLPP(address(this), TEST_UUID);
   976          (bytes32 rootA, bytes32[] memory preProof) = _generateProof(agreedLeafIdx, leaves);
   977          assertEq(rootA, canonicalRoot);
   978          (bytes32 rootB, bytes32[] memory postProof) = _generateProof(disputedLeafIdx, leaves);
   979          assertEq(rootB, canonicalRoot);
   980  
   981          LibKeccak.StateMatrix memory preMatrix = _stateMatrixAtBlockIndex(data, disputedLeafIdx);
   982          oracle.challengeLPP({
   983              _claimant: address(this),
   984              _uuid: TEST_UUID,
   985              _stateMatrix: preMatrix,
   986              _preState: leaves[agreedLeafIdx],
   987              _preStateProof: preProof,
   988              _postState: leaves[disputedLeafIdx],
   989              _postStateProof: postProof
   990          });
   991  
   992          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
   993          assertTrue(metaData.countered());
   994      }
   995  
   996      /// @notice Tests that challenging the a divergence in a large preimage proposal at the first leaf always succeeds.
   997      function testFuzz_challengeFirst_succeeds(uint256 _numBlocks) public {
   998          _numBlocks = bound(_numBlocks, 1, 2 ** 8);
   999  
  1000          // Allocate the preimage data.
  1001          bytes memory data = new bytes(136 * _numBlocks);
  1002  
  1003          // Initialize the proposal.
  1004          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
  1005  
  1006          // Add the leaves to the tree with corrupted state commitments.
  1007          bytes32[] memory stateCommitments = new bytes32[](_numBlocks + 1);
  1008          for (uint256 i = 0; i < stateCommitments.length; i++) {
  1009              stateCommitments[i] = 0;
  1010          }
  1011          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
  1012  
  1013          // Construct the leaf preimage data for the blocks added and corrupt the state commitments.
  1014          LibKeccak.StateMatrix memory matrixB;
  1015          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrixB, data);
  1016          for (uint256 i = 0; i < leaves.length; i++) {
  1017              leaves[i].stateCommitment = 0;
  1018          }
  1019  
  1020          // Fetch the merkle proofs for the pre/post state leaves in the proposal tree.
  1021          bytes32 canonicalRoot = oracle.getTreeRootLPP(address(this), TEST_UUID);
  1022          (bytes32 rootA, bytes32[] memory postProof) = _generateProof(0, leaves);
  1023          assertEq(rootA, canonicalRoot);
  1024  
  1025          oracle.challengeFirstLPP({
  1026              _claimant: address(this),
  1027              _uuid: TEST_UUID,
  1028              _postState: leaves[0],
  1029              _postStateProof: postProof
  1030          });
  1031  
  1032          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
  1033          assertTrue(metaData.countered());
  1034      }
  1035  
  1036      /// @notice Tests that a valid leaf cannot be countered with the `challenge` function in the middle of the tree.
  1037      function test_challenge_validCommitment_reverts() public {
  1038          // Allocate the preimage data.
  1039          bytes memory data = new bytes(136);
  1040          for (uint256 i; i < data.length; i++) {
  1041              data[i] = 0xFF;
  1042          }
  1043  
  1044          // Initialize the proposal.
  1045          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
  1046  
  1047          // Add the leaves to the tree with mismatching state commitments.
  1048          LibKeccak.StateMatrix memory stateMatrix;
  1049          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
  1050          oracle.addLeavesLPP(TEST_UUID, 0, data, stateCommitments, true);
  1051  
  1052          // Construct the leaf preimage data for the blocks added.
  1053          LibKeccak.StateMatrix memory matrix;
  1054          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, data);
  1055  
  1056          // Create a proof array with 16 elements.
  1057          bytes32[] memory preProof = new bytes32[](16);
  1058          preProof[0] = _hashLeaf(leaves[1]);
  1059          bytes32[] memory postProof = new bytes32[](16);
  1060          postProof[0] = _hashLeaf(leaves[0]);
  1061          for (uint256 i = 1; i < preProof.length; i++) {
  1062              bytes32 zeroHash = oracle.zeroHashes(i);
  1063              preProof[i] = zeroHash;
  1064              postProof[i] = zeroHash;
  1065          }
  1066  
  1067          LibKeccak.StateMatrix memory preMatrix = _stateMatrixAtBlockIndex(data, 1);
  1068  
  1069          vm.expectRevert(PostStateMatches.selector);
  1070          oracle.challengeLPP({
  1071              _claimant: address(this),
  1072              _uuid: TEST_UUID,
  1073              _stateMatrix: preMatrix,
  1074              _preState: leaves[0],
  1075              _preStateProof: preProof,
  1076              _postState: leaves[1],
  1077              _postStateProof: postProof
  1078          });
  1079  
  1080          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
  1081          assertFalse(metaData.countered());
  1082      }
  1083  
  1084      /// @notice Tests that an invalid leaf can not be countered with non-contiguous states.
  1085      function test_challenge_statesNotContiguous_reverts() public {
  1086          // Allocate the preimage data.
  1087          bytes memory data = new bytes(136 * 2);
  1088          for (uint256 i; i < data.length; i++) {
  1089              data[i] = 0xFF;
  1090          }
  1091          bytes memory phonyData = new bytes(136 * 2);
  1092          for (uint256 i = 0; i < phonyData.length / 2; i++) {
  1093              data[i] = 0xFF;
  1094          }
  1095  
  1096          // Initialize the proposal.
  1097          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
  1098  
  1099          // Add the leaves to the tree with mismatching state commitments.
  1100          LibKeccak.StateMatrix memory stateMatrix;
  1101          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
  1102          oracle.addLeavesLPP(TEST_UUID, 0, phonyData, stateCommitments, true);
  1103  
  1104          // Construct the leaf preimage data for the blocks added.
  1105          LibKeccak.StateMatrix memory matrix;
  1106          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, phonyData);
  1107          leaves[0].stateCommitment = stateCommitments[0];
  1108          leaves[1].stateCommitment = stateCommitments[1];
  1109          leaves[2].stateCommitment = stateCommitments[2];
  1110  
  1111          // Create a proof array with 16 elements.
  1112          bytes32[] memory preProof = new bytes32[](16);
  1113          preProof[0] = _hashLeaf(leaves[1]);
  1114          preProof[1] = keccak256(abi.encode(_hashLeaf(leaves[2]), bytes32(0)));
  1115          bytes32[] memory postProof = new bytes32[](16);
  1116          postProof[0] = _hashLeaf(leaves[0]);
  1117          postProof[1] = keccak256(abi.encode(_hashLeaf(leaves[2]), bytes32(0)));
  1118          for (uint256 i = 2; i < preProof.length; i++) {
  1119              bytes32 zeroHash = oracle.zeroHashes(i);
  1120              preProof[i] = zeroHash;
  1121              postProof[i] = zeroHash;
  1122          }
  1123  
  1124          LibKeccak.StateMatrix memory preMatrix = _stateMatrixAtBlockIndex(data, 2);
  1125  
  1126          vm.expectRevert(StatesNotContiguous.selector);
  1127          oracle.challengeLPP({
  1128              _claimant: address(this),
  1129              _uuid: TEST_UUID,
  1130              _stateMatrix: preMatrix,
  1131              _preState: leaves[1],
  1132              _preStateProof: postProof,
  1133              _postState: leaves[0],
  1134              _postStateProof: preProof
  1135          });
  1136      }
  1137  
  1138      /// @notice Tests that an invalid leaf can not be countered with an incorrect prestate matrix reveal.
  1139      function test_challenge_invalidPreimage_reverts() public {
  1140          // Allocate the preimage data.
  1141          bytes memory data = new bytes(136 * 2);
  1142          for (uint256 i; i < data.length; i++) {
  1143              data[i] = 0xFF;
  1144          }
  1145          bytes memory phonyData = new bytes(136 * 2);
  1146          for (uint256 i = 0; i < phonyData.length / 2; i++) {
  1147              data[i] = 0xFF;
  1148          }
  1149  
  1150          // Initialize the proposal.
  1151          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
  1152  
  1153          // Add the leaves to the tree with mismatching state commitments.
  1154          LibKeccak.StateMatrix memory stateMatrix;
  1155          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
  1156          oracle.addLeavesLPP(TEST_UUID, 0, phonyData, stateCommitments, true);
  1157  
  1158          // Construct the leaf preimage data for the blocks added.
  1159          LibKeccak.StateMatrix memory matrix;
  1160          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, phonyData);
  1161          leaves[0].stateCommitment = stateCommitments[0];
  1162          leaves[1].stateCommitment = stateCommitments[1];
  1163          leaves[2].stateCommitment = stateCommitments[2];
  1164  
  1165          // Create a proof array with 16 elements.
  1166          bytes32[] memory preProof = new bytes32[](16);
  1167          preProof[0] = _hashLeaf(leaves[1]);
  1168          preProof[1] = keccak256(abi.encode(_hashLeaf(leaves[2]), bytes32(0)));
  1169          bytes32[] memory postProof = new bytes32[](16);
  1170          postProof[0] = _hashLeaf(leaves[0]);
  1171          postProof[1] = keccak256(abi.encode(_hashLeaf(leaves[2]), bytes32(0)));
  1172          for (uint256 i = 2; i < preProof.length; i++) {
  1173              bytes32 zeroHash = oracle.zeroHashes(i);
  1174              preProof[i] = zeroHash;
  1175              postProof[i] = zeroHash;
  1176          }
  1177  
  1178          LibKeccak.StateMatrix memory preMatrix = _stateMatrixAtBlockIndex(data, 2);
  1179  
  1180          vm.expectRevert(InvalidPreimage.selector);
  1181          oracle.challengeLPP({
  1182              _claimant: address(this),
  1183              _uuid: TEST_UUID,
  1184              _stateMatrix: preMatrix,
  1185              _preState: leaves[0],
  1186              _preStateProof: preProof,
  1187              _postState: leaves[1],
  1188              _postStateProof: postProof
  1189          });
  1190      }
  1191  
  1192      /// @notice Tests that an invalid leaf can be countered with the `challenge` function in the middle of the tree.
  1193      function test_challenge_invalidCommitment_succeeds() public {
  1194          // Allocate the preimage data.
  1195          bytes memory data = new bytes(136 * 2);
  1196          for (uint256 i; i < data.length; i++) {
  1197              data[i] = 0xFF;
  1198          }
  1199          bytes memory phonyData = new bytes(136 * 2);
  1200          for (uint256 i = 0; i < phonyData.length / 2; i++) {
  1201              data[i] = 0xFF;
  1202          }
  1203  
  1204          // Initialize the proposal.
  1205          oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(TEST_UUID, 0, uint32(data.length));
  1206  
  1207          // Add the leaves to the tree with mismatching state commitments.
  1208          LibKeccak.StateMatrix memory stateMatrix;
  1209          bytes32[] memory stateCommitments = _generateStateCommitments(stateMatrix, data);
  1210          oracle.addLeavesLPP(TEST_UUID, 0, phonyData, stateCommitments, true);
  1211  
  1212          // Construct the leaf preimage data for the blocks added.
  1213          LibKeccak.StateMatrix memory matrix;
  1214          PreimageOracle.Leaf[] memory leaves = _generateLeaves(matrix, phonyData);
  1215          leaves[0].stateCommitment = stateCommitments[0];
  1216          leaves[1].stateCommitment = stateCommitments[1];
  1217          leaves[2].stateCommitment = stateCommitments[2];
  1218  
  1219          // Create a proof array with 16 elements.
  1220          bytes32[] memory preProof = new bytes32[](16);
  1221          preProof[0] = _hashLeaf(leaves[1]);
  1222          preProof[1] = keccak256(abi.encode(_hashLeaf(leaves[2]), bytes32(0)));
  1223          bytes32[] memory postProof = new bytes32[](16);
  1224          postProof[0] = _hashLeaf(leaves[0]);
  1225          postProof[1] = keccak256(abi.encode(_hashLeaf(leaves[2]), bytes32(0)));
  1226          for (uint256 i = 2; i < preProof.length; i++) {
  1227              bytes32 zeroHash = oracle.zeroHashes(i);
  1228              preProof[i] = zeroHash;
  1229              postProof[i] = zeroHash;
  1230          }
  1231  
  1232          uint256 balanceBefore = address(this).balance;
  1233          LibKeccak.StateMatrix memory preMatrix = _stateMatrixAtBlockIndex(data, 1);
  1234          oracle.challengeLPP({
  1235              _claimant: address(this),
  1236              _uuid: TEST_UUID,
  1237              _stateMatrix: preMatrix,
  1238              _preState: leaves[0],
  1239              _preStateProof: preProof,
  1240              _postState: leaves[1],
  1241              _postStateProof: postProof
  1242          });
  1243          assertEq(address(this).balance, balanceBefore + oracle.MIN_BOND_SIZE());
  1244          assertEq(oracle.proposalBonds(address(this), TEST_UUID), 0);
  1245  
  1246          LPPMetaData metaData = oracle.proposalMetadata(address(this), TEST_UUID);
  1247          assertTrue(metaData.countered());
  1248      }
  1249  
  1250      /// @notice Hashes leaf data for the preimage proposals tree
  1251      function _hashLeaf(PreimageOracle.Leaf memory _leaf) internal pure returns (bytes32 leaf_) {
  1252          leaf_ = keccak256(abi.encodePacked(_leaf.input, _leaf.index, _leaf.stateCommitment));
  1253      }
  1254  
  1255      /// @notice Helper to construct the keccak merkle tree's leaves from a given input `_data`.
  1256      function _generateLeaves(
  1257          LibKeccak.StateMatrix memory _stateMatrix,
  1258          bytes memory _data
  1259      )
  1260          internal
  1261          pure
  1262          returns (PreimageOracle.Leaf[] memory leaves_)
  1263      {
  1264          bytes memory data = LibKeccak.padMemory(_data);
  1265          uint256 numCommitments = data.length / LibKeccak.BLOCK_SIZE_BYTES;
  1266  
  1267          leaves_ = new PreimageOracle.Leaf[](numCommitments);
  1268          for (uint256 i = 0; i < numCommitments; i++) {
  1269              bytes memory blockSlice = Bytes.slice(data, i * LibKeccak.BLOCK_SIZE_BYTES, LibKeccak.BLOCK_SIZE_BYTES);
  1270              LibKeccak.absorb(_stateMatrix, blockSlice);
  1271              LibKeccak.permutation(_stateMatrix);
  1272  
  1273              leaves_[i] = PreimageOracle.Leaf({
  1274                  input: blockSlice,
  1275                  index: uint32(i),
  1276                  stateCommitment: keccak256(abi.encode(_stateMatrix))
  1277              });
  1278          }
  1279      }
  1280  
  1281      /// @notice Helper to get the keccak state matrix before applying the block at `_blockIndex` within `_data`.
  1282      function _stateMatrixAtBlockIndex(
  1283          bytes memory _data,
  1284          uint256 _blockIndex
  1285      )
  1286          internal
  1287          pure
  1288          returns (LibKeccak.StateMatrix memory matrix_)
  1289      {
  1290          bytes memory data = LibKeccak.padMemory(_data);
  1291  
  1292          for (uint256 i = 0; i < _blockIndex; i++) {
  1293              bytes memory blockSlice = Bytes.slice(data, i * LibKeccak.BLOCK_SIZE_BYTES, LibKeccak.BLOCK_SIZE_BYTES);
  1294              LibKeccak.absorb(matrix_, blockSlice);
  1295              LibKeccak.permutation(matrix_);
  1296          }
  1297      }
  1298  
  1299      /// @notice Helper to construct the keccak state commitments for each block processed in the input `_data`.
  1300      function _generateStateCommitments(
  1301          LibKeccak.StateMatrix memory _stateMatrix,
  1302          bytes memory _data
  1303      )
  1304          internal
  1305          pure
  1306          returns (bytes32[] memory stateCommitments_)
  1307      {
  1308          bytes memory data = LibKeccak.padMemory(_data);
  1309          uint256 numCommitments = data.length / LibKeccak.BLOCK_SIZE_BYTES;
  1310  
  1311          stateCommitments_ = new bytes32[](numCommitments);
  1312          for (uint256 i = 0; i < numCommitments; i++) {
  1313              bytes memory blockSlice = Bytes.slice(data, i * LibKeccak.BLOCK_SIZE_BYTES, LibKeccak.BLOCK_SIZE_BYTES);
  1314              LibKeccak.absorb(_stateMatrix, blockSlice);
  1315              LibKeccak.permutation(_stateMatrix);
  1316  
  1317              stateCommitments_[i] = keccak256(abi.encode(_stateMatrix));
  1318          }
  1319      }
  1320  
  1321      /// @notice Calls out to the `go-ffi` tool to generate a merkle proof for the leaf at `_leafIdx` in a merkle tree
  1322      ///         constructed with `_leaves`.
  1323      function _generateProof(
  1324          uint256 _leafIdx,
  1325          PreimageOracle.Leaf[] memory _leaves
  1326      )
  1327          internal
  1328          returns (bytes32 root_, bytes32[] memory proof_)
  1329      {
  1330          bytes32[] memory leaves = new bytes32[](_leaves.length);
  1331          for (uint256 i = 0; i < _leaves.length; i++) {
  1332              leaves[i] = _hashLeaf(_leaves[i]);
  1333          }
  1334  
  1335          string[] memory commands = new string[](5);
  1336          commands[0] = "scripts/go-ffi/go-ffi";
  1337          commands[1] = "merkle";
  1338          commands[2] = "gen_proof";
  1339          commands[3] = vm.toString(abi.encodePacked(leaves));
  1340          commands[4] = vm.toString(_leafIdx);
  1341          (root_, proof_) = abi.decode(vm.ffi(commands), (bytes32, bytes32[]));
  1342      }
  1343  
  1344      fallback() external payable { }
  1345  
  1346      receive() external payable { }
  1347  }
  1348  
  1349  /// @notice Sets the status byte of a hash.
  1350  function _setStatusByte(bytes32 _hash, uint8 _status) pure returns (bytes32 out_) {
  1351      assembly {
  1352          out_ := or(and(not(shl(248, 0xFF)), _hash), shl(248, _status))
  1353      }
  1354  }
  1355  
  1356  /// @notice Computes a precompile key for a given precompile address and input.
  1357  function precompilePreimageKey(address _precompile, bytes memory _input) pure returns (bytes32 key_) {
  1358      bytes memory p = abi.encodePacked(_precompile, _input);
  1359      uint256 sz = 20 + _input.length;
  1360      assembly {
  1361          let h := keccak256(add(0x20, p), sz)
  1362          // Mask out prefix byte, replace with type 6 byte
  1363          key_ := or(and(h, not(shl(248, 0xFF))), shl(248, 6))
  1364      }
  1365  }