Commit 73f374b7 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge pull request #4426 from ethereum-optimism/clabby/ctb/bytes-lib-tests

feat(ctb): Add tests for `Bytes` library
parents d30e727f 43b61405
---
'@eth-optimism/contracts-bedrock': patch
---
Add tests for the `Bytes` library
......@@ -9,6 +9,13 @@ GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 88513)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 74998)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 36156)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 167240)
Bytes_Test:test_slice_acrossMultipleWords_works() (gas: 9391)
Bytes_Test:test_slice_acrossWords_works() (gas: 1397)
Bytes_Test:test_slice_fromNonZeroIdx_works() (gas: 17218)
Bytes_Test:test_slice_fromZeroIdx_works() (gas: 20826)
Bytes_Test:test_toNibbles_expectedResult128Bytes_works() (gas: 129885)
Bytes_Test:test_toNibbles_expectedResult5Bytes_works() (gas: 6132)
Bytes_Test:test_toNibbles_zeroLengthInput_works() (gas: 966)
CrossDomainMessenger_BaseGas_Test:test_baseGas_succeeds() (gas: 20098)
CrossDomainOwnableThroughPortal_Test:test_depositTransaction_crossDomainOwner_succeeds() (gas: 61806)
CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10530)
......
pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { Bytes } from "../libraries/Bytes.sol";
/// @title BytesTest
contract Bytes_Test is Test {
/// @dev Tests that the `slice` function works as expected when starting from
/// index 0.
function test_slice_fromZeroIdx_works() public {
bytes memory input = hex"11223344556677889900";
// Exhaustively check if all possible slices starting from index 0 are correct.
assertEq(Bytes.slice(input, 0, 0), hex"");
assertEq(Bytes.slice(input, 0, 1), hex"11");
assertEq(Bytes.slice(input, 0, 2), hex"1122");
assertEq(Bytes.slice(input, 0, 3), hex"112233");
assertEq(Bytes.slice(input, 0, 4), hex"11223344");
assertEq(Bytes.slice(input, 0, 5), hex"1122334455");
assertEq(Bytes.slice(input, 0, 6), hex"112233445566");
assertEq(Bytes.slice(input, 0, 7), hex"11223344556677");
assertEq(Bytes.slice(input, 0, 8), hex"1122334455667788");
assertEq(Bytes.slice(input, 0, 9), hex"112233445566778899");
assertEq(Bytes.slice(input, 0, 10), hex"11223344556677889900");
}
/// @dev Tests that the `slice` function works as expected when starting from
/// indexes [1, 9] with lengths [1, 9], in reverse order.
function test_slice_fromNonZeroIdx_works() public {
bytes memory input = hex"11223344556677889900";
// Exhaustively check correctness of slices starting from indexes [1, 9]
// and spanning [1, 9] bytes, in reverse order
assertEq(Bytes.slice(input, 9, 1), hex"00");
assertEq(Bytes.slice(input, 8, 2), hex"9900");
assertEq(Bytes.slice(input, 7, 3), hex"889900");
assertEq(Bytes.slice(input, 6, 4), hex"77889900");
assertEq(Bytes.slice(input, 5, 5), hex"6677889900");
assertEq(Bytes.slice(input, 4, 6), hex"556677889900");
assertEq(Bytes.slice(input, 3, 7), hex"44556677889900");
assertEq(Bytes.slice(input, 2, 8), hex"3344556677889900");
assertEq(Bytes.slice(input, 1, 9), hex"223344556677889900");
}
/// @dev Tests that the `slice` function works as expected when slicing between
/// multiple words in memory. In this case, we test that a 2 byte slice between
/// the 32nd byte of the first word and the 1st byte of the second word is
/// correct.
function test_slice_acrossWords_works() public {
bytes
memory input = hex"00000000000000000000000000000000000000000000000000000000000000112200000000000000000000000000000000000000000000000000000000000000";
assertEq(Bytes.slice(input, 31, 2), hex"1122");
}
/// @dev Tests that the `slice` function works as expected when slicing between
/// multiple words in memory. In this case, we test that a 34 byte slice between
/// 3 separate words returns the correct result.
function test_slice_acrossMultipleWords_works() public {
bytes
memory input = hex"000000000000000000000000000000000000000000000000000000000000001122FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1100000000000000000000000000000000000000000000000000000000000000";
bytes
memory expected = hex"1122FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11";
assertEq(Bytes.slice(input, 31, 34), expected);
}
/// @dev Tests that, when given an input bytes array of length `n`,
/// the `slice` function will always revert if `_start + _length > n`.
function testFuzz_slice_outOfBounds_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// We want a valid start index and a length that will not overflow.
vm.assume(_start < _input.length && _length < type(uint256).max - 31);
// But, we want an invalid slice length.
vm.assume(_start + _length > _input.length);
vm.expectRevert("slice_outOfBounds");
Bytes.slice(_input, _start, _length);
}
/// @dev Tests that, when given a length `n` that is greater than `type(uint256).max - 31`,
/// the `slice` function reverts.
function testFuzz_slice_lengthOverflows_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// Ensure that the `_length` will overflow if a number >= 31 is added to it.
vm.assume(_length > type(uint256).max - 31);
vm.expectRevert("slice_overflow");
Bytes.slice(_input, _start, _length);
}
/// @dev Tests that, when given a length `n` that is greater than `type(uint256).max - 31`,
/// the `slice` function reverts.
function testFuzz_slice_rangeOverflows_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// Ensure that `_length` is a realistic length of a slice. This is to make sure
// we revert on the correct require statement.
vm.assume(_length < _input.length);
// Ensure that `_start` will overflow if `_length` is added to it.
vm.assume(_start > type(uint256).max - _length);
vm.expectRevert("slice_overflow");
Bytes.slice(_input, _start, _length);
}
/// @dev Tests that, given an input of 5 bytes, the `toNibbles` function returns
/// an array of 10 nibbles corresponding to the input data.
function test_toNibbles_expectedResult5Bytes_works() public {
bytes memory input = hex"1234567890";
bytes memory expected = hex"01020304050607080900";
bytes memory actual = Bytes.toNibbles(input);
assertEq(input.length * 2, actual.length);
assertEq(expected.length, actual.length);
assertEq(actual, expected);
}
/// @dev Tests that, given an input of 128 bytes, the `toNibbles` function returns
/// an array of 256 nibbles corresponding to the input data.
/// This test exists to ensure that, given a large input, the `toNibbles` function
/// works as expected.
function test_toNibbles_expectedResult128Bytes_works() public {
bytes
memory input = hex"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f";
bytes
memory expected = hex"0000000100020003000400050006000700080009000a000b000c000d000e000f0100010101020103010401050106010701080109010a010b010c010d010e010f0200020102020203020402050206020702080209020a020b020c020d020e020f0300030103020303030403050306030703080309030a030b030c030d030e030f0400040104020403040404050406040704080409040a040b040c040d040e040f0500050105020503050405050506050705080509050a050b050c050d050e050f0600060106020603060406050606060706080609060a060b060c060d060e060f0700070107020703070407050706070707080709070a070b070c070d070e070f";
bytes memory actual = Bytes.toNibbles(input);
assertEq(input.length * 2, actual.length);
assertEq(expected.length, actual.length);
assertEq(actual, expected);
}
/// @dev Tests that, given an input of 0 bytes, the `toNibbles` function returns
/// a zero length array.
function test_toNibbles_zeroLengthInput_works() public {
bytes memory input = hex"";
bytes memory expected = hex"";
bytes memory actual = Bytes.toNibbles(input);
assertEq(input.length, 0);
assertEq(expected.length, 0);
assertEq(actual.length, 0);
assertEq(actual, expected);
}
/// @dev Test that the `toNibbles` function in the `Bytes` library is equivalent to the
/// Yul implementation.
function testDiff_toNibbles_succeeds(bytes memory _input) public {
assertEq(Bytes.toNibbles(_input), toNibblesYul(_input));
}
/// @dev Test that the `equal` function in the `Bytes` library returns `false` if given
/// two non-equal byte arrays.
function testFuzz_equal_notEqual_works(bytes memory _a, bytes memory _b) public {
vm.assume(!manualEq(_a, _b));
assertFalse(Bytes.equal(_a, _b));
}
/// @dev Test whether or not the `equal` function in the `Bytes` library is equivalent
/// to manually checking equality of the two dynamic `bytes` arrays in memory.
function testDiff_equal_works(bytes memory _a, bytes memory _b) public {
assertEq(Bytes.equal(_a, _b), manualEq(_a, _b));
}
////////////////////////////////////////////////////////////////
// HELPERS //
////////////////////////////////////////////////////////////////
/// @dev Utility function to manually check equality of two dynamic `bytes` arrays in memory.
function manualEq(bytes memory _a, bytes memory _b) internal pure returns (bool) {
bool _eq;
assembly {
_eq := and(
// Check if the contents of the two bytes arrays are equal in memory.
eq(keccak256(add(0x20, _a), mload(_a)), keccak256(add(0x20, _b), mload(_b))),
// Check if the length of the two bytes arrays are equal in memory.
// This is redundant given the above check, but included for completeness.
eq(mload(_a), mload(_b))
)
}
return _eq;
}
/// @dev Utility function to diff test Solidity version of `toNibbles`
function toNibblesYul(bytes memory _bytes) internal pure returns (bytes memory) {
// Allocate memory for the `nibbles` array.
bytes memory nibbles = new bytes(_bytes.length << 1);
assembly {
// Load the length of the passed bytes array from memory
let bytesLength := mload(_bytes)
// Store the memory offset of the _bytes array's contents on the stack
let bytesStart := add(_bytes, 0x20)
// Store the memory offset of the nibbles array's contents on the stack
let nibblesStart := add(nibbles, 0x20)
// Loop through each byte in the input array
for {
let i := 0x00
} lt(i, bytesLength) {
i := add(i, 0x01)
} {
// Get the starting offset of the next 2 bytes in the nibbles array
let offset := add(nibblesStart, shl(0x01, i))
// Load the byte at the current index within the `_bytes` array
let b := byte(0x00, mload(add(bytesStart, i)))
// Pull out the first nibble and store it in the new array
mstore8(offset, shr(0x04, b))
// Pull out the second nibble and store it in the new array
mstore8(add(offset, 0x01), and(b, 0x0F))
}
}
return nibbles;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment