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

[Feat] 817 rlp tx writer #818

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions contracts/src/_testing/unit/libraries/TestEip1559RlpEncoder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

import { Eip1559RlpEncoder } from "../../../libraries/Eip1559RlpEncoder.sol";

contract TestEip1559RlpEncoder {
uint256 chainId;

constructor(uint256 _chainId) {
chainId = _chainId;
}

function encodeEip1559Transaction(
Eip1559RlpEncoder.Eip1559Transaction calldata _transaction
) external view returns (bytes memory rlpEncodedTransaction, bytes32 transactionHash) {
(rlpEncodedTransaction, transactionHash) = Eip1559RlpEncoder.encodeEIP1559Tx(chainId, _transaction);
}
}
52 changes: 52 additions & 0 deletions contracts/src/libraries/Eip1559RlpEncoder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0

/**
* @title Library for RLP Encoding EIP-1559 transactions.
* @author ConsenSys Software Inc.
* @custom:security-contact [email protected]
*/
pragma solidity ^0.8.28;

import { RlpWriter } from "./RlpWriter.sol";

library Eip1559RlpEncoder {
/// @dev chainId is defined in the function and access list is not encoded.
struct Eip1559Transaction {
uint256 nonce;
uint256 maxPriorityFeePerGas;
uint256 maxFeePerGas;
uint256 gasLimit;
address to;
uint256 value;
bytes input;
uint8 v;
uint256 r;
uint256 s;
}

function encodeEIP1559Tx(
uint256 _chainId,
Eip1559Transaction memory _transaction
) internal pure returns (bytes memory rlpEncodedTransaction, bytes32 transactionHash) {
bytes[] memory fields = new bytes[](12);

fields[0] = RlpWriter._encodeUint(_chainId);
fields[1] = RlpWriter._encodeUint(_transaction.nonce);
fields[2] = RlpWriter._encodeUint(_transaction.maxPriorityFeePerGas);
fields[3] = RlpWriter._encodeUint(_transaction.maxFeePerGas);
fields[4] = RlpWriter._encodeUint(_transaction.gasLimit);
fields[5] = RlpWriter._encodeAddress(_transaction.to);
fields[6] = RlpWriter._encodeUint(_transaction.value);
fields[7] = RlpWriter._encodeBytes(_transaction.input);
fields[8] = RlpWriter._encodeList(new bytes[](0)); // AccessList empty on purpose.
fields[9] = RlpWriter._encodeUint(_transaction.v);
fields[10] = RlpWriter._encodeUint(_transaction.r);
fields[11] = RlpWriter._encodeUint(_transaction.s);

bytes memory encodedList = RlpWriter._encodeList(fields);

// Prepend type byte 0x02
rlpEncodedTransaction = abi.encodePacked(hex"02", encodedList);
transactionHash = keccak256(rlpEncodedTransaction);
}
}
197 changes: 197 additions & 0 deletions contracts/src/libraries/RlpWriter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// SPDX-License-Identifier: Apache-2.0

/**
* @title Library for RLP Encoding data.
* @author ConsenSys Software Inc.
* @custom:security-contact [email protected]
*/
pragma solidity ^0.8.28;

/// @custom:attribution https://github.com/bakaoh/solidity-rlp-encode
/// @title RLPWriter
/// @author RLPWriter is a library for encoding Solidity types to RLP bytes. Adapted from Bakaoh's
/// RLPEncode library (https://github.com/bakaoh/solidity-rlp-encode) with
/// modifications to improve legibility and gas consumption.
library RlpWriter {
function _encodeBytes(bytes memory _bytesIn) internal pure returns (bytes memory bytesOut) {
if (_bytesIn.length == 1 && uint8(_bytesIn[0]) < 0x80) {
return _bytesIn; // Return as-is for single-byte < 0x80
}

bytes memory lengthPrefix = _writeLength(_bytesIn.length, 128);
uint256 prefixLen = lengthPrefix.length;
uint256 dataLen = _bytesIn.length;
uint256 totalLen = prefixLen + dataLen;

assembly {
// Allocate output buffer
bytesOut := mload(0x40)
mstore(bytesOut, totalLen)

// Copy prefix
let dest := add(bytesOut, 0x20)
let src := add(lengthPrefix, 0x20)
mcopy(dest, src, prefixLen)

// Copy input bytes
dest := add(dest, prefixLen)
src := add(_bytesIn, 0x20)
mcopy(dest, src, dataLen)

// Move free memory pointer
mstore(0x40, add(add(bytesOut, 0x20), totalLen))
}
}

function _encodeUint(uint256 _uintIn) internal pure returns (bytes memory uintBytes) {
uintBytes = _encodeBytes(_toBinary(_uintIn));
}

function _encodeAddress(address _addressIn) internal pure returns (bytes memory addressBytes) {
bytes memory addressRaw = new bytes(20);
assembly {
mstore(add(addressRaw, 0x20), shl(96, _addressIn)) // store address left-aligned
}
addressBytes = _encodeBytes(addressRaw);
}

function _encodeString(string memory _stringIn) internal pure returns (bytes memory stringBytes) {
stringBytes = _encodeBytes(bytes(_stringIn));
}

function _encodeBool(bool _boolIn) internal pure returns (bytes memory boolBytes) {
boolBytes = new bytes(1);
boolBytes[0] = (_boolIn ? bytes1(0x01) : bytes1(0x80));
}

function _encodeList(bytes[] memory _bytesToEncode) internal pure returns (bytes memory listBytes) {
listBytes = _flatten(_bytesToEncode);
listBytes = abi.encodePacked(_writeLength(listBytes.length, 192), listBytes);
}

/// @notice Encode integer in big endian binary form with no leading zeroes.
/// @param _uintValue The integer to encode.
/// @return binaryBytes RLP encoded bytes.
function _toBinary(uint256 _uintValue) private pure returns (bytes memory binaryBytes) {
assembly {
let ptr := mload(0x40) // Get free memory pointer

let i := 0
// Scan for first non-zero byte from MSB (big-endian)
for {

} lt(i, 32) {
i := add(i, 1)
} {
if iszero(and(shr(sub(248, mul(i, 8)), _uintValue), 0xff)) {
continue
}
break
}

let length := sub(32, i) // Number of non-zero bytes
binaryBytes := ptr
mstore(binaryBytes, length)

// Write stripped bytes
for {
let j := 0
} lt(j, length) {
j := add(j, 1)
} {
let shift := mul(sub(length, add(j, 1)), 8)
let b := and(shr(shift, _uintValue), 0xff)
mstore8(add(add(binaryBytes, 0x20), j), b)
}

// Move free memory pointer
mstore(0x40, add(add(ptr, 0x20), length))
}
}

function _writeLength(uint256 _itemLength, uint256 _offset) private pure returns (bytes memory lengthBytes) {
assembly {
// Start from free memory pointer
lengthBytes := mload(0x40)

switch lt(_itemLength, 56)
case 1 {
// Case: short length
mstore8(add(lengthBytes, 0x20), add(_itemLength, _offset))
mstore(lengthBytes, 1) // Set bytes length to 1
mstore(0x40, add(lengthBytes, 0x21)) // Advance free memory pointer
}
default {
// Case: long length
let temp := _itemLength
let lengthOfLength := 0

for {

} gt(temp, 0) {

} {
lengthOfLength := add(lengthOfLength, 1)
temp := shr(8, temp)
}

// First byte: offset + 55 + lengthOfLength
mstore8(add(lengthBytes, 0x20), add(add(lengthOfLength, _offset), 55))

// Write big-endian bytes of _itemLength
for {
let i := 0
} lt(i, lengthOfLength) {
i := add(i, 1)
} {
let shift := mul(8, sub(lengthOfLength, add(i, 1)))
let b := and(shr(shift, _itemLength), 0xff)
mstore8(add(add(lengthBytes, 0x21), i), b)
}

let totalLen := add(lengthOfLength, 1)
mstore(lengthBytes, totalLen) // Set bytes length
mstore(0x40, add(add(lengthBytes, 0x20), totalLen)) // Advance free memory pointer
}
}
}

// @custom:attribution https://github.com/sammayo/solidity-rlp-encoder
/// @notice Flattens a list of byte strings into one byte string.
/// @dev mcopy is used for the Cancun EVM fork. See original for other forks.
/// @param _bytesList List of byte strings to flatten.
/// @return flattenedBytes The flattened byte string.
function _flatten(bytes[] memory _bytesList) private pure returns (bytes memory flattenedBytes) {
uint256 bytesListLength = _bytesList.length;
if (bytesListLength == 0) {
return new bytes(0);
}

uint256 flattenedBytesLength;
uint256 reusableCounter;
for (; reusableCounter < bytesListLength; reusableCounter++) {
unchecked {
flattenedBytesLength += _bytesList[reusableCounter].length;
}
}

flattenedBytes = new bytes(flattenedBytesLength);

uint256 flattenedPtr;
assembly {
flattenedPtr := add(flattenedBytes, 0x20)
}

bytes memory item;
uint256 itemLength;

for (reusableCounter = 0; reusableCounter < bytesListLength; reusableCounter++) {
item = _bytesList[reusableCounter];
itemLength = item.length;
assembly {
mcopy(flattenedPtr, add(item, 0x20), itemLength)
flattenedPtr := add(flattenedPtr, itemLength)
}
}
}
}
6 changes: 2 additions & 4 deletions contracts/src/security/pausing/PauseManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
if (isPaused(_pauseType)) {
revert IsPaused(_pauseType);
}

