Blockhash.t.sol 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.24;
  3. import {Test} from "forge-std/Test.sol";
  4. import {Blockhash} from "../../contracts/utils/Blockhash.sol";
  5. contract BlockhashTest is Test {
  6. uint256 internal startingBlock;
  7. address internal constant SYSTEM_ADDRESS = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;
  8. // See https://eips.ethereum.org/EIPS/eip-2935#bytecode
  9. // Generated using https://www.evm.codes/playground
  10. bytes private constant HISTORY_STORAGE_BYTECODE =
  11. hex"3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500";
  12. function setUp() public {
  13. vm.roll(block.number + 100);
  14. startingBlock = block.number;
  15. vm.etch(Blockhash.HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_BYTECODE);
  16. }
  17. function testFuzzRecentBlocks(uint8 offset, uint64 currentBlock, bytes32 expectedHash) public {
  18. // Recent blocks (1-256 blocks old)
  19. uint256 boundedOffset = uint256(offset) + 1;
  20. vm.assume(currentBlock > boundedOffset);
  21. vm.roll(currentBlock);
  22. uint256 targetBlock = currentBlock - boundedOffset;
  23. vm.setBlockhash(targetBlock, expectedHash);
  24. bytes32 result = Blockhash.blockHash(targetBlock);
  25. assertEq(result, blockhash(targetBlock));
  26. assertEq(result, expectedHash);
  27. }
  28. function testFuzzHistoryBlocks(uint16 offset, uint256 currentBlock, bytes32 expectedHash) public {
  29. // History blocks (257-8191 blocks old)
  30. offset = uint16(bound(offset, 257, 8191));
  31. vm.assume(currentBlock > offset);
  32. vm.roll(currentBlock);
  33. uint256 targetBlock = currentBlock - offset;
  34. _setHistoryBlockhash(targetBlock, expectedHash);
  35. bytes32 result = Blockhash.blockHash(targetBlock);
  36. (bool success, bytes memory returndata) = Blockhash.HISTORY_STORAGE_ADDRESS.staticcall(
  37. abi.encodePacked(bytes32(targetBlock))
  38. );
  39. assertTrue(success);
  40. assertEq(result, abi.decode(returndata, (bytes32)));
  41. assertEq(result, expectedHash);
  42. }
  43. function testFuzzVeryOldBlocks(uint256 offset, uint256 currentBlock) public {
  44. // Very old blocks (>8191 blocks old)
  45. offset = bound(offset, 8192, type(uint256).max);
  46. vm.assume(currentBlock > offset);
  47. vm.roll(currentBlock);
  48. uint256 targetBlock = currentBlock - offset;
  49. bytes32 result = Blockhash.blockHash(targetBlock);
  50. assertEq(result, bytes32(0));
  51. }
  52. function testFuzzFutureBlocks(uint256 offset, uint256 currentBlock) public {
  53. // Future blocks
  54. offset = bound(offset, 1, type(uint256).max);
  55. currentBlock = bound(currentBlock, 0, type(uint256).max - offset);
  56. vm.roll(currentBlock);
  57. unchecked {
  58. uint256 targetBlock = currentBlock + offset;
  59. bytes32 result = Blockhash.blockHash(targetBlock);
  60. assertEq(result, blockhash(targetBlock));
  61. }
  62. }
  63. function testUnsupportedChainsReturnZeroWhenOutOfRange() public {
  64. vm.etch(Blockhash.HISTORY_STORAGE_ADDRESS, hex"");
  65. vm.roll(block.number + 1000);
  66. assertEq(Blockhash.blockHash(block.number - 1000), bytes32(0));
  67. }
  68. function _setHistoryBlockhash(bytes32 blockHash) internal {
  69. _setHistoryBlockhash(block.number, blockHash);
  70. }
  71. function _setHistoryBlockhash(uint256 blockNumber, bytes32 blockHash) internal {
  72. // Subtracting 1 due to bug encountered during coverage
  73. uint256 currentBlock = block.number - 1;
  74. vm.assume(blockNumber < type(uint256).max);
  75. vm.roll(blockNumber + 1); // roll to the next block so the storage contract sets the parent's blockhash
  76. vm.prank(SYSTEM_ADDRESS);
  77. (bool success, ) = Blockhash.HISTORY_STORAGE_ADDRESS.call(abi.encode(blockHash)); // set parent's blockhash
  78. assertTrue(success);
  79. vm.roll(currentBlock + 1);
  80. }
  81. }