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 }