Skip to content

Commit

Permalink
Encode dynamic abi fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Agusx1211 committed Jan 24, 2024
1 parent bcaf9ab commit 6f5f557
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 75 deletions.
30 changes: 29 additions & 1 deletion foundry_test/modules/utils/L2CompressorEncoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,32 @@ function encode_sequence_chained_signatures(bytes[] memory _payloads) pure retur
}

return encoded;
}
}

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;
}
52 changes: 52 additions & 0 deletions foundry_test/modules/utils/L2CompressorHuffReadFlag.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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]));
}
}
157 changes: 83 additions & 74 deletions src/L2Compressor.huff
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -1577,69 +1577,71 @@

// 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]

// We will need to have two write indexes, one for the pointers (or values)
// 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(<nrfs>) // [windex, rindex, bwindex, d_bitmap, size, i]
PERFORM_NESTED_READ_FLAG(<nrfs>) // [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:
Expand All @@ -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(<nrfs>) // [bwindex, rindex, windex, i, prev_bwindex, d_bitmap, size, size_b_pointer]
PERFORM_NESTED_READ_FLAG(<nrfs>) // [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]
}
Expand Down

0 comments on commit 6f5f557

Please sign in to comment.