From 30f4916ca3a2423dead8eb6849c376720c32be1d Mon Sep 17 00:00:00 2001 From: Agusx1211 Date: Fri, 19 Jan 2024 20:04:13 +0000 Subject: [PATCH] Implement read multiple txs --- .../modules/utils/L2CompressorEncoder.sol | 189 ++++++++++++++++++ .../modules/utils/L2CompressorHuff.t.sol | 80 -------- .../utils/L2CompressorHuffReadTx.t.sol | 188 +---------------- .../utils/L2CompressorHuffReadTxs.t.sol | 130 ++++++++++++ src/L2Compressor.huff | 104 +++++++++- src/imps/L2CompressorReadTxs.huff | 30 +++ 6 files changed, 451 insertions(+), 270 deletions(-) create mode 100644 foundry_test/modules/utils/L2CompressorEncoder.sol delete mode 100644 foundry_test/modules/utils/L2CompressorHuff.t.sol create mode 100644 foundry_test/modules/utils/L2CompressorHuffReadTxs.t.sol create mode 100644 src/imps/L2CompressorReadTxs.huff diff --git a/foundry_test/modules/utils/L2CompressorEncoder.sol b/foundry_test/modules/utils/L2CompressorEncoder.sol new file mode 100644 index 0000000..c1ae0d7 --- /dev/null +++ b/foundry_test/modules/utils/L2CompressorEncoder.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.18; + + +function requiredBytesFor(bytes32 value) pure returns (uint8) { + return requiredBytesFor(uint256(value)); +} + +function requiredBytesFor(uint256 value) pure returns (uint8) { + if (value <= type(uint8).max) { + return 1; + } else if (value <= type(uint16).max) { + return 2; + } else if (value <= type(uint24).max) { + return 3; + } else if (value <= type(uint32).max) { + return 4; + } else if (value <= type(uint40).max) { + return 5; + } else if (value <= type(uint48).max) { + return 6; + } else if (value <= type(uint56).max) { + return 7; + } else if (value <= type(uint64).max) { + return 8; + } else if (value <= type(uint72).max) { + return 9; + } else if (value <= type(uint80).max) { + return 10; + } else if (value <= type(uint88).max) { + return 11; + } else if (value <= type(uint96).max) { + return 12; + } else if (value <= type(uint104).max) { + return 13; + } else if (value <= type(uint112).max) { + return 14; + } else if (value <= type(uint120).max) { + return 15; + } else if (value <= type(uint128).max) { + return 16; + } else if (value <= type(uint136).max) { + return 17; + } else if (value <= type(uint144).max) { + return 18; + } else if (value <= type(uint152).max) { + return 19; + } else if (value <= type(uint160).max) { + return 20; + } else if (value <= type(uint168).max) { + return 21; + } else if (value <= type(uint176).max) { + return 22; + } else if (value <= type(uint184).max) { + return 23; + } else if (value <= type(uint192).max) { + return 24; + } else if (value <= type(uint200).max) { + return 25; + } else if (value <= type(uint208).max) { + return 26; + } else if (value <= type(uint216).max) { + return 27; + } else if (value <= type(uint224).max) { + return 28; + } else if (value <= type(uint232).max) { + return 29; + } else if (value <= type(uint240).max) { + return 30; + } else if (value <= type(uint248).max) { + return 31; + } + + return 32; +} + +function packToBytes(bytes32 value, uint256 b) pure returns (bytes memory) { + return packToBytes(uint256(value), b); +} + +function packToBytes(uint256 value, uint256 b) pure returns (bytes memory) { + if (b == 1) { + return abi.encodePacked(uint8(value)); + } else if (b == 2) { + return abi.encodePacked(uint16(value)); + } else if (b == 3) { + return abi.encodePacked(uint24(value)); + } else if (b == 4) { + return abi.encodePacked(uint32(value)); + } else if (b == 5) { + return abi.encodePacked(uint40(value)); + } else if (b == 6) { + return abi.encodePacked(uint48(value)); + } else if (b == 7) { + return abi.encodePacked(uint56(value)); + } else if (b == 8) { + return abi.encodePacked(uint64(value)); + } else if (b == 9) { + return abi.encodePacked(uint72(value)); + } else if (b == 10) { + return abi.encodePacked(uint80(value)); + } else if (b == 11) { + return abi.encodePacked(uint88(value)); + } else if (b == 12) { + return abi.encodePacked(uint96(value)); + } else if (b == 13) { + return abi.encodePacked(uint104(value)); + } else if (b == 14) { + return abi.encodePacked(uint112(value)); + } else if (b == 15) { + return abi.encodePacked(uint120(value)); + } else if (b == 16) { + return abi.encodePacked(uint128(value)); + } else if (b == 17) { + return abi.encodePacked(uint136(value)); + } else if (b == 18) { + return abi.encodePacked(uint144(value)); + } else if (b == 19) { + return abi.encodePacked(uint152(value)); + } else if (b == 20) { + return abi.encodePacked(uint160(value)); + } else if (b == 21) { + return abi.encodePacked(uint168(value)); + } else if (b == 22) { + return abi.encodePacked(uint176(value)); + } else if (b == 23) { + return abi.encodePacked(uint184(value)); + } else if (b == 24) { + return abi.encodePacked(uint192(value)); + } else if (b == 25) { + return abi.encodePacked(uint200(value)); + } else if (b == 26) { + return abi.encodePacked(uint208(value)); + } else if (b == 27) { + return abi.encodePacked(uint216(value)); + } else if (b == 28) { + return abi.encodePacked(uint224(value)); + } else if (b == 29) { + return abi.encodePacked(uint232(value)); + } else if (b == 30) { + return abi.encodePacked(uint240(value)); + } else if (b == 31) { + return abi.encodePacked(uint248(value)); + } else if (b == 32) { + return abi.encodePacked(uint256(value)); + } else { + revert("Invalid number of bytes"); + } +} + +function encodeWord(uint256 _value) pure returns (bytes memory) { + uint8 b = requiredBytesFor(_value); + return abi.encodePacked(b, packToBytes(_value, b)); +} + +function build_flag(bool _delegateCall, bool _revertOnError, bool _hasGasLimit, bool _hasValue, bool _hasData) pure returns (uint8) { + uint8 res = 0; + + if (_delegateCall) { + res |= 128; + } + + if (_revertOnError) { + res |= 64; + } + + if (_hasGasLimit) { + res |= 32; + } + + if (_hasValue) { + res |= 16; + } + + // Hasdata uses first bit + if (_hasData) { + res |= 1; + } + + return res; +} + +function encode_raw_address(address _addr) pure returns (bytes memory) { + return encodeWord(uint256(uint160(_addr))); +} + +function encode_bytes_n(bytes memory _data) pure returns (bytes memory) { + return abi.encodePacked(uint8(0x2b), encodeWord(_data.length), _data); +} diff --git a/foundry_test/modules/utils/L2CompressorHuff.t.sol b/foundry_test/modules/utils/L2CompressorHuff.t.sol deleted file mode 100644 index b7c929f..0000000 --- a/foundry_test/modules/utils/L2CompressorHuff.t.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "foundry_test/base/AdvTest.sol"; - -import { HuffConfig } from "foundry-huff/HuffConfig.sol"; -import { HuffDeployer } from "foundry-huff/HuffDeployer.sol"; - -interface L2CompressorImps { - function testLoadDynamicSize( - bytes32 _a, - bytes32 _b, - uint256 rindex, - uint256 size - ) external pure returns (uint256, uint256); - - function testReadBytes32(bytes32 _a, bytes32 _b, uint256 rindex, uint256 windex, uint256 flag) external pure returns (uint256, uint256, bytes32); -} - -contract L2CompressorHuffTests is Test { - L2CompressorImps public imp; - - function setUp() public { - imp = L2CompressorImps( - HuffDeployer - .config() - .with_evm_version("paris") - .deploy("imps/L2CompressorImps") - ); - } - - function test_load_dynamic_size() external { - uint256 size; uint256 rindex; - - (size, rindex) = imp.testLoadDynamicSize( - bytes32(0x082366f82de6ef3a1d439d0adbfa2ee606f86c2774b4d946f895dfc1f88443a2), - bytes32(0), - 4, - 1 - ); - - assertEq(size, 8); - assertEq(rindex, 5); - - (size, rindex) = imp.testLoadDynamicSize( - bytes32(0x082366f82de6ef3a1d439d0adbfa2ee606f86c2774b4d946f895dfc1f88443a2), - bytes32(0), - 4 + 0, - 2 - ); - - assertEq(size, 2083); - assertEq(rindex, 4 + 2); - - (size, rindex) = imp.testLoadDynamicSize( - bytes32(0x082366f82de6ef3a1d439d0adbfa2ee606f86c2774b4d946f895dfc1f88443a2), - bytes32(0), - 4 + 2, - 4 - ); - - assertEq(size, 1727540710); - assertEq(rindex, 4 + 4 + 2); - } - - function test_load_bytes32() external { - uint256 windex; uint256 rindex; bytes32 value; - - (windex, rindex, value) = imp.testReadBytes32( - bytes32(0x020166f82de6ef3a1d439d0adbfa2ee606f86c2774b4d946f895dfc1f88443a2), - bytes32(0), - 4, - 0, - 0x23 - ); - - assertEq(windex, 32); - assertEq(rindex, 4 + 2); - } -} diff --git a/foundry_test/modules/utils/L2CompressorHuffReadTx.t.sol b/foundry_test/modules/utils/L2CompressorHuffReadTx.t.sol index e1c5e2c..400df42 100644 --- a/foundry_test/modules/utils/L2CompressorHuffReadTx.t.sol +++ b/foundry_test/modules/utils/L2CompressorHuffReadTx.t.sol @@ -11,193 +11,9 @@ import { HuffDeployer } from "foundry-huff/HuffDeployer.sol"; import "contracts/modules/commons/interfaces/IModuleCalls.sol"; -uint256 constant FMS = 0xa0; - -function requiredBytesFor(bytes32 value) pure returns (uint8) { - return requiredBytesFor(uint256(value)); -} - -function requiredBytesFor(uint256 value) pure returns (uint8) { - if (value <= type(uint8).max) { - return 1; - } else if (value <= type(uint16).max) { - return 2; - } else if (value <= type(uint24).max) { - return 3; - } else if (value <= type(uint32).max) { - return 4; - } else if (value <= type(uint40).max) { - return 5; - } else if (value <= type(uint48).max) { - return 6; - } else if (value <= type(uint56).max) { - return 7; - } else if (value <= type(uint64).max) { - return 8; - } else if (value <= type(uint72).max) { - return 9; - } else if (value <= type(uint80).max) { - return 10; - } else if (value <= type(uint88).max) { - return 11; - } else if (value <= type(uint96).max) { - return 12; - } else if (value <= type(uint104).max) { - return 13; - } else if (value <= type(uint112).max) { - return 14; - } else if (value <= type(uint120).max) { - return 15; - } else if (value <= type(uint128).max) { - return 16; - } else if (value <= type(uint136).max) { - return 17; - } else if (value <= type(uint144).max) { - return 18; - } else if (value <= type(uint152).max) { - return 19; - } else if (value <= type(uint160).max) { - return 20; - } else if (value <= type(uint168).max) { - return 21; - } else if (value <= type(uint176).max) { - return 22; - } else if (value <= type(uint184).max) { - return 23; - } else if (value <= type(uint192).max) { - return 24; - } else if (value <= type(uint200).max) { - return 25; - } else if (value <= type(uint208).max) { - return 26; - } else if (value <= type(uint216).max) { - return 27; - } else if (value <= type(uint224).max) { - return 28; - } else if (value <= type(uint232).max) { - return 29; - } else if (value <= type(uint240).max) { - return 30; - } else if (value <= type(uint248).max) { - return 31; - } - - return 32; -} - -function packToBytes(bytes32 value, uint256 b) pure returns (bytes memory) { - return packToBytes(uint256(value), b); -} - -function packToBytes(uint256 value, uint256 b) pure returns (bytes memory) { - if (b == 1) { - return abi.encodePacked(uint8(value)); - } else if (b == 2) { - return abi.encodePacked(uint16(value)); - } else if (b == 3) { - return abi.encodePacked(uint24(value)); - } else if (b == 4) { - return abi.encodePacked(uint32(value)); - } else if (b == 5) { - return abi.encodePacked(uint40(value)); - } else if (b == 6) { - return abi.encodePacked(uint48(value)); - } else if (b == 7) { - return abi.encodePacked(uint56(value)); - } else if (b == 8) { - return abi.encodePacked(uint64(value)); - } else if (b == 9) { - return abi.encodePacked(uint72(value)); - } else if (b == 10) { - return abi.encodePacked(uint80(value)); - } else if (b == 11) { - return abi.encodePacked(uint88(value)); - } else if (b == 12) { - return abi.encodePacked(uint96(value)); - } else if (b == 13) { - return abi.encodePacked(uint104(value)); - } else if (b == 14) { - return abi.encodePacked(uint112(value)); - } else if (b == 15) { - return abi.encodePacked(uint120(value)); - } else if (b == 16) { - return abi.encodePacked(uint128(value)); - } else if (b == 17) { - return abi.encodePacked(uint136(value)); - } else if (b == 18) { - return abi.encodePacked(uint144(value)); - } else if (b == 19) { - return abi.encodePacked(uint152(value)); - } else if (b == 20) { - return abi.encodePacked(uint160(value)); - } else if (b == 21) { - return abi.encodePacked(uint168(value)); - } else if (b == 22) { - return abi.encodePacked(uint176(value)); - } else if (b == 23) { - return abi.encodePacked(uint184(value)); - } else if (b == 24) { - return abi.encodePacked(uint192(value)); - } else if (b == 25) { - return abi.encodePacked(uint200(value)); - } else if (b == 26) { - return abi.encodePacked(uint208(value)); - } else if (b == 27) { - return abi.encodePacked(uint216(value)); - } else if (b == 28) { - return abi.encodePacked(uint224(value)); - } else if (b == 29) { - return abi.encodePacked(uint232(value)); - } else if (b == 30) { - return abi.encodePacked(uint240(value)); - } else if (b == 31) { - return abi.encodePacked(uint248(value)); - } else if (b == 32) { - return abi.encodePacked(uint256(value)); - } else { - revert("Invalid number of bytes"); - } -} +import "./L2CompressorEncoder.sol"; -function encodeWord(uint256 _value) pure returns (bytes memory) { - uint8 b = requiredBytesFor(_value); - return abi.encodePacked(b, packToBytes(_value, b)); -} - -function build_flag(bool _delegateCall, bool _revertOnError, bool _hasGasLimit, bool _hasValue, bool _hasData) pure returns (uint8) { - uint8 res = 0; - - if (_delegateCall) { - res |= 128; - } - - if (_revertOnError) { - res |= 64; - } - - if (_hasGasLimit) { - res |= 32; - } - - if (_hasValue) { - res |= 16; - } - - // Hasdata uses first bit - if (_hasData) { - res |= 1; - } - - return res; -} - -function encode_raw_address(address _addr) pure returns (bytes memory) { - return encodeWord(uint256(uint160(_addr))); -} - -function encode_bytes_n(bytes memory _data) pure returns (bytes memory) { - return abi.encodePacked(uint8(0x2b), encodeWord(_data.length), _data); -} +uint256 constant FMS = 0xa0; contract L2CompressorHuffReadTxTests is AdvTest { address public imp; diff --git a/foundry_test/modules/utils/L2CompressorHuffReadTxs.t.sol b/foundry_test/modules/utils/L2CompressorHuffReadTxs.t.sol new file mode 100644 index 0000000..1ba6fb8 --- /dev/null +++ b/foundry_test/modules/utils/L2CompressorHuffReadTxs.t.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "foundry_test/base/AdvTest.sol"; + +import "forge-std/console.sol"; +import "forge-std/console2.sol"; + +import { HuffConfig } from "foundry-huff/HuffConfig.sol"; +import { HuffDeployer } from "foundry-huff/HuffDeployer.sol"; + +import "contracts/modules/commons/interfaces/IModuleCalls.sol"; + +uint256 constant FMS = 0xa0; + +import "./L2CompressorEncoder.sol"; + +contract L2CompressorHuffReadTxTests is AdvTest { + address public imp; + + function setUp() public { + imp = address( + HuffDeployer + .config() + .with_evm_version("paris") + .deploy("imps/L2CompressorReadTxs") + ); + } + + function test_read_simple_2_transactions() external { + address _addr = address(0x1234567890123456789012345678901234567890); + address _addr2 = address(this); + bytes memory encoded = abi.encodePacked( + uint8(0x02), + build_flag(false, true, false, false, false), + encode_raw_address(_addr), + build_flag(true, true, false, false, false), + encode_raw_address(_addr2) + ); + + (bool s, bytes memory r) = imp.staticcall{ gas: 10000 }(encoded); + + assertTrue(s); + (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes)); + + IModuleCalls.Transaction[] memory t = new IModuleCalls.Transaction[](2); + t[0].delegateCall = false; + t[0].revertOnError = true; + t[0].target = _addr; + t[1].delegateCall = true; + t[1].revertOnError = true; + t[1].target = _addr2; + + assertEq(rindex, encoded.length); + assertEq(windex, res.length + FMS); + + // Abi encode prefixes with the point on which the data starts + // we don't do it on the compressor, so we need to append 32 + assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(t)); + } + + function test_read_simple_2_transactions_asymetric() external { + address _addr = address(0x1234567890123456789012345678901234567890); + address _addr2 = address(this); + bytes memory _data = hex"123456789012345678901234567890123456789012345678901234567890123456789011222211"; + + bytes memory encoded = abi.encodePacked( + uint8(0x02), + build_flag(false, true, false, false, true), + encode_raw_address(_addr), + encode_bytes_n(_data), + build_flag(true, true, false, false, false), + encode_raw_address(_addr2) + ); + + (bool s, bytes memory r) = imp.staticcall{ gas: 10000 }(encoded); + + assertTrue(s); + (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes)); + + IModuleCalls.Transaction[] memory t = new IModuleCalls.Transaction[](2); + t[0].delegateCall = false; + t[0].revertOnError = true; + t[0].target = _addr; + t[0].data = _data; + t[1].delegateCall = true; + t[1].revertOnError = true; + t[1].target = _addr2; + + assertEq(rindex, encoded.length); + assertEq(windex, res.length + FMS); + + // Abi encode prefixes with the point on which the data starts + // we don't do it on the compressor, so we need to append 32 + assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(t)); + } + + function test_read_transactions(IModuleCalls.Transaction[] memory _txs) external { + vm.assume(_txs.length != 0 && _txs.length <= type(uint8).max); + + bytes memory encoded = abi.encodePacked( + uint8(_txs.length) + ); + + for (uint256 i = 0; i < _txs.length; i++) { + IModuleCalls.Transaction memory t = _txs[i]; + + encoded = abi.encodePacked( + encoded, + build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0), + t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(""), + encode_raw_address(t.target), + t.value != 0 ? encodeWord(t.value) : bytes(""), + t.data.length != 0 ? encode_bytes_n(t.data) : bytes("") + ); + } + + (bool s, bytes memory r) = imp.staticcall(encoded); + + assertTrue(s); + (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes)); + + assertEq(rindex, encoded.length); + assertEq(windex, res.length + FMS); + + // Abi encode prefixes with the point on which the data starts + // we don't do it on the compressor, so we need to append 32 + assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(_txs)); + } +} diff --git a/src/L2Compressor.huff b/src/L2Compressor.huff index 662adf1..c5ba633 100644 --- a/src/L2Compressor.huff +++ b/src/L2Compressor.huff @@ -162,10 +162,15 @@ [HIGHEST_FLAG] lt // [HIGHEST_FLAG < flag, flag, windex, rindex + 1] default jumpi // [flag, windex, rindex + 1] - __tablesize(FLAG_TABLE) // [table_size, flag, windex, rindex + 1] - __tablestart(FLAG_TABLE) // [table_start, table_size, flag, windex, rindex + 1] - 0x00 // [0x00, table_start, table_size, flag, windex, rindex + 1] - codecopy // [flag, windex, rindex + 1] + // Starts to become cheaper to skip the loading + // after 5 times, most real world cases will have more than + // 5 times. Notice that this assumes a single READ_FLAG instance + 0x00 mload no_load jumpi + __tablesize(FLAG_TABLE) // [table_size, flag, windex, rindex + 1] + __tablestart(FLAG_TABLE) // [table_start, table_size, flag, windex, rindex + 1] + 0x00 // [0x00, table_start, table_size, flag, windex, rindex + 1] + codecopy // [flag, windex, rindex + 1] + no_load: dup1 // [flag, flag, windex, rindex + 1] 0x01 shl // [flag << 0x01, flag, windex, rindex + 1] @@ -446,6 +451,97 @@ 0x03 eq ASSERT() // [] } +#define macro READ_TRANSACTIONS_STANDALONE() = takes (2) returns (2) { + skip jump + rf: + READ_FLAG() + skip: + READ_TRANSACTIONS(rf) +} + +#define macro READ_TRANSACTIONS(nrfs) = takes (2) returns (2) { + // input stack: [windex, rindex] + + dup2 // [rindex, windex, rindex] + calldataload // [mem[rindex], windex, rindex] + 0x00 byte // [tx_num, windex, rindex] + swap2 // [rindex, windex, tx_num] + 0x01 add // [rindex + 1, windex, tx_num] + 0x00 // [i, rindex, windex, tx_num] + + swap3 // [tx_num, rindex, windex, i] + swap2 // [windex, rindex, tx_num, i] + + // Write the number of transactions + + dup3 // [tx_num, windex, rindex, tx_num, i] + dup2 // [windex, tx_num, windex, rindex, tx_num, i] + mstore // [windex, rindex, tx_num, i] + 0x20 add // [windex + 0x20, rindex, tx_num, i] + + // Reserve 32 bytes for each tx (excluding the first one, as we already know) + // these will be used to store start of each tx + + // The first transaction will always start at 0x20 * txs + 0x20 + + dup3 // [tx_num, windex, rindex, tx_num, i] + 0x05 shl // [r_start, windex, rindex, tx_num, i] + dup1 // [r_start, r_start, windex, rindex, tx_num, i] + + dup3 // [windex, r_start, r_start, ts_index, rindex, tx_num, i] + add // [windex, r_start, ts_index, rindex, tx_num, i] + + swap3 // [rindex, r_start, ts_index, windex, tx_num, i] + dup4 // [windex, rindex, r_start, ts_index, windex, tx_num, i] + + do_tx: // [windex, rindex, pos, ts_index, windex, tx_num, i] + + // store pos for this transaction + // but keep a copy of it as it will be used again + + swap2 // [pos, rindex, windex, ts_index, windex, tx_num, i] + dup1 // [pos, pos, rindex, windex, ts_index, windex, tx_num, i] + dup5 // [ts_index, pos, pos, rindex, windex, ts_index, windex, tx_num, i] + mstore // [pos, rindex, windex, ts_index, windex, tx_num, i] + + swap3 // [ts_index, rindex, windex, pos, windex, tx_num, i] + 0x20 add // [ts_index, rindex, windex, pos, windex, tx_num, i] + + swap3 // [pos, rindex, windex, ts_index, windex, tx_num, i] + swap2 // [windex, rindex, pos, ts_index, windex, tx_num, i] + + READ_TRANSACTION() // [windex, rindex, pos, ts_index, prev_windex, tx_num, i] + + // size = windex - prev_windex + + swap4 // [prev_windex, rindex, r_start, ts_index, windex, tx_num, i] + dup5 // [windex, prev_windex, rindex, r_start, ts_index, windex, tx_num, i] + sub // [tx_i_size, rindex, r_start, ts_index, windex, tx_num, i] + + // pos = size + r_start + + swap1 // [rindex, tx_i_size, r_start, ts_index, windex, tx_num, i] + swap2 // [r_start, tx_i_size, rindex, ts_index, windex, tx_num, i] + add // [pos, rindex, ts_index, windex, tx_num, i] + + // Re-arrange the stack, we are about to loop back + + swap1 // [rindex, pos, ts_index, windex, tx_num, i] + dup4 // [windex, rindex, pos, ts_index, windex, tx_num, i] + + // Check if we have more to read + swap6 // [i, rindex, pos, ts_index, windex, tx_num, windex] + 0x01 add // [i + 1, rindex, pos, ts_index, windex, tx_num, windex] + swap6 // [windex, rindex, pos, ts_index, windex, tx_num, i] + dup7 // [i, windex, rindex, pos, ts_index, windex, tx_num, i] + + // The ts_index contains the index of the transaction x 32, we can + // easily get the i and compare it with the len of transactions to know if we must continue or not + + dup7 // [tx_num, i, windex, rindex, pos, ts_index, windex, tx_num, i] + xor do_tx jumpi // [windex, rindex, pos, ts_index, windex, tx_num] +} + #define macro READ_TRANSACTION_STANDALONE() = takes (2) returns (2) { skip jump rf: diff --git a/src/imps/L2CompressorReadTxs.huff b/src/imps/L2CompressorReadTxs.huff new file mode 100644 index 0000000..453a385 --- /dev/null +++ b/src/imps/L2CompressorReadTxs.huff @@ -0,0 +1,30 @@ +#include "../L2Compressor.huff" + +#define constant FMS = 0xa0 + +// Function Dispatching +#define macro MAIN() = takes (1) returns (1) { + // readAdvanced with whatever calldata is passed + // first 32 bytes returns the new rindex and the next 32 bytes returns the new windex + + 0x00 // [rindex] + [FMS] // [windex, rindex] + + READ_TRANSACTIONS_STANDALONE() // [windex, rindex] + + [FMS] // [0xa0, windex, rindex] + dup2 // [windex, 0xa0, windex, rindex] + sub // [len, windex, rindex] + + swap2 // [rindex, windex, len] + + 0x80 [FMS] sub mstore // [windex, len] + 0x60 [FMS] sub mstore // [len] + + 0x60 0x40 [FMS] sub mstore // [len] + dup1 0x20 [FMS] sub mstore // [len] + + 0x80 add // [len + 0x80] + + 0x80 [FMS] sub return +}