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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  // Testing utilities
     5  import { Test } from "forge-std/Test.sol";
     6  
     7  // Target contract
     8  import { Bytes } from "src/libraries/Bytes.sol";
     9  
    10  contract Bytes_slice_Test is Test {
    11      /// @notice Tests that the `slice` function works as expected when starting from index 0.
    12      function test_slice_fromZeroIdx_works() public {
    13          bytes memory input = hex"11223344556677889900";
    14  
    15          // Exhaustively check if all possible slices starting from index 0 are correct.
    16          assertEq(Bytes.slice(input, 0, 0), hex"");
    17          assertEq(Bytes.slice(input, 0, 1), hex"11");
    18          assertEq(Bytes.slice(input, 0, 2), hex"1122");
    19          assertEq(Bytes.slice(input, 0, 3), hex"112233");
    20          assertEq(Bytes.slice(input, 0, 4), hex"11223344");
    21          assertEq(Bytes.slice(input, 0, 5), hex"1122334455");
    22          assertEq(Bytes.slice(input, 0, 6), hex"112233445566");
    23          assertEq(Bytes.slice(input, 0, 7), hex"11223344556677");
    24          assertEq(Bytes.slice(input, 0, 8), hex"1122334455667788");
    25          assertEq(Bytes.slice(input, 0, 9), hex"112233445566778899");
    26          assertEq(Bytes.slice(input, 0, 10), hex"11223344556677889900");
    27      }
    28  
    29      /// @notice Tests that the `slice` function works as expected when starting from indices [1, 9]
    30      ///         with lengths [1, 9], in reverse order.
    31      function test_slice_fromNonZeroIdx_works() public {
    32          bytes memory input = hex"11223344556677889900";
    33  
    34          // Exhaustively check correctness of slices starting from indexes [1, 9]
    35          // and spanning [1, 9] bytes, in reverse order
    36          assertEq(Bytes.slice(input, 9, 1), hex"00");
    37          assertEq(Bytes.slice(input, 8, 2), hex"9900");
    38          assertEq(Bytes.slice(input, 7, 3), hex"889900");
    39          assertEq(Bytes.slice(input, 6, 4), hex"77889900");
    40          assertEq(Bytes.slice(input, 5, 5), hex"6677889900");
    41          assertEq(Bytes.slice(input, 4, 6), hex"556677889900");
    42          assertEq(Bytes.slice(input, 3, 7), hex"44556677889900");
    43          assertEq(Bytes.slice(input, 2, 8), hex"3344556677889900");
    44          assertEq(Bytes.slice(input, 1, 9), hex"223344556677889900");
    45      }
    46  
    47      /// @notice Tests that the `slice` function works as expected when slicing between multiple words
    48      ///         in memory. In this case, we test that a 2 byte slice between the 32nd byte of the
    49      ///         first word and the 1st byte of the second word is correct.
    50      function test_slice_acrossWords_works() public {
    51          bytes memory input =
    52              hex"00000000000000000000000000000000000000000000000000000000000000112200000000000000000000000000000000000000000000000000000000000000";
    53  
    54          assertEq(Bytes.slice(input, 31, 2), hex"1122");
    55      }
    56  
    57      /// @notice Tests that the `slice` function works as expected when slicing between multiple
    58      ///         words in memory. In this case, we test that a 34 byte slice between 3 separate words
    59      ///        returns the correct result.
    60      function test_slice_acrossMultipleWords_works() public {
    61          bytes memory input =
    62              hex"000000000000000000000000000000000000000000000000000000000000001122FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1100000000000000000000000000000000000000000000000000000000000000";
    63          bytes memory expected = hex"1122FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11";
    64  
    65          assertEq(Bytes.slice(input, 31, 34), expected);
    66      }
    67  
    68      /// @notice Tests that the `slice` function correctly updates the free memory pointer depending
    69      ///         on the length of the slice.
    70      function testFuzz_slice_memorySafety_succeeds(bytes memory _input, uint256 _start, uint256 _length) public {
    71          // The start should never be more than the length of the input bytes array - 1
    72          vm.assume(_start < _input.length);
    73          // The length should never be more than the length of the input bytes array - the starting
    74          // slice index.
    75          vm.assume(_length <= _input.length - _start);
    76  
    77          // Grab the free memory pointer before the slice operation
    78          uint64 initPtr;
    79          assembly {
    80              initPtr := mload(0x40)
    81          }
    82          uint64 expectedPtr = uint64(initPtr + 0x20 + ((_length + 0x1f) & ~uint256(0x1f)));
    83  
    84          // Ensure that all memory outside of the expected range is safe.
    85          vm.expectSafeMemory(initPtr, expectedPtr);
    86  
    87          // Slice the input bytes array from `_start` to `_start + _length`
    88          bytes memory slice = Bytes.slice(_input, _start, _length);
    89  
    90          // Grab the free memory pointer after the slice operation
    91          uint64 finalPtr;
    92          assembly {
    93              finalPtr := mload(0x40)
    94          }
    95  
    96          // The free memory pointer should have been updated properly
    97          if (_length == 0) {
    98              // If the slice length is zero, only 32 bytes of memory should have been allocated.
    99              assertEq(finalPtr, initPtr + 0x20);
   100          } else {
   101              // If the slice length is greater than zero, the memory allocated should be the
   102              // length of the slice rounded up to the next 32 byte word + 32 bytes for the
   103              // length of the byte array.
   104              //
   105              // Note that we use a slightly less efficient, but equivalent method of rounding
   106              // up `_length` to the next multiple of 32 than is used in the `slice` function.
   107              // This is to diff test the method used in `slice`.
   108              uint64 _expectedPtr = uint64(initPtr + 0x20 + (((_length + 0x1F) >> 5) << 5));
   109              assertEq(finalPtr, _expectedPtr);
   110  
   111              // Sanity check for equivalence of the rounding methods.
   112              assertEq(_expectedPtr, expectedPtr);
   113          }
   114  
   115          // The slice length should be equal to `_length`
   116          assertEq(slice.length, _length);
   117      }
   118  }
   119  
   120  contract Bytes_slice_TestFail is Test {
   121      /// @notice Tests that, when given an input bytes array of length `n`, the `slice` function will
   122      ///         always revert if `_start + _length > n`.
   123      function testFuzz_slice_outOfBounds_reverts(bytes memory _input, uint256 _start, uint256 _length) public {
   124          // We want a valid start index and a length that will not overflow.
   125          vm.assume(_start < _input.length && _length < type(uint256).max - 31);
   126          // But, we want an invalid slice length.
   127          vm.assume(_start + _length > _input.length);
   128  
   129          vm.expectRevert("slice_outOfBounds");
   130          Bytes.slice(_input, _start, _length);
   131      }
   132  
   133      /// @notice Tests that, when given a length `n` that is greater than `type(uint256).max - 31`,
   134      ///         the `slice` function reverts.
   135      function testFuzz_slice_lengthOverflows_reverts(bytes memory _input, uint256 _start, uint256 _length) public {
   136          // Ensure that the `_length` will overflow if a number >= 31 is added to it.
   137          vm.assume(_length > type(uint256).max - 31);
   138  
   139          vm.expectRevert("slice_overflow");
   140          Bytes.slice(_input, _start, _length);
   141      }
   142  
   143      /// @notice Tests that, when given a start index `n` that is greater than
   144      ///         `type(uint256).max - n`, the `slice` function reverts.
   145      function testFuzz_slice_rangeOverflows_reverts(bytes memory _input, uint256 _start, uint256 _length) public {
   146          // Ensure that `_length` is a realistic length of a slice. This is to make sure
   147          // we revert on the correct require statement.
   148          vm.assume(_length < _input.length);
   149          // Ensure that `_start` will overflow if `_length` is added to it.
   150          vm.assume(_start > type(uint256).max - _length);
   151  
   152          vm.expectRevert("slice_overflow");
   153          Bytes.slice(_input, _start, _length);
   154      }
   155  }
   156  
   157  contract Bytes_toNibbles_Test is Test {
   158      /// @notice Tests that, given an input of 5 bytes, the `toNibbles` function returns an array of
   159      ///         10 nibbles corresponding to the input data.
   160      function test_toNibbles_expectedResult5Bytes_works() public {
   161          bytes memory input = hex"1234567890";
   162          bytes memory expected = hex"01020304050607080900";
   163          bytes memory actual = Bytes.toNibbles(input);
   164  
   165          assertEq(input.length * 2, actual.length);
   166          assertEq(expected.length, actual.length);
   167          assertEq(actual, expected);
   168      }
   169  
   170      /// @notice Tests that, given an input of 128 bytes, the `toNibbles` function returns an array
   171      ///         of 256 nibbles corresponding to the input data. This test exists to ensure that,
   172      ///         given a large input, the `toNibbles` function works as expected.
   173      function test_toNibbles_expectedResult128Bytes_works() public {
   174          bytes memory input =
   175              hex"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f";
   176          bytes memory expected =
   177              hex"0000000100020003000400050006000700080009000a000b000c000d000e000f0100010101020103010401050106010701080109010a010b010c010d010e010f0200020102020203020402050206020702080209020a020b020c020d020e020f0300030103020303030403050306030703080309030a030b030c030d030e030f0400040104020403040404050406040704080409040a040b040c040d040e040f0500050105020503050405050506050705080509050a050b050c050d050e050f0600060106020603060406050606060706080609060a060b060c060d060e060f0700070107020703070407050706070707080709070a070b070c070d070e070f";
   178          bytes memory actual = Bytes.toNibbles(input);
   179  
   180          assertEq(input.length * 2, actual.length);
   181          assertEq(expected.length, actual.length);
   182          assertEq(actual, expected);
   183      }
   184  
   185      /// @notice Tests that, given an input of 0 bytes, the `toNibbles` function returns a zero
   186      ///         length array.
   187      function test_toNibbles_zeroLengthInput_works() public {
   188          bytes memory input = hex"";
   189          bytes memory expected = hex"";
   190          bytes memory actual = Bytes.toNibbles(input);
   191  
   192          assertEq(input.length, 0);
   193          assertEq(expected.length, 0);
   194          assertEq(actual.length, 0);
   195          assertEq(actual, expected);
   196      }
   197  
   198      /// @notice Tests that the `toNibbles` function correctly updates the free memory pointer depending
   199      ///         on the length of the resulting array.
   200      function testFuzz_toNibbles_memorySafety_succeeds(bytes memory _input) public {
   201          // Grab the free memory pointer before the `toNibbles` operation
   202          uint64 initPtr;
   203          assembly {
   204              initPtr := mload(0x40)
   205          }
   206          uint64 expectedPtr = uint64(initPtr + 0x20 + ((_input.length * 2 + 0x1F) & ~uint256(0x1F)));
   207  
   208          // Ensure that all memory outside of the expected range is safe.
   209          vm.expectSafeMemory(initPtr, expectedPtr);
   210  
   211          // Pull out each individual nibble from the input bytes array
   212          bytes memory nibbles = Bytes.toNibbles(_input);
   213  
   214          // Grab the free memory pointer after the `toNibbles` operation
   215          uint64 finalPtr;
   216          assembly {
   217              finalPtr := mload(0x40)
   218          }
   219  
   220          // The free memory pointer should have been updated properly
   221          if (_input.length == 0) {
   222              // If the input length is zero, only 32 bytes of memory should have been allocated.
   223              assertEq(finalPtr, initPtr + 0x20);
   224          } else {
   225              // If the input length is greater than zero, the memory allocated should be the
   226              // length of the input * 2 + 32 bytes for the length field.
   227              //
   228              // Note that we use a slightly less efficient, but equivalent method of rounding
   229              // up `_length` to the next multiple of 32 than is used in the `toNibbles` function.
   230              // This is to diff test the method used in `toNibbles`.
   231              uint64 _expectedPtr = uint64(initPtr + 0x20 + (((_input.length * 2 + 0x1F) >> 5) << 5));
   232              assertEq(finalPtr, _expectedPtr);
   233  
   234              // Sanity check for equivalence of the rounding methods.
   235              assertEq(_expectedPtr, expectedPtr);
   236          }
   237  
   238          // The nibbles length should be equal to `_length * 2`
   239          assertEq(nibbles.length, _input.length << 1);
   240      }
   241  }
   242  
   243  contract Bytes_equal_Test is Test {
   244      /// @notice Manually checks equality of two dynamic `bytes` arrays in memory.
   245      /// @param _a The first `bytes` array to compare.
   246      /// @param _b The second `bytes` array to compare.
   247      /// @return True if the two `bytes` arrays are equal in memory.
   248      function manualEq(bytes memory _a, bytes memory _b) internal pure returns (bool) {
   249          bool _eq;
   250          assembly {
   251              _eq :=
   252                  and(
   253                      // Check if the contents of the two bytes arrays are equal in memory.
   254                      eq(keccak256(add(0x20, _a), mload(_a)), keccak256(add(0x20, _b), mload(_b))),
   255                      // Check if the length of the two bytes arrays are equal in memory.
   256                      // This is redundant given the above check, but included for completeness.
   257                      eq(mload(_a), mload(_b))
   258                  )
   259          }
   260          return _eq;
   261      }
   262  
   263      /// @notice Tests that the `equal` function in the `Bytes` library returns `false` if given two
   264      ///         non-equal byte arrays.
   265      function testFuzz_equal_notEqual_works(bytes memory _a, bytes memory _b) public {
   266          vm.assume(!manualEq(_a, _b));
   267          assertFalse(Bytes.equal(_a, _b));
   268      }
   269  
   270      /// @notice Test whether or not the `equal` function in the `Bytes` library is equivalent to
   271      ///         manually checking equality of the two dynamic `bytes` arrays in memory.
   272      function testDiff_equal_works(bytes memory _a, bytes memory _b) public {
   273          assertEq(Bytes.equal(_a, _b), manualEq(_a, _b));
   274      }
   275  }