From 6f5f5576368843f70e46face82685324590241bd Mon Sep 17 00:00:00 2001 From: Agusx1211 Date: Wed, 24 Jan 2024 18:34:41 +0000 Subject: [PATCH] Encode dynamic abi fixes --- .../modules/utils/L2CompressorEncoder.sol | 30 +++- .../utils/L2CompressorHuffReadFlag.t.sol | 52 ++++++ src/L2Compressor.huff | 157 +++++++++--------- 3 files changed, 164 insertions(+), 75 deletions(-) diff --git a/foundry_test/modules/utils/L2CompressorEncoder.sol b/foundry_test/modules/utils/L2CompressorEncoder.sol index 285aa51..f7327b2 100644 --- a/foundry_test/modules/utils/L2CompressorEncoder.sol +++ b/foundry_test/modules/utils/L2CompressorEncoder.sol @@ -316,4 +316,32 @@ function encode_sequence_chained_signatures(bytes[] memory _payloads) pure retur } return encoded; -} \ No newline at end of file +} + +function encode_abi_dynamic( + bytes4 _selector, + bool[] memory _isDynamic, + bytes[] memory _values +) pure returns (bytes memory) { + bytes memory encoded = abi.encodePacked(uint8(0x4b), _selector, uint8(_isDynamic.length)); + uint8 isDynamicBitmap = 0; + + // The first 8 values can be dynamic, this is marked using a bitmap + for (uint256 i = 0; i < 8 && i < _isDynamic.length; i++) { + if (_isDynamic[i]) { + isDynamicBitmap |= uint8(1 << i); + } + } + + encoded = abi.encodePacked(encoded, isDynamicBitmap); + + for (uint256 i = 0; i < _values.length; i++) { + if (_isDynamic[i]) { + encoded = abi.encodePacked(encoded, encode_bytes_n(_values[i])); + } else { + encoded = abi.encodePacked(encoded, encodeWord(abi.decode(_values[i], (uint256)))); + } + } + + return encoded; +} diff --git a/foundry_test/modules/utils/L2CompressorHuffReadFlag.t.sol b/foundry_test/modules/utils/L2CompressorHuffReadFlag.t.sol index 99f72cd..135ebc5 100644 --- a/foundry_test/modules/utils/L2CompressorHuffReadFlag.t.sol +++ b/foundry_test/modules/utils/L2CompressorHuffReadFlag.t.sol @@ -557,4 +557,56 @@ contract L2CompressorHuffReadFlagTests is AdvTest { assertEq(res, expected); } + + function test_read_abi_dynamic_no_dynamic( + bytes4 _selector, + bytes32[] calldata _values + ) external { + vm.assume(_values.length <= type(uint8).max && _values.length != 0); + bool[] memory isDynamic = new bool[](_values.length); + bytes[] memory values = new bytes[](_values.length); + for (uint256 i = 0; i < _values.length; i++) { + values[i] = abi.encodePacked(_values[i]); + } + + bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, values); + + (bool s, bytes memory r) = imp.staticcall(encoded); + assertTrue(s); + + (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes)); + + assertEq(windex, FMS + res.length); + assertEq(rindex, encoded.length); + + assertEq(res, abi.encodePacked(_selector, _values)); + } + + function test_read_abi_dynamic_only( + bytes4 _selector, + bytes memory _data1, + bytes memory _data2 + ) external { + bool[] memory isDynamic = new bool[](2); + bytes[] memory _values = new bytes[](2); + + isDynamic[0] = true; + isDynamic[1] = true; + + _values[0] = _data1; + _values[1] = _data2; + + bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values); + + (bool s, bytes memory r) = imp.staticcall(encoded); + + assertTrue(s); + + (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes)); + + assertEq(windex, FMS + res.length); + assertEq(rindex, encoded.length); + + assertEq(res, abi.encodeWithSelector(_selector, _values[0], _values[1])); + } } diff --git a/src/L2Compressor.huff b/src/L2Compressor.huff index 8af6781..632a4bf 100644 --- a/src/L2Compressor.huff +++ b/src/L2Compressor.huff @@ -168,7 +168,7 @@ FLAG_READ_DYNAMIC_ABI // 0x4b } -#define constant HIGHEST_FLAG = 0x4a +#define constant HIGHEST_FLAG = 0x4b #define macro READ_FLAG() = takes (2) returns (2) { nrfs: @@ -1577,23 +1577,24 @@ // First byte determines the number of parameters - swap1 // [rindex, windex] - dup1 // [rindex, rindex, windex] - mload // [word, rindex, windex] - callvalue byte // [size, rindex, windex] - swap1 // [rindex, size, windex] - 0x01 add // [rindex, size, windex] + dup1 // [windex, windex, rindex] + swap2 // [rindex, windex, windex] + dup1 // [rindex, rindex, windex, windex] + calldataload // [word, rindex, windex, windex] + callvalue byte // [size, rindex, windex, windex] + swap1 // [rindex, size, windex, windex] + 0x01 add // [rindex, size, windex, windex] // Second is a bitmap, it determines which // parameters are dynamic and which ones are static // notice: this limits dynamic parameters to the first 8 ones // all other ones are automatically considered static - dup1 // [rindex, rindex, size, windex] - mload // [word, rindex, size, windex] - callvalue byte // [d_bitmap, rindex, size, windex] - swap1 // [rindex, d_bitmap, size, windex] - 0x01 add // [rindex, d_bitmap, size, windex] + dup1 // [rindex, rindex, size, windex, windex] + calldataload // [word, rindex, size, windex, windex] + callvalue byte // [d_bitmap, rindex, size, windex, windex] + swap1 // [rindex, d_bitmap, size, windex, windex] + 0x01 add // [rindex, d_bitmap, size, windex, windex] callvalue // [0x00, rindex, d_bitmap, size, windex] @@ -1601,45 +1602,46 @@ // and another one for the data blobs. The data blobs are the actual data // of the dynamic parameters. It starts on (windex + size * 0x20). - dup5 // [windex, i, rindex, d_bitmap, size, windex] - dup5 // [size, windex, i, rindex, d_bitmap, size, windex] - 0x05 shl // [size * 0x20, windex, i, rindex, d_bitmap, size, windex] - add // [bwindex, i, rindex, d_bitmap, size, windex] + dup5 // [windex, i, rindex, d_bitmap, size, windex, windex] + dup5 // [size, windex, i, rindex, d_bitmap, size, windex, windex] + 0x05 shl // [size * 0x20, windex, i, rindex, d_bitmap, size, windex, windex] + add // [bwindex, i, rindex, d_bitmap, size, windex, windex] - read_param: // [bwindex, i, rindex, d_bitmap, size, windex] + read_param: // [bwindex, i, rindex, d_bitmap, size, windex, swindex] // We read the bitmap and determine if the param is dynamic or not - dup4 // [d_bitmap, bwindex, i, rindex, d_bitmap, size, windex] - 0x01 // [0x01, d_bitmap, bwindex, i, rindex, d_bitmap, size, windex] - dup4 // [i, 0x01, d_bitmap, bwindex, i, rindex, d_bitmap, size, windex] - shl // [(0x01 << i), d_bitmap, bwindex, i, rindex, d_bitmap, size, windex] - and // [is_dynamic, bwindex, i, rindex, d_bitmap, size, windex] + dup4 // [d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex] + 0x01 // [0x01, d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex] + dup4 // [i, 0x01, d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex] + shl // [(0x01 << i), d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex] + and // [is_dynamic, bwindex, i, rindex, d_bitmap, size, windex, swindex] - is_dynamic jumpi // [bwindex, i, rindex, d_bitmap, size, windex] + is_dynamic jumpi // [bwindex, i, rindex, d_bitmap, size, windex, swindex] // is_not_dynamic: // The parameter is not dynamic, we just need to read one value on windex // and we can continue - swap2 // [rindex, i, bwindex, d_bitmap, size, windex] - swap1 // [i, rindex, bwindex, d_bitmap, size, windex] - swap5 // [windex, rindex, bwindex, d_bitmap, size, i] + swap2 // [rindex, i, bwindex, d_bitmap, size, windex, swindex] + swap1 // [i, rindex, bwindex, d_bitmap, size, windex, swindex] + swap5 // [windex, rindex, bwindex, d_bitmap, size, i, swindex] - PERFORM_NESTED_READ_FLAG() // [windex, rindex, bwindex, d_bitmap, size, i] + PERFORM_NESTED_READ_FLAG() // [windex, rindex, bwindex, d_bitmap, size, i, swindex] // We trust that we only increased windex by 0x20, and we keep iterating - swap5 // [i, rindex, bwindex, d_bitmap, size, windex] - 0x01 add // [i + 1, rindex, bwindex, d_bitmap, size, windex] + swap5 // [i, rindex, bwindex, d_bitmap, size, windex, swindex] + 0x01 add // [i + 1, rindex, bwindex, d_bitmap, size, windex, swindex] - swap1 // [rindex, i, bwindex, d_bitmap, size, windex] - swap2 // [bwindex, i, rindex, d_bitmap, size, windex] - dup5 // [size, bwindex, i, rindex, d_bitmap, size, windex] - dup3 // [i, size, bwindex, i, rindex, d_bitmap, size, windex] - lt // [(i < size), bwindex, i, rindex, d_bitmap, size, windex] - read_param jumpi // [bwindex, i, rindex, d_bitmap, size, windex] + swap1 // [rindex, i, bwindex, d_bitmap, size, windex, swindex] + swap2 // [bwindex, i, rindex, d_bitmap, size, windex, swindex] + dup5 // [size, bwindex, i, rindex, d_bitmap, size, windex, swindex] + dup3 // [i, size, bwindex, i, rindex, d_bitmap, size, windex, swindex] + lt // [(i < size), bwindex, i, rindex, d_bitmap, size, windex, swindex] + read_param jumpi // [bwindex, i, rindex, d_bitmap, size, windex, swindex] + break jump - is_dynamic: // [bwindex, i, rindex, d_bitmap, size, windex] + is_dynamic: // [bwindex, i, rindex, d_bitmap, size, windex, swindex] // The parameter is dynamic, we are going to write it on bwindex // but we need to: @@ -1648,57 +1650,64 @@ // - pad the end result to 32 bytes // - store in windex a pointer to bwindex - dup1 // [bwindex, bwindex, i, rindex, d_bitmap, size, windex] - dup7 // [windex, bwindex, bwindex, i, rindex, d_bitmap, size, windex] - mstore // [bwindex, i, rindex, d_bitmap, size, windex] - swap5 // [windex, i, rindex, d_bitmap, size, bwindex] - 0x20 add // [windex + 0x20, i, rindex, d_bitmap, size, bwindex] + // The data pointer should lead to bwindex - swindex + dup7 // [swindex, bwindex, i, rindex, d_bitmap, size, windex, swindex] + dup2 // [bwindex, swindex, bwindex, i, rindex, d_bitmap, size, windex, swindex] + sub // [d_pointer, bwindex, i, rindex, d_bitmap, size, windex, swindex] + dup7 // [windex, d_pointer, bwindex, i, rindex, d_bitmap, size, windex, swindex] + mstore // [bwindex, i, rindex, d_bitmap, size, windex, swindex] + swap5 // [windex, i, rindex, d_bitmap, size, bwindex, swindex] + 0x20 add // [windex + 0x20, i, rindex, d_bitmap, size, bwindex, swindex] - dup6 // [bwindex, windex, i, rindex, d_bitmap, size, bwindex] - 0x20 add // [bwindex + 0x20, windex, i, rindex, d_bitmap, size, bwindex] - swap3 // [rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer] - dup4 // [bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer] + dup6 // [bwindex, windex, i, rindex, d_bitmap, size, bwindex, swindex] + 0x20 add // [bwindex + 0x20, windex, i, rindex, d_bitmap, size, bwindex, swindex] + swap3 // [rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex] + dup4 // [bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex] - PERFORM_NESTED_READ_FLAG() // [bwindex, rindex, windex, i, prev_bwindex, d_bitmap, size, size_b_pointer] + PERFORM_NESTED_READ_FLAG() // [bwindex, rindex, windex, i, prev_bwindex, d_bitmap, size, size_b_pointer, swindex] - swap4 // [prev_bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer] - dup5 // [bwindex, prev_bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer] - sub // [b_size, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer] + swap4 // [prev_bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex] + dup5 // [bwindex, prev_bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex] + sub // [b_size, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex] - dup1 // [b_size, b_size, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer] - swap8 // [size_b_pointer, b_size, rindex, windex, i, bwindex, d_bitmap, size, b_size] - mstore // [rindex, windex, i, bwindex, d_bitmap, size, b_size] + dup1 // [b_size, b_size, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex] + swap8 // [size_b_pointer, b_size, rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex] + mstore // [rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex] // Last we need to pad the bwindex callvalue // [0x00, rindex, windex, i, bwindex, d_bitmap, size, b_size] - dup5 // [bwindex, 0x00, rindex, windex, i, bwindex, d_bitmap, size, b_size] - mstore // [rindex, windex, i, bwindex, d_bitmap, size, b_size] + dup5 // [bwindex, 0x00, rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex] + mstore // [rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex] - swap3 // [bwindex, windex, i, rindex, d_bitmap, size, b_size] - swap1 // [windex, bwindex, i, rindex, d_bitmap, size, b_size] - swap6 // [b_size, bwindex, i, rindex, d_bitmap, size, windex] + swap3 // [bwindex, windex, i, rindex, d_bitmap, size, b_size, swindex] + swap1 // [windex, bwindex, i, rindex, d_bitmap, size, b_size, swindex] + swap6 // [b_size, bwindex, i, rindex, d_bitmap, size, windex, swindex] - 0x1f and // [b_size % 32, bwindex, i, rindex, d_bitmap, size, windex] - 0x20 sub // [pad_diff, bwindex, i, rindex, d_bitmap, size, windex] - 0x1f and // [pad_diff % 32, bwindex, i, rindex, d_bitmap, size, windex] - add // [bwindex, i, rindex, d_bitmap, size, windex] + 0x1f and // [b_size % 32, bwindex, i, rindex, d_bitmap, size, windex, swindex] + 0x20 sub // [pad_diff, bwindex, i, rindex, d_bitmap, size, windex, swindex] + 0x1f and // [pad_diff % 32, bwindex, i, rindex, d_bitmap, size, windex, swindex] + add // [bwindex, i, rindex, d_bitmap, size, windex, swindex] - swap1 // [i, bwindex, rindex, d_bitmap, size, windex] - 0x01 add // [i + 1, bwindex, rindex, d_bitmap, size, windex] - swap1 // [bwindex, i, rindex, d_bitmap, size, windex] + swap1 // [i, bwindex, rindex, d_bitmap, size, windex, swindex] + 0x01 add // [i + 1, bwindex, rindex, d_bitmap, size, windex, swindex] + swap1 // [bwindex, i, rindex, d_bitmap, size, windex, swindex] - dup5 // [size, bwindex, i, rindex, d_bitmap, size, windex] - dup3 // [i, size, bwindex, i, rindex, d_bitmap, size, windex] - lt // [(i < size), bwindex, i, rindex, d_bitmap, size, windex] - read_param jumpi // [bwindex, i, rindex, d_bitmap, size, windex] + dup5 // [size, bwindex, i, rindex, d_bitmap, size, windex, swindex] + dup3 // [i, size, bwindex, i, rindex, d_bitmap, size, windex, swindex] + lt // [(i < size), bwindex, i, rindex, d_bitmap, size, windex, swindex] + read_param jumpi // [bwindex, i, rindex, d_bitmap, size, windex, swindex] + + break: // We have finished! we only need to clear the stack - pop // [i, rindex, d_bitmap, size, windex] - pop // [rindex, d_bitmap, size, windex] - swap2 // [size, d_bitmap, rindex, windex] - pop // [d_bitmap, rindex, windex] - pop // [rindex, windex] - swap1 // [windex, rindex] + // notice that bwindex is the windex now + swap5 // [windex, i, rindex, d_bitmap, size, bwindex, swindex] + pop // [i, rindex, d_bitmap, size, bwindex, swindex] + pop // [rindex, d_bitmap, size, bwindex, swindex] + swap4 // [swindex, d_bitmap, size, bwindex, rindex] + pop // [d_bitmap, size, bwindex, rindex] + pop // [size, bwindex, rindex] + pop // [bwindex, rindex] // output stack: [windex, rindex] }