diff --git a/.gas-snapshot b/.gas-snapshot index d59a11034f..5ea0e2f1a2 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -732,12 +732,21 @@ LibPRNGTest:testPRNGShuffleGas() (gas: 1610927) LibPRNGTest:testPRNGUniform() (gas: 559430) LibPRNGTest:testStandardNormalWad() (gas: 4425913) LibPRNGTest:test__codesize() (gas: 14289) -LibRLPTest:testComputeAddressDifferential(address,uint256) (runs: 298, μ: 1944, ~: 1808) -LibRLPTest:testComputeAddressForLargeNonces() (gas: 1727) -LibRLPTest:testComputeAddressForSmallNonces() (gas: 989) -LibRLPTest:testComputeAddressOriginalForLargeNonces() (gas: 1909) -LibRLPTest:testComputeAddressOriginalForSmallNonces() (gas: 1454) -LibRLPTest:test__codesize() (gas: 7397) +LibRLPTest:testComputeAddressDifferential(address,uint256) (runs: 298, μ: 2389, ~: 2179) +LibRLPTest:testComputeAddressForLargeNonces() (gas: 1737) +LibRLPTest:testComputeAddressForSmallNonces() (gas: 1007) +LibRLPTest:testComputeAddressOriginalForLargeNonces() (gas: 3743) +LibRLPTest:testComputeAddressOriginalForSmallNonces() (gas: 2218) +LibRLPTest:testRLPEncodeBytes() (gas: 383735) +LibRLPTest:testRLPEncodeBytesDifferential(bytes) (runs: 298, μ: 2570, ~: 2320) +LibRLPTest:testRLPEncodeList() (gas: 5230) +LibRLPTest:testRLPEncodeList2() (gas: 15468) +LibRLPTest:testRLPEncodeUint() (gas: 146294) +LibRLPTest:testRLPEncodeUint(uint256) (runs: 298, μ: 2187, ~: 2046) +LibRLPTest:testRLPEncodeUintDifferential(uint256) (runs: 298, μ: 2463, ~: 2415) +LibRLPTest:testRLPPUint256() (gas: 250190) +LibRLPTest:testSmallLog256Equivalence(uint256) (runs: 298, μ: 856, ~: 934) +LibRLPTest:test__codesize() (gas: 11475) LibSortTest:testCopy(uint256) (runs: 298, μ: 138799, ~: 26968) LibSortTest:testInsertionSortAddressesDifferential(uint256) (runs: 298, μ: 73417, ~: 25190) LibSortTest:testInsertionSortInts() (gas: 112151) diff --git a/README.md b/README.md index 3f824e8fa1..c2eb03d94b 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ utils ├─ Base64 — "Library for Base64 encoding and decoding" ├─ SSTORE2 — "Library for cheaper reads and writes to persistent storage" ├─ CREATE3 — "Deploy to deterministic addresses without an initcode factor" -├─ LibRLP — "Library for computing contract addresses from their deployer and nonce" +├─ LibRLP — "Library for RLP encoding and CREATE address computation" ├─ LibBit — "Library for bit twiddling and boolean operations" ├─ LibZip — "Library for compressing and decompressing bytes" ├─ Clone — "Class with helper read functions for clone with immutable args" diff --git a/src/utils/LibRLP.sol b/src/utils/LibRLP.sol index 4f489c890e..b453bf33ea 100644 --- a/src/utils/LibRLP.sol +++ b/src/utils/LibRLP.sol @@ -1,10 +1,24 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; -/// @notice Library for computing contract addresses from their deployer and nonce. +/// @notice Library for RLP encoding and CREATE address computation. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibRLP.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibRLP.sol) library LibRLP { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* STRUCTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev A pointer to a RLP item list. + struct List { + // Do NOT modify the `_data` directly. + uint256 _data; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CREATE ADDRESS PREDICTION */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + /// @dev Returns the address where a contract will be stored if deployed via /// `deployer` with `nonce` using the `CREATE` opcode. /// For the specification of the Recursive Length Prefix (RLP) @@ -68,4 +82,220 @@ library LibRLP { } } } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* RLP ENCODING OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns a new empty list. + function l() internal pure returns (List memory result) {} + + /// @dev Returns a new list with `x` as the only element. + function l(uint256 x) internal pure returns (List memory result) { + p(result, x); + } + + /// @dev Returns a new list with `x` as the only element. + function l(bytes memory x) internal pure returns (List memory result) { + p(result, x); + } + + /// @dev Returns a new list with `x` as the only element. + function l(List memory x) internal pure returns (List memory result) { + p(result, x); + } + + /// @dev Appends `x` to `list`. + function p(List memory list, uint256 x) internal pure returns (List memory result) { + /// @solidity memory-safe-assembly + assembly { + mstore(result, shl(48, x)) + let v := or(shr(mload(list), result), mload(list)) + let tail := shr(40, v) + mstore(list, xor(shl(40, xor(tail, result)), v)) // Update the tail. + mstore(tail, or(mload(tail), result)) // Make the previous tail point to `m`. + if shr(208, x) { + let m := mload(0x40) + mstore(m, x) + mstore(0x40, add(m, 0x20)) + mstore(result, shl(40, or(1, shl(8, m)))) + } + result := list + } + } + + /// @dev Appends `x` to `list`. + function p(List memory list, bytes memory x) internal pure returns (List memory result) { + /// @solidity memory-safe-assembly + assembly { + mstore(result, shl(40, or(2, shl(8, x)))) + let v := or(shr(mload(list), result), mload(list)) + let tail := shr(40, v) + mstore(list, xor(shl(40, xor(tail, result)), v)) // Update the tail. + mstore(tail, or(mload(tail), result)) // Make the previous tail point to `m`. + result := list + } + } + + /// @dev Appends `x` to `list`. + function p(List memory list, List memory x) internal pure returns (List memory result) { + /// @solidity memory-safe-assembly + assembly { + mstore(result, shl(40, or(3, shl(8, x)))) + let v := or(shr(mload(list), result), mload(list)) + let tail := shr(40, v) + mstore(list, xor(shl(40, xor(tail, result)), v)) // Update the tail. + mstore(tail, or(mload(tail), result)) // Make the previous tail point to `m`. + result := list + } + } + + /// @dev Returns the RLP encoding of `list`. + function encode(List memory list) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + function encodeUint(x_, o_) -> _o { + _o := add(o_, 1) + if iszero(gt(x_, 0x7f)) { + mstore8(o_, or(shl(7, iszero(x_)), x_)) // Copy `x_`. + leave + } + let r_ := shl(7, lt(0xffffffffffffffffffffffffffffffff, x_)) + r_ := or(r_, shl(6, lt(0xffffffffffffffff, shr(r_, x_)))) + r_ := or(r_, shl(5, lt(0xffffffff, shr(r_, x_)))) + r_ := or(r_, shl(4, lt(0xffff, shr(r_, x_)))) + r_ := or(shr(3, r_), lt(0xff, shr(r_, x_))) + mstore8(o_, add(r_, 0x81)) // Store the prefix. + mstore(0x00, x_) + mstore(_o, mload(xor(31, r_))) // Copy `x_`. + _o := add(add(1, r_), _o) + } + function encodeBytes(x_, o_, c_) -> _o { + _o := add(o_, 1) + let f_ := mload(add(0x20, x_)) + let n_ := mload(x_) + if iszero(gt(n_, 55)) { + if iszero(and(eq(1, n_), lt(byte(0, f_), 0x80))) { + mstore8(o_, add(n_, c_)) // Store the prefix. + mstore(add(0x21, o_), mload(add(0x40, x_))) + mstore(_o, f_) + _o := add(n_, _o) + leave + } + mstore(o_, f_) // Copy `x_`. + leave + } + returndatacopy(returndatasize(), returndatasize(), shr(32, n_)) + let r_ := add(1, add(lt(0xff, n_), add(lt(0xffff, n_), lt(0xffffff, n_)))) + mstore(o_, shl(248, add(r_, add(c_, 55)))) // Store the prefix. + // Copy `x`. + let i_ := add(r_, _o) + _o := add(i_, n_) + for { let d_ := sub(add(0x20, x_), i_) } 1 {} { + mstore(i_, mload(add(d_, i_))) + i_ := add(i_, 0x20) + if iszero(lt(i_, _o)) { break } + } + mstore(o_, or(mload(o_), shl(sub(248, shl(3, r_)), n_))) // Store the prefix. + } + function encodeList(l_, o_) -> _o { + if iszero(mload(l_)) { + mstore8(o_, 0xc0) + _o := add(o_, 1) + leave + } + let j_ := add(o_, 0x20) + for { let h_ := l_ } 1 {} { + h_ := and(mload(h_), 0xffffffffff) + if iszero(h_) { break } + let t_ := byte(26, mload(h_)) + if iszero(gt(t_, 1)) { + if iszero(t_) { + j_ := encodeUint(shr(48, mload(h_)), j_) + continue + } + j_ := encodeUint(mload(shr(48, mload(h_))), j_) + continue + } + if eq(t_, 2) { + j_ := encodeBytes(shr(48, mload(h_)), j_, 0x80) + continue + } + j_ := encodeList(shr(48, mload(h_)), j_) + } + mstore(o_, sub(j_, add(o_, 0x20))) + _o := encodeBytes(o_, o_, 0xc0) + } + result := mload(0x40) + let begin := add(result, 0x20) + let end := encodeList(list, begin) + mstore(result, sub(end, begin)) + mstore(end, 0) + mstore(0x40, add(end, 0x20)) + } + } + + /// @dev Returns the RLP encoding of `x`. + function encode(uint256 x) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + for {} 1 {} { + result := mload(0x40) + if iszero(gt(x, 0x7f)) { + mstore(result, 1) // Store the length of `result`. + mstore(add(result, 0x20), shl(248, or(shl(7, iszero(x)), x))) // Copy `x`. + mstore(0x40, add(result, 0x40)) // Allocate memory for `result`. + break + } + let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) + r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) + r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) + r := or(r, shl(4, lt(0xffff, shr(r, x)))) + r := add(1, or(shr(3, r), lt(0xff, shr(r, x)))) + mstore(add(result, 0x40), 0) // Zeroize the slot after `result`. + mstore(add(r, add(result, 1)), x) // Copy `x`. + mstore(add(result, 1), add(r, 0x80)) // Store the prefix. + mstore(result, add(1, r)) // Store the length of `result`. + mstore(0x40, add(result, 0x60)) // Allocate memory for `result`. + break + } + } + } + + /// @dev Returns the RLP encoding of `x`. + function encode(bytes memory x) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + result := x + for {} iszero(and(eq(1, mload(x)), lt(byte(0, mload(add(x, 0x20))), 0x80))) {} { + result := mload(0x40) + let n := mload(x) + if iszero(gt(n, 55)) { + mstore(0x40, add(result, 0x60)) + mstore(add(0x41, result), mload(add(0x40, x))) + mstore(add(0x21, result), mload(add(0x20, x))) + mstore(add(1, result), add(n, 0x80)) // Store the prefix. + mstore(result, add(1, n)) // Store the length of `result`. + mstore(add(add(result, 0x21), n), 0) // Zeroize the slot after `result`. + break + } + returndatacopy(returndatasize(), returndatasize(), shr(32, n)) + let r := add(1, add(lt(0xff, n), add(lt(0xffff, n), lt(0xffffff, n)))) + // Copy `x`. + let i := add(r, add(0x21, result)) + let end := add(i, n) + for { let d := sub(add(0x20, x), i) } 1 {} { + mstore(i, mload(add(d, i))) + i := add(i, 0x20) + if iszero(lt(i, end)) { break } + } + mstore(add(r, add(1, result)), n) // Store the prefix. + mstore(add(1, result), add(r, 0xb7)) // Store the prefix. + mstore(result, add(r, add(1, n))) // Store the length of `result`. + mstore(end, 0) // Zeroize the slot after `result`. + mstore(0x40, add(end, 0x20)) // Allocate memory. + break + } + } + } } diff --git a/test/LibRLP.t.sol b/test/LibRLP.t.sol index f01551f7c5..e8191675b9 100644 --- a/test/LibRLP.t.sol +++ b/test/LibRLP.t.sol @@ -3,8 +3,11 @@ pragma solidity ^0.8.4; import "./utils/SoladyTest.sol"; import {LibRLP} from "../src/utils/LibRLP.sol"; +import {FixedPointMathLib} from "../src/utils/FixedPointMathLib.sol"; contract LibRLPTest is SoladyTest { + using LibRLP for LibRLP.List; + function testComputeAddressDifferential(address deployer, uint256 nonce) public { assertEq(LibRLP.computeAddress(deployer, nonce), computeAddressOriginal(deployer, nonce)); } @@ -41,343 +44,303 @@ contract LibRLPTest is SoladyTest { internal pure returns (address) + { + return address(uint160(uint256(keccak256(_computeAddressOriginal(deployer, nonce))))); + } + + function _computeAddressOriginal(address deployer, uint256 nonce) + internal + pure + returns (bytes memory) { // Although the theoretical allowed limit, based on EIP-2681, // for an account nonce is 2**64-2: https://eips.ethereum.org/EIPS/eip-2681, // we just test all the way to 2**256-1 to ensure that the computeAddress function does not revert // for whatever nonce we provide. - // forgefmt: disable-next-item + if (nonce == 0x00) { - return address( - uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80))))) - ); + return abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80)); } - // forgefmt: disable-next-item if (nonce <= 0x7f) { - return address( - uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce))))) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint8).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint16).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint24).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint32).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint40).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdb), bytes1(0x94), deployer, bytes1(0x85), uint40(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint48).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdc), bytes1(0x94), deployer, bytes1(0x86), uint48(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint56).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdd), bytes1(0x94), deployer, bytes1(0x87), uint56(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint64).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xde), bytes1(0x94), deployer, bytes1(0x88), uint64(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint72).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdf), bytes1(0x94), deployer, bytes1(0x89), uint72(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint80).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe0), bytes1(0x94), deployer, bytes1(0x8a), uint80(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint88).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe1), bytes1(0x94), deployer, bytes1(0x8b), uint88(nonce))) - ) - ) - ); + return abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce)); } - // forgefmt: disable-next-item - if (nonce <= type(uint96).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe2), bytes1(0x94), deployer, bytes1(0x8c), uint96(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint104).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe3), bytes1(0x94), deployer, bytes1(0x8d), uint104(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint112).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe4), bytes1(0x94), deployer, bytes1(0x8e), uint112(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint120).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe5), bytes1(0x94), deployer, bytes1(0x8f), uint120(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint128).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe6), bytes1(0x94), deployer, bytes1(0x90), uint128(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint136).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe7), bytes1(0x94), deployer, bytes1(0x91), uint136(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint144).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe8), bytes1(0x94), deployer, bytes1(0x92), uint144(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint152).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe9), bytes1(0x94), deployer, bytes1(0x93), uint152(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint160).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xea), bytes1(0x94), deployer, bytes1(0x94), uint160(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint168).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xeb), bytes1(0x94), deployer, bytes1(0x95), uint168(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint176).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xec), bytes1(0x94), deployer, bytes1(0x96), uint176(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint184).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xed), bytes1(0x94), deployer, bytes1(0x97), uint184(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint192).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xee), bytes1(0x94), deployer, bytes1(0x98), uint192(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint200).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xef), bytes1(0x94), deployer, bytes1(0x99), uint200(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint208).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf0), bytes1(0x94), deployer, bytes1(0x9a), uint208(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint216).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf1), bytes1(0x94), deployer, bytes1(0x9b), uint216(nonce))) - ) - ) - ); + bytes memory ep = _ep(nonce); + uint256 n = ep.length; + return abi.encodePacked(uint8(0xd6 + n), uint8(0x94), deployer, uint8(0x80 + n), ep); + } + + function _ep(uint256 x) internal pure returns (bytes memory) { + if (x <= type(uint8).max) return abi.encodePacked(uint8(x)); + if (x <= type(uint16).max) return abi.encodePacked(uint16(x)); + if (x <= type(uint24).max) return abi.encodePacked(uint24(x)); + if (x <= type(uint32).max) return abi.encodePacked(uint32(x)); + if (x <= type(uint40).max) return abi.encodePacked(uint40(x)); + if (x <= type(uint48).max) return abi.encodePacked(uint48(x)); + if (x <= type(uint56).max) return abi.encodePacked(uint56(x)); + if (x <= type(uint64).max) return abi.encodePacked(uint64(x)); + if (x <= type(uint72).max) return abi.encodePacked(uint72(x)); + if (x <= type(uint80).max) return abi.encodePacked(uint80(x)); + if (x <= type(uint88).max) return abi.encodePacked(uint88(x)); + if (x <= type(uint96).max) return abi.encodePacked(uint96(x)); + if (x <= type(uint104).max) return abi.encodePacked(uint104(x)); + if (x <= type(uint112).max) return abi.encodePacked(uint112(x)); + if (x <= type(uint120).max) return abi.encodePacked(uint120(x)); + if (x <= type(uint128).max) return abi.encodePacked(uint128(x)); + if (x <= type(uint136).max) return abi.encodePacked(uint136(x)); + if (x <= type(uint144).max) return abi.encodePacked(uint144(x)); + if (x <= type(uint152).max) return abi.encodePacked(uint152(x)); + if (x <= type(uint160).max) return abi.encodePacked(uint160(x)); + if (x <= type(uint168).max) return abi.encodePacked(uint168(x)); + if (x <= type(uint176).max) return abi.encodePacked(uint176(x)); + if (x <= type(uint184).max) return abi.encodePacked(uint184(x)); + if (x <= type(uint192).max) return abi.encodePacked(uint192(x)); + if (x <= type(uint200).max) return abi.encodePacked(uint200(x)); + if (x <= type(uint208).max) return abi.encodePacked(uint208(x)); + if (x <= type(uint216).max) return abi.encodePacked(uint216(x)); + if (x <= type(uint224).max) return abi.encodePacked(uint224(x)); + if (x <= type(uint232).max) return abi.encodePacked(uint232(x)); + if (x <= type(uint240).max) return abi.encodePacked(uint240(x)); + if (x <= type(uint248).max) return abi.encodePacked(uint248(x)); + return abi.encodePacked(uint256(x)); + } + + function testRLPPUint256() public { + _testRLPPUint256(0); + _testRLPPUint256(1); + _testRLPPUint256(1 << 255); + } + + function _testRLPPUint256(uint256 x) internal { + LibRLP.List memory l; + unchecked { + for (uint256 i; i != 32; ++i) { + uint256 y = x ^ i; + l.p(y); + _checkMemory(); + assertEq(_getUint256(l, i), y); + } + for (uint256 i; i != 32; ++i) { + uint256 y = x ^ i; + assertEq(_getUint256(l, i), y); + } } - // forgefmt: disable-next-item - if (nonce <= type(uint224).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf2), bytes1(0x94), deployer, bytes1(0x9c), uint224(nonce))) - ) - ) - ); + } + + function _getUint256(LibRLP.List memory l, uint256 i) internal pure returns (uint256 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, 0) + let head := and(mload(l), 0xffffffffff) + if head { + for { let j := 0 } iszero(eq(j, i)) { j := add(j, 1) } { + head := and(mload(head), 0xffffffffff) + } + result := shr(48, mload(head)) + if eq(1, byte(26, mload(head))) { result := mload(result) } + } } - // forgefmt: disable-next-item - if (nonce <= type(uint232).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf3), bytes1(0x94), deployer, bytes1(0x9d), uint232(nonce))) - ) - ) - ); + } + + function testRLPEncodeBytes() public { + bytes memory s; + testRLPEncodeBytesDifferential(s); + assertEq(LibRLP.encode(""), hex"80"); + s = "dog"; + assertEq(LibRLP.encode(s), abi.encodePacked(hex"83", s)); + assertEq(LibRLP.encode(hex"00"), hex"00"); + assertEq(LibRLP.encode(hex"0f"), hex"0f"); + assertEq(LibRLP.encode(hex"0400"), hex"820400"); + s = "Lorem ipsum dolor sit amet, consectetur adipisicing eli"; + assertEq(LibRLP.encode(s), abi.encodePacked(hex"b7", s)); + s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; + assertEq(LibRLP.encode(s), abi.encodePacked(hex"b838", s)); + s = new bytes(0x100); + assertEq(LibRLP.encode(s), abi.encodePacked(hex"b90100", s)); + s = new bytes(0xfffe); + assertEq(LibRLP.encode(s), abi.encodePacked(hex"b9fffe", s)); + } + + function testRLPEncodeBytesDifferential(bytes memory x) public { + bytes memory computed = LibRLP.encode(x); + _checkMemory(); + assertEq(computed, _encode(x)); + assertEq(computed, _encodeSimple(x)); + _checkMemory(); + } + + function testRLPEncodeUintDifferential(uint256 x) public { + bytes memory computed = LibRLP.encode(x); + _checkMemory(); + assertEq(computed, _encode(x)); + assertEq(computed, _encodeSimple(x)); + _checkMemory(); + } + + function _encode(uint256 x) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + function encodeUint(x_, o_) -> _o { + _o := add(o_, 1) + if iszero(gt(x_, 0x7f)) { + mstore8(o_, or(shl(7, iszero(x_)), x_)) // Copy `x_`. + leave + } + let r_ := shl(7, lt(0xffffffffffffffffffffffffffffffff, x_)) + r_ := or(r_, shl(6, lt(0xffffffffffffffff, shr(r_, x_)))) + r_ := or(r_, shl(5, lt(0xffffffff, shr(r_, x_)))) + r_ := or(r_, shl(4, lt(0xffff, shr(r_, x_)))) + r_ := or(shr(3, r_), lt(0xff, shr(r_, x_))) + mstore8(o_, add(r_, 0x81)) // Store the prefix. + mstore(0x00, x_) + mstore(_o, mload(xor(31, r_))) // Copy `x_`. + _o := add(add(1, r_), _o) + } + result := mload(0x40) + let o := encodeUint(x, add(result, 0x20)) + mstore(result, sub(o, add(result, 0x20))) + mstore(o, 0) + mstore(0x40, add(o, 0x20)) } - // forgefmt: disable-next-item - if (nonce <= type(uint240).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf4), bytes1(0x94), deployer, bytes1(0x9e), uint240(nonce))) - ) - ) - ); + } + + function _encode(bytes memory x) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + function encodeBytes(x_, o_, c_) -> _o { + _o := add(o_, 1) + let f_ := mload(add(0x20, x_)) + let n_ := mload(x_) + if iszero(gt(n_, 55)) { + if iszero(and(eq(1, n_), lt(byte(0, f_), 0x80))) { + mstore8(o_, add(n_, c_)) // Store the prefix. + mstore(add(0x21, o_), mload(add(0x40, x_))) + mstore(_o, f_) + _o := add(n_, _o) + leave + } + mstore(o_, f_) // Copy `x_`. + leave + } + returndatacopy(returndatasize(), returndatasize(), shr(32, n_)) + let r_ := add(1, add(lt(0xff, n_), add(lt(0xffff, n_), lt(0xffffff, n_)))) + mstore(o_, shl(248, add(r_, add(c_, 55)))) // Store the prefix. + // Copy `x`. + let i_ := add(r_, _o) + _o := add(i_, n_) + for { let d_ := sub(add(0x20, x_), i_) } 1 {} { + mstore(i_, mload(add(d_, i_))) + i_ := add(i_, 0x20) + if iszero(lt(i_, _o)) { break } + } + mstore(o_, or(mload(o_), shl(sub(248, shl(3, r_)), n_))) // Store the prefix. + } + result := mload(0x40) + let o := encodeBytes(x, add(result, 0x20), 0x80) + mstore(result, sub(o, add(result, 0x20))) + mstore(o, 0) + mstore(0x40, add(o, 0x20)) } - // forgefmt: disable-next-item - if (nonce <= type(uint248).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf5), bytes1(0x94), deployer, bytes1(0x9f), uint248(nonce))) - ) - ) - ); + } + + function _encodeSimple(uint256 x) internal pure returns (bytes memory) { + if (x == 0) return hex"80"; + if (x < 0x80) return abi.encodePacked(uint8(x)); + bytes memory ep = _ep(x); + return abi.encodePacked(uint8(0x80 + ep.length), ep); + } + + function _encodeSimple(bytes memory x) internal pure returns (bytes memory) { + uint256 n = x.length; + if (n == 0) return hex"80"; + if (n == 1 && uint8(bytes1(x[0])) < 0x80) return x; + if (n < 56) return abi.encodePacked(uint8(n + 0x80), x); + bytes memory ep = _ep(n); + return abi.encodePacked(uint8(0xb7 + ep.length), ep, x); + } + + function testRLPEncodeUint(uint256 x) public { + if (x == 0) { + _testRLPEncodeUint(x, hex"80"); + return; + } + if (x < 0x80) { + _testRLPEncodeUint(x, abi.encodePacked(uint8(x))); + return; + } + bytes memory ep = _ep(x); + uint256 n = ep.length; + _testRLPEncodeUint(x, abi.encodePacked(uint8(0x80 + n), _ep(x))); + } + + function testRLPEncodeUint() public { + _testRLPEncodeUint(0, hex"80"); + _testRLPEncodeUint(0x1, hex"01"); + _testRLPEncodeUint(0x2, hex"02"); + _testRLPEncodeUint(0x7e, hex"7e"); + _testRLPEncodeUint(0x7f, hex"7f"); + _testRLPEncodeUint(0x80, hex"8180"); + _testRLPEncodeUint(0x81, hex"8181"); + _testRLPEncodeUint(0x82, hex"8182"); + _testRLPEncodeUint(0xfe, hex"81fe"); + _testRLPEncodeUint(0xff, hex"81ff"); + unchecked { + uint256 x = type(uint256).max; + while (x != 0) { + testRLPEncodeUint(x); + testRLPEncodeUint(x - 1); + x >>= 8; + } } - // forgefmt: disable-next-item - if (nonce <= type(uint256).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf6), bytes1(0x94), deployer, bytes1(0xa0), uint256(nonce))) - ) - ) - ); + } + + function _testRLPEncodeUint(uint256 x, bytes memory expected) internal { + assertEq(LibRLP.encode(x), expected); + _checkMemory(); + } + + function testRLPEncodeList() public { + LibRLP.List memory l; + assertEq(LibRLP.encode(l), hex"c0"); + l.p(LibRLP.l()); + l.p(LibRLP.l(LibRLP.l())); + l.p(LibRLP.l(LibRLP.l()).p(LibRLP.l(LibRLP.l()))); + _checkMemory(); + bytes memory computed = LibRLP.encode(l); + _checkMemory(); + assertEq(computed, hex"c7c0c1c0c3c0c1c0"); + } + + function testRLPEncodeList2() public { + LibRLP.List memory l; + l.p("The").p("quick").p("brown").p("fox"); + l.p("jumps").p("over").p("the").p("lazy").p("dog"); + { + LibRLP.List memory lSub; + lSub.p(0).p(1).p(0x7f).p(0x80).p(0x81); + lSub.p(2 ** 256 - 1); + lSub.p("Jackdaws").p("loves").p("my").p(""); + lSub.p("great").p("sphinx").p("of").p("quartz"); + l.p(lSub); + } + l.p("0123456789abcdefghijklmnopqrstuvwxyz"); + _checkMemory(); + bytes memory computed = LibRLP.encode(l); + _checkMemory(); + bytes memory expected = + hex"f8a58354686585717569636b8562726f776e83666f78856a756d7073846f76657283746865846c617a7983646f67f85280017f81808181a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff884a61636b64617773856c6f766573826d798085677265617486737068696e78826f668671756172747aa4303132333435363738396162636465666768696a6b6c6d6e6f707172737475767778797a"; + assertEq(computed, expected); + } + + function testSmallLog256Equivalence(uint256 n) public { + n = _bound(n, 0, 0xffffffff); + assertEq(_smallLog256(n), FixedPointMathLib.log256(n)); + } + + function _smallLog256(uint256 n) internal pure returns (uint256 result) { + require(n >> 32 == 0); + /// @solidity memory-safe-assembly + assembly { + result := add(lt(0xff, n), add(lt(0xffff, n), lt(0xffffff, n))) } - revert(); } }