unchecked {
if (hasRole(SECURITY_COUNCIL_ROLE, _msgSender())) {
pauseExpiryTimestamp = type(uint256).max - COOLDOWN_DURATION;
Expand Down Expand Up @@ -193,9 +193,7 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
* @dev Throws if UNUSED pause type is used, or the pause expiry period has not passed.
* @param _pauseType The pause type value.
*/
function unPauseByExpiredType(
PauseType _pauseType
) external onlyUsedPausedTypes(_pauseType) {
function unPauseByExpiredType(PauseType _pauseType) external onlyUsedPausedTypes(_pauseType) {
if (!isPaused(_pauseType)) {
revert IsNotPaused(_pauseType);
}
Expand Down
17 changes: 7 additions & 10 deletions contracts/test/foundry/LineaRollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ILineaRollup } from "src/rollup/interfaces/ILineaRollup.sol";
import { IPauseManager } from "src/security/pausing/interfaces/IPauseManager.sol";
import { IPermissionsManager } from "src/security/access/interfaces/IPermissionsManager.sol";
import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import { ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract LineaRollupTestHelper is LineaRollup {
function calculateY(bytes calldata data, bytes32 dataEvaluationPoint) external pure returns (bytes32) {
Expand All @@ -24,7 +24,7 @@ contract LineaRollupTestHelper is LineaRollup {
bytes32 _dataEvaluationPoint,
bytes32 _dataEvaluationClaim
) external pure returns (bytes32 shnarf) {
return _computeShnarf(_parentShnarf, _snarkHash, _finalStateRootHash, _dataEvaluationPoint, _dataEvaluationClaim);
return _computeShnarf(_parentShnarf, _snarkHash, _finalStateRootHash, _dataEvaluationPoint, _dataEvaluationClaim);
}
}

Expand Down Expand Up @@ -95,17 +95,14 @@ contract LineaRollupTest is Test {
// Adjust compressedData to start with 0x00
submission.compressedData = hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";

bytes32 dataEvaluationPoint = EfficientLeftRightKeccak._efficientKeccak(submission.snarkHash, keccak256(submission.compressedData));
bytes32 dataEvaluationPoint = EfficientLeftRightKeccak._efficientKeccak(
submission.snarkHash,
keccak256(submission.compressedData)
);

bytes32 dataEvaluationClaim = lineaRollup.calculateY(submission.compressedData, dataEvaluationPoint);

bytes32 parentShnarf = lineaRollup.computeShnarf(
0x0,
0x0,
0x0,
0x0,
0x0
);
bytes32 parentShnarf = lineaRollup.computeShnarf(0x0, 0x0, 0x0, 0x0, 0x0);

bytes32 expectedShnarf = keccak256(
abi.encodePacked(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockHash": "0xeb8235afc4b60d415a9567f36c0c5a5e4662f0b8c93f5343beb61af105dba6c2",
"blockNumber": "0x151b18c",
"from": "0x13fb49bd78e18fff091c0b9019f052f1dd6d7433",
"gas": "0x18fd1",
"gasPrice": "0x2b1c92c6",
"maxFeePerGas": "0x35a4e900",
"maxPriorityFeePerGas": "0x2faf080",
"hash": "0xd147a5f40f4224c0ed73777ba74f590c683ab0a4c8553b195d84bc5dfbd3ae0b",
"input": "0x9f3ce55a00000000000000000000000013fb49bd78e18fff091c0b9019f052f1dd6d743300000000000000000000000000000000000000000000000000000a400e08caa400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x8b",
"to": "0xd19d4b5d358258f05d7b411e21a1460d11b0876f",
"transactionIndex": "0xaa",
"value": "0x166dd3710594aa4",
"type": "0x2",
"accessList": [],
"chainId": "0x1",
"v": "0x1",
"r": "0x1fdb8dea465b1fa7c8e77b09a26cbfe1aa2b9b5110a78e39c3414ad516243621",
"s": "0x716097a1f1e0d8489b9e9cdd91dca9e6207832e26b350b4146dacec914b238dd",
"yParity": "0x1"
},
"rlpEncoded": "0x02f8f901818b8402faf0808435a4e90083018fd194d19d4b5d358258f05d7b411e21a1460d11b0876f880166dd3710594aa4b8849f3ce55a00000000000000000000000013fb49bd78e18fff091c0b9019f052f1dd6d743300000000000000000000000000000000000000000000000000000a400e08caa400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000c001a01fdb8dea465b1fa7c8e77b09a26cbfe1aa2b9b5110a78e39c3414ad516243621a0716097a1f1e0d8489b9e9cdd91dca9e6207832e26b350b4146dacec914b238dd"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"accessList": [],
"blockHash": "0xd20ab77f23688dbd42e3c8fa2d448c070bb4dc9ce5cae8f393e99907fde41755",
"blockNumber": "0x151b88e",
"chainId": "0x1",
"from": "0x0d85129b29781bab8a422b1bcd60b68f45dd2c40",
"gas": "0x5208",
"gasPrice": "0x1fcf0b58",
"hash": "0xd4d9a0482cf4f43d2f712ee6edcf7f6824aed99b5a339b1fae9d093e59c2b394",
"input": "0x",
"maxFeePerGas": "0x2835231b",
"maxPriorityFeePerGas": "0xe195f",
"nonce": "0x87",
"r": "0x266af86d7a0de8ad3cad80493401207ac2c7470fd66be304a2efbb33fbabb8a5",
"s": "0x7e15a5b7a18360eaebfc2635f84abecfbce441037afcacbc39f46af810e9fa5d",
"to": "0xdfaa75323fb721e5f29d43859390f62cc4b600b8",
"transactionIndex": "0x61",
"type": "0x2",
"v": "0x0",
"value": "0x20e0e27022b718",
"yParity": "0x0"
},
"rlpEncoded": "0x02f871018187830e195f842835231b82520894dfaa75323fb721e5f29d43859390f62cc4b600b88720e0e27022b71880c080a0266af86d7a0de8ad3cad80493401207ac2c7470fd66be304a2efbb33fbabb8a5a07e15a5b7a18360eaebfc2635f84abecfbce441037afcacbc39f46af810e9fa5d"
}
4 changes: 3 additions & 1 deletion contracts/test/hardhat/common/helpers/hashing.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ethers } from "ethers";
import { BytesLike, ethers } from "ethers";
import { encodeData } from "./encoding";

export const generateKeccak256BytesDirectly = (data: BytesLike) => ethers.keccak256(data);

export const generateKeccak256Hash = (str: string) => generateKeccak256(["string"], [str], true);

export const generateKeccak256 = (types: string[], values: unknown[], packed?: boolean) =>
Expand Down
Loading
Loading