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 }