feat: import all circuit utils tests

This commit is contained in:
zkfriendly
2025-08-20 20:24:29 +02:00
parent cf6b0c828d
commit b3816b0d68
6 changed files with 571 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import { Test } from "forge-std/Test.sol";
import { CommandUtils } from "@zk-email/email-tx-builder/src/libraries/CommandUtils.sol";
import { CircuitUtilsHelper } from "./_CircuitUtilsHelper.sol";
contract ExtractCommandParamByIndexTest is Test {
CircuitUtilsHelper private _helper;
function setUp() public {
_helper = new CircuitUtilsHelper();
}
function test_simpleStringParameter() public view {
string[] memory template = new string[](2);
template[0] = "String:";
template[1] = CommandUtils.STRING_MATCHER;
string memory command = "String: username";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "username");
}
function test_ethAddressParameter() public view {
string[] memory template = new string[](4);
template[0] = "Address:";
template[1] = CommandUtils.ETH_ADDR_MATCHER;
string memory command = "Address: 0xafBD210c60dD651892a61804A989eEF7bD63CBA0";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "0xafBD210c60dD651892a61804A989eEF7bD63CBA0");
}
function test_multipleParameters() public view {
string[] memory template = new string[](4);
template[0] = "Address1:";
template[1] = CommandUtils.ETH_ADDR_MATCHER;
template[2] = "Address2:";
template[3] = CommandUtils.ETH_ADDR_MATCHER;
string memory command =
"Address1: 0x0000000000000000000000000000000000000000 Address2: 0x1111111111111111111111111111111111111111";
string memory result1 = _helper.callExtractCommandParamByIndex(template, command, 0);
string memory result2 = _helper.callExtractCommandParamByIndex(template, command, 1);
assertEq(result1, "0x0000000000000000000000000000000000000000");
assertEq(result2, "0x1111111111111111111111111111111111111111");
}
function test_parameterIndexOutOfBounds() public view {
string[] memory template = new string[](2);
template[0] = "Address:";
template[1] = CommandUtils.ETH_ADDR_MATCHER;
string memory command = "Address: 0xafBD210c60dD651892a61804A989eEF7bD63CBA0";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 2);
assertEq(result, "");
}
function test_noParameters() public view {
string[] memory template = new string[](2);
template[0] = "PING";
template[1] = "PONG";
string memory command = "PING PONG";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "");
}
function test_parameterAtStart() public view {
string[] memory template = new string[](2);
template[0] = CommandUtils.STRING_MATCHER;
template[1] = "!";
string memory command = "username !";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "username");
}
function test_consecutiveParameters() public view {
string[] memory template = new string[](4);
template[0] = CommandUtils.STRING_MATCHER;
template[1] = CommandUtils.UINT_MATCHER;
template[2] = CommandUtils.STRING_MATCHER;
template[3] = "!";
string memory command = "user 123 token !";
string memory result1 = _helper.callExtractCommandParamByIndex(template, command, 0);
string memory result2 = _helper.callExtractCommandParamByIndex(template, command, 1);
string memory result3 = _helper.callExtractCommandParamByIndex(template, command, 2);
assertEq(result1, "user");
assertEq(result2, "123");
assertEq(result3, "token");
}
function test_realEnsClaimCommand() public view {
string[] memory template = new string[](9);
template[0] = "Claim";
template[1] = "ENS";
template[2] = "name";
template[3] = "for";
template[4] = "address";
template[5] = CommandUtils.ETH_ADDR_MATCHER;
template[6] = "with";
template[7] = "resolver";
template[8] = CommandUtils.STRING_MATCHER;
string memory command =
"Claim ENS name for address 0xafBD210c60dD651892a61804A989eEF7bD63CBA0 with resolver resolver.eth";
string memory result1 = _helper.callExtractCommandParamByIndex(template, command, 0);
string memory result2 = _helper.callExtractCommandParamByIndex(template, command, 1);
assertEq(result1, "0xafBD210c60dD651892a61804A989eEF7bD63CBA0");
assertEq(result2, "resolver.eth");
}
function test_simpleEnsClaimCommand() public view {
string[] memory template = new string[](6);
template[0] = "Claim";
template[1] = "ENS";
template[2] = "name";
template[3] = "for";
template[4] = "address";
template[5] = CommandUtils.ETH_ADDR_MATCHER;
string memory command = "Claim ENS name for address 0xafBD210c60dD651892a61804A989eEF7bD63CBA0";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "0xafBD210c60dD651892a61804A989eEF7bD63CBA0");
}
}

View File

