Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ RLP encoding #976

Merged
merged 19 commits into from
Jun 29, 2024
21 changes: 15 additions & 6 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
232 changes: 231 additions & 1 deletion src/utils/LibRLP.sol
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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
}
}
}
}
Loading