@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import { Test } from "forge-std/Test.sol";
import { CircuitUtilsHelper } from "./_CircuitUtilsHelper.sol";
contract ExtractEmailPartsTest is Test {
CircuitUtilsHelper private _helper;
function setUp() public {
_helper = new CircuitUtilsHelper();
}
function test_simpleEmail() public view {
string memory email = "user@gmail.com";
string[] memory parts = _helper.callExtractEmailParts(email);
assertEq(parts.length, 2);
assertEq(parts[0], "user$gmail");
assertEq(parts[1], "com");
}
function test_emailWithSubdomain() public view {
string memory email = "user@sub.domain.com";
string[] memory parts = _helper.callExtractEmailParts(email);
assertEq(parts.length, 3);
assertEq(parts[0], "user$sub");
assertEq(parts[1], "domain");
assertEq(parts[2], "com");
}
function test_emailWithMultipleDots() public view {
string memory email = "user@domain.co.uk";
string[] memory parts = _helper.callExtractEmailParts(email);
assertEq(parts.length, 3);
assertEq(parts[0], "user$domain");
assertEq(parts[1], "co");
assertEq(parts[2], "uk");
}
function test_complexEmail() public view {
string memory email = "user.name+tag@sub.domain.co.uk";
string[] memory parts = _helper.callExtractEmailParts(email);
assertEq(parts.length, 5);
assertEq(parts[0], "user");
assertEq(parts[1], "name+tag$sub");
assertEq(parts[2], "domain");
assertEq(parts[3], "co");
assertEq(parts[4], "uk");
}
}

View File

@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import { Test } from "forge-std/Test.sol";
import { CircuitUtils } from "../../CircuitUtils.sol";
import { CircuitUtilsHelper } from "./_CircuitUtilsHelper.sol";
contract FlattenFieldsTest is Test {
CircuitUtilsHelper private _helper;
function setUp() public {
_helper = new CircuitUtilsHelper();
}
function test_expectRevert_tooManyElements() public {
uint256[][] memory inputs = new uint256[][](2);
inputs[0] = new uint256[](30);
inputs[1] = new uint256[](31);
for (uint256 i = 0; i < 30; i++) {
inputs[0][i] = i + 1;
}
for (uint256 i = 0; i < 31; i++) {
inputs[1][i] = i + 31;
}
vm.expectRevert(CircuitUtils.InvalidPubSignalsLength.selector);
_helper.callFlattenFields(inputs);
}
function test_expectRevert_tooFewElements() public {
uint256[][] memory inputs = new uint256[][](2);
inputs[0] = new uint256[](30);
inputs[1] = new uint256[](29);
for (uint256 i = 0; i < 30; i++) {
inputs[0][i] = i + 1;
}
for (uint256 i = 0; i < 29; i++) {
inputs[1][i] = i + 31;
}
vm.expectRevert(CircuitUtils.InvalidPubSignalsLength.selector);
_helper.callFlattenFields(inputs);
}
function test_zeroArrays() public {
uint256[][] memory inputs = new uint256[][](0);
vm.expectRevert(CircuitUtils.InvalidPubSignalsLength.selector);
_helper.callFlattenFields(inputs);
}
function test_singleArray() public view {
uint256[][] memory inputs = new uint256[][](1);
inputs[0] = new uint256[](60);
for (uint256 i = 0; i < 60; i++) {
inputs[0][i] = i + 1;
}
uint256[60] memory result = _helper.callFlattenFields(inputs);
for (uint256 i = 0; i < 60; i++) {
assertEq(result[i], i + 1);
}
}
function test_multipleArrays() public view {
uint256[][] memory inputs = new uint256[][](3);
inputs[0] = new uint256[](20);
inputs[1] = new uint256[](20);
inputs[2] = new uint256[](20);
for (uint256 i = 0; i < 20; i++) {
inputs[0][i] = i + 1;
inputs[1][i] = i + 21;
inputs[2][i] = i + 41;
}
uint256[60] memory result = _helper.callFlattenFields(inputs);
for (uint256 i = 0; i < 20; i++) {
assertEq(result[i], i + 1);
assertEq(result[i + 20], i + 21);
assertEq(result[i + 40], i + 41);
}
}
function test_manySmallArrays() public view {
uint256[][] memory inputs = new uint256[][](60);
for (uint256 i = 0; i < 60; i++) {
inputs[i] = new uint256[](1);
inputs[i][0] = i + 1;
}
uint256[60] memory result = _helper.callFlattenFields(inputs);
for (uint256 i = 0; i < 60; i++) {
assertEq(result[i], i + 1);
}
}
}

View File

@@ -0,0 +1,128 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import { Test } from "forge-std/Test.sol";
import { CircuitUtilsHelper } from "./_CircuitUtilsHelper.sol";
contract PackBytes2FieldsTest is Test {
CircuitUtilsHelper private _helper;
function setUp() public {
_helper = new CircuitUtilsHelper();
}
function test_emptyBytes() public view {
bytes memory emptyBytes = "";
uint256[] memory fields = _helper.callPackBytes2Fields(emptyBytes, 0);
assertEq(fields.length, 0);
}
function test_singleByte() public view {
bytes memory singleByte = hex"41";
uint256[] memory fields = _helper.callPackBytes2Fields(singleByte, 1);
assertEq(fields.length, 1);
assertEq(uint8(fields[0]), 0x41);
}
function test_exactly31Bytes() public view {
bytes memory data = new bytes(31);
for (uint256 i = 0; i < 31; i++) {
data[i] = bytes1(uint8(i + 1));
}
uint256[] memory fields = _helper.callPackBytes2Fields(data, 31);
assertEq(fields.length, 1);
uint256 expected = 0;
for (uint256 i = 0; i < 31; i++) {
expected += uint256(uint8(data[i])) << (8 * i);
}
assertEq(fields[0], expected);
}
function test_32Bytes() public view {
bytes memory data = new bytes(32);
for (uint256 i = 0; i < 32; i++) {
data[i] = bytes1(uint8(i + 1));
}
uint256[] memory fields = _helper.callPackBytes2Fields(data, 32);
assertEq(fields.length, 2);
uint256 expectedFirst = 0;
for (uint256 i = 0; i < 31; i++) {
expectedFirst += uint256(uint8(data[i])) << (8 * i);
}
assertEq(fields[0], expectedFirst);
assertEq(fields[1], uint256(uint8(data[31])));
}
function test_withPadding() public view {
bytes memory data = hex"414243";
uint256[] memory fields = _helper.callPackBytes2Fields(data, 10);
assertEq(fields.length, 1);
uint256 expected = 0x41 + (0x42 << 8) + (0x43 << 16);
assertEq(fields[0], expected);
}
function test_exactFieldBoundaries() public view {
bytes memory data = new bytes(62);
for (uint256 i = 0; i < 62; i++) {
data[i] = bytes1(uint8(i + 1));
}
uint256[] memory fields = _helper.callPackBytes2Fields(data, 62);
assertEq(fields.length, 2);
uint256 expectedFirst = 0;
for (uint256 i = 0; i < 31; i++) {
expectedFirst += uint256(uint8(data[i])) << (8 * i);
}
assertEq(fields[0], expectedFirst);
uint256 expectedSecond = 0;
for (uint256 i = 31; i < 62; i++) {
expectedSecond += uint256(uint8(data[i])) << (8 * (i - 31));
}
assertEq(fields[1], expectedSecond);
}
function test_allZeros() public view {
bytes memory data = new bytes(31);
uint256[] memory fields = _helper.callPackBytes2Fields(data, 31);
assertEq(fields.length, 1);
assertEq(fields[0], 0);
}
function test_maxByteValues() public view {
bytes memory data = new bytes(31);
for (uint256 i = 0; i < 31; i++) {
data[i] = 0xFF;
}
uint256[] memory fields = _helper.callPackBytes2Fields(data, 31);
assertEq(fields.length, 1);
uint256 expected = 0;
for (uint256 i = 0; i < 31; i++) {
expected += 0xFF << (8 * i);
}
assertEq(fields[0], expected);
}
function test_realisticString() public view {
bytes memory data = "gmail.com";
uint256[] memory fields = _helper.callPackBytes2Fields(data, 255);
assertEq(fields.length, 9);
uint256 expected = 0;
for (uint256 i = 0; i < data.length; i++) {
expected += uint256(uint8(data[i])) << (8 * i);
}
assertEq(fields[0], expected);
for (uint256 i = 1; i < 9; i++) {
assertEq(fields[i], 0);
}
}
function test_paddedSizeSmallerThanData() public view {
bytes memory data = "This is a longer string that should be truncated";
uint256[] memory fields = _helper.callPackBytes2Fields(data, 10);
assertEq(fields.length, 1);
uint256 expected = 0;
for (uint256 i = 0; i < 10; i++) {
expected += uint256(uint8(data[i])) << (8 * i);
}
assertEq(fields[0], expected);
}
}

View File

@@ -0,0 +1,130 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import { Test } from "forge-std/Test.sol";
import { CircuitUtilsHelper } from "./_CircuitUtilsHelper.sol";
contract UnpackFields2BytesTest is Test {
CircuitUtilsHelper private _helper;
function setUp() public {
_helper = new CircuitUtilsHelper();
}
function test_emptyFields() public view {
uint256[] memory fields = new uint256[](0);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 0);
assertEq(result.length, 0);
}
function test_singleFieldSingleByte() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0x41;
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 1);
assertEq(result.length, 1);
assertEq(uint8(result[0]), 0x41);
}
function test_singleFieldMultipleBytes() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0x41 + (0x42 << 8) + (0x43 << 16);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 3);
assertEq(result.length, 3);
assertEq(uint8(result[0]), 0x41);
assertEq(uint8(result[1]), 0x42);
assertEq(uint8(result[2]), 0x43);
}
function test_multipleFields() public view {
uint256[] memory fields = new uint256[](2);
fields[0] = 0x41 + (0x42 << 8) + (0x43 << 16);
fields[1] = 0x44 + (0x45 << 8) + (0x46 << 16);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 6);
// Only the first 3 bytes are non-zero, the rest are zeros and will be trimmed
assertEq(result.length, 3);
assertEq(uint8(result[0]), 0x41);
assertEq(uint8(result[1]), 0x42);
assertEq(uint8(result[2]), 0x43);
}
function test_trimTrailingZeros() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0x41 + (0x42 << 8) + (0x00 << 16);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 3);
assertEq(result.length, 2);
assertEq(uint8(result[0]), 0x41);
assertEq(uint8(result[1]), 0x42);
}
function test_zerosInMiddle() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0x41 + (0x00 << 8) + (0x43 << 16);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 3);
assertEq(result.length, 3);
assertEq(uint8(result[0]), 0x41);
assertEq(uint8(result[1]), 0x00);
assertEq(uint8(result[2]), 0x43);
}
function test_withOffset() public view {
uint256[] memory fields = new uint256[](3);
fields[0] = 0x11 + (0x12 << 8);
fields[1] = 0x21 + (0x22 << 8) + (0x23 << 16);
fields[2] = 0x31 + (0x32 << 8);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 1, 3);
assertEq(result.length, 3);
assertEq(uint8(result[0]), 0x21);
assertEq(uint8(result[1]), 0x22);
assertEq(uint8(result[2]), 0x23);
}
function test_moreFieldsThanAvailable() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0x41 + (0x42 << 8);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 4);
assertEq(result.length, 2);
assertEq(uint8(result[0]), 0x41);
assertEq(uint8(result[1]), 0x42);
}
function test_allZeros() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0;
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 31);
assertEq(result.length, 0);
}
function test_maxFieldValue() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0;
for (uint256 i = 0; i < 31; i++) {
fields[0] += 0xFF << (8 * i);
}
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 31);
assertEq(result.length, 31);
for (uint256 i = 0; i < 31; i++) {
assertEq(uint8(result[i]), 0xFF);
}
}
function test_multipleFieldsWithPadding() public view {
uint256[] memory fields = new uint256[](2);
fields[0] = 0x41 + (0x42 << 8) + (0x43 << 16);
fields[1] = 0x44 + (0x45 << 8);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 5);
// Only the first 3 bytes are non-zero, the rest are zeros and will be trimmed
assertEq(result.length, 3);
assertEq(uint8(result[0]), 0x41);
assertEq(uint8(result[1]), 0x42);
assertEq(uint8(result[2]), 0x43);
}
function test_partialFieldUnpack() public view {
uint256[] memory fields = new uint256[](1);
fields[0] = 0x41 + (0x42 << 8) + (0x43 << 16) + (0x44 << 24);
bytes memory result = _helper.callUnpackFields2Bytes(fields, 0, 2);
assertEq(result.length, 2);
assertEq(uint8(result[0]), 0x41);
assertEq(uint8(result[1]), 0x42);
}
}

View File

@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import { CircuitUtils } from "../../CircuitUtils.sol";
contract CircuitUtilsHelper {
function callExtractCommandParamByIndex(
string[] memory template,
string memory command,
uint256 index
)
external
pure
returns (string memory)
{
return CircuitUtils.extractCommandParamByIndex(template, command, index);
}
function callExtractEmailParts(string memory email) external pure returns (string[] memory) {
return CircuitUtils.extractEmailParts(email);
}
function callFlattenFields(uint256[][] memory inputs) external pure returns (uint256[60] memory) {
return CircuitUtils.flattenFields(inputs);
}
function callPackBytes2Fields(bytes memory data, uint256 paddedSize) external pure returns (uint256[] memory) {
return CircuitUtils.packBytes2Fields(data, paddedSize);
}
function callUnpackFields2Bytes(
uint256[] calldata fields,
uint256 startIndex,
uint256 paddedSize
)
external
pure
returns (bytes memory)
{
return CircuitUtils.unpackFields2Bytes(fields, startIndex, paddedSize);
}
}