mirror of
https://github.com/zkemail/zk-email-verify.git
synced 2026-01-07 20:54:05 -05:00
Merge pull request #279 from zkemail/zkfriendly/sol-191-circuit-utils-library
feat: circuit utils library
This commit is contained in:
8
.github/workflows/action.yml
vendored
8
.github/workflows/action.yml
vendored
@@ -48,20 +48,20 @@ jobs:
|
||||
environment: development
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Set Node.js 18.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
|
||||
- name: Install Yarn dependencies
|
||||
working-directory: ./packages/helpers
|
||||
run: yarn install --immutable
|
||||
|
||||
|
||||
- name: Run build
|
||||
working-directory: ./packages/helpers
|
||||
run: yarn build
|
||||
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: ./packages/helpers
|
||||
run: yarn test
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -102,3 +102,6 @@ target/
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# ignore build logs
|
||||
thinking.log
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.3.cjs
|
||||
|
||||
checksumBehavior: ignore
|
||||
|
||||
@@ -43,4 +43,4 @@
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
236
packages/contracts/CircuitUtils.sol
Normal file
236
packages/contracts/CircuitUtils.sol
Normal file
@@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import { Bytes } from "@openzeppelin/contracts/utils/Bytes.sol";
|
||||
|
||||
/**
|
||||
* @title CircuitUtils
|
||||
* @notice Library for ZK circuit-related utilities including field element packing and proof processing
|
||||
* @dev This library provides functions for converting between byte arrays and field elements
|
||||
* and other utilities needed for zero-knowledge proof circuit compatibility.
|
||||
*/
|
||||
library CircuitUtils {
|
||||
using Bytes for bytes;
|
||||
|
||||
/**
|
||||
* @notice Error thrown when the public signals array length is not exactly 60
|
||||
* @dev The ZK circuit expects exactly 60 public signals for verification
|
||||
*/
|
||||
error InvalidPubSignalsLength();
|
||||
|
||||
/**
|
||||
* @notice Error thrown when the command length is invalid
|
||||
* @dev The command should have the expected format and length
|
||||
*/
|
||||
error InvalidCommandLength();
|
||||
|
||||
/**
|
||||
* @notice Error thrown when the data length is greater than the padded size
|
||||
* @dev The data should have the expected format and length
|
||||
*/
|
||||
error InvalidDataLength();
|
||||
|
||||
/**
|
||||
* @notice Packs byte arrays into field elements for ZK circuit compatibility
|
||||
* @param _bytes The byte array to pack into field elements
|
||||
* @param _paddedSize The target size after padding (must be larger than or equal to _bytes.length)
|
||||
* @return An array of field elements containing the packed byte data
|
||||
* @dev This function packs bytes into field elements by:
|
||||
* 1. Determining how many field elements are needed (31 bytes per field element)
|
||||
* 2. Packing bytes in little-endian order within each field element
|
||||
* 3. Padding with zeros if the input is shorter than _paddedSize
|
||||
* 4. Ensuring the resulting field elements are compatible with ZK circuits
|
||||
*
|
||||
* Each field element can contain up to 31 bytes to ensure the result stays below
|
||||
* the BN128 curve order. Bytes are packed as: byte0 + (byte1 << 8) + (byte2 << 16) + ...
|
||||
*/
|
||||
function packBytes2Fields(bytes memory _bytes, uint256 _paddedSize) internal pure returns (uint256[] memory) {
|
||||
if (_bytes.length > _paddedSize) revert InvalidDataLength();
|
||||
|
||||
uint256 remain = _paddedSize % 31;
|
||||
uint256 numFields = (_paddedSize - remain) / 31;
|
||||
if (remain > 0) {
|
||||
numFields += 1;
|
||||
}
|
||||
uint256[] memory fields = new uint256[](numFields);
|
||||
uint256 idx = 0;
|
||||
uint256 byteVal = 0;
|
||||
for (uint256 i = 0; i < numFields; i++) {
|
||||
for (uint256 j = 0; j < 31; j++) {
|
||||
idx = i * 31 + j;
|
||||
if (idx >= _paddedSize) {
|
||||
break;
|
||||
}
|
||||
if (idx >= _bytes.length) {
|
||||
byteVal = 0;
|
||||
} else {
|
||||
byteVal = uint256(uint8(_bytes[idx]));
|
||||
}
|
||||
if (j == 0) {
|
||||
fields[i] = byteVal;
|
||||
} else {
|
||||
fields[i] += (byteVal << (8 * j));
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Packs a string into field elements for ZK circuit compatibility
|
||||
* @param _string The string to pack
|
||||
* @param paddedSize The target size after padding
|
||||
* @return fields The packed field elements
|
||||
*/
|
||||
function packString(string memory _string, uint256 paddedSize) internal pure returns (uint256[] memory fields) {
|
||||
fields = packBytes2Fields(bytes(_string), paddedSize);
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Packs a bytes32 value into a single field element
|
||||
* @param _bytes32 The bytes32 value to pack
|
||||
* @return fields The packed field element
|
||||
*/
|
||||
function packBytes32(bytes32 _bytes32) internal pure returns (uint256[] memory fields) {
|
||||
fields = new uint256[](1);
|
||||
fields[0] = uint256(_bytes32);
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Packs a boolean value into a single field element
|
||||
* @param b The boolean value to pack
|
||||
* @return fields The packed field element
|
||||
*/
|
||||
function packBool(bool b) internal pure returns (uint256[] memory fields) {
|
||||
fields = new uint256[](1);
|
||||
fields[0] = b ? 1 : 0;
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Packs a uint256 value into a single field element
|
||||
* @param _uint256 The uint256 value to pack
|
||||
* @return fields The packed field element
|
||||
*/
|
||||
function packUint256(uint256 _uint256) internal pure returns (uint256[] memory fields) {
|
||||
fields = new uint256[](1);
|
||||
fields[0] = _uint256;
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Unpacks field elements back to bytes
|
||||
* @param _pucSignals Array of public signals
|
||||
* @param _startIndex Starting index in pubSignals
|
||||
* @param _paddedSize Original padded size of the bytes
|
||||
* @return The unpacked bytes
|
||||
*/
|
||||
function unpackFields2Bytes(
|
||||
uint256[] calldata _pucSignals,
|
||||
uint256 _startIndex,
|
||||
uint256 _paddedSize
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
uint256 remain = _paddedSize % 31;
|
||||
uint256 numFields = (_paddedSize - remain) / 31;
|
||||
if (remain > 0) {
|
||||
numFields += 1;
|
||||
}
|
||||
|
||||
bytes memory result = new bytes(_paddedSize);
|
||||
uint256 resultIndex = 0;
|
||||
|
||||
for (uint256 i = 0; i < numFields; i++) {
|
||||
uint256 field = _pucSignals[_startIndex + i];
|
||||
for (uint256 j = 0; j < 31 && resultIndex < _paddedSize; j++) {
|
||||
result[resultIndex] = bytes1(uint8(field & 0xFF));
|
||||
field = field >> 8;
|
||||
resultIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Trim trailing zeros
|
||||
uint256 actualLength = 0;
|
||||
for (uint256 i = 0; i < result.length; i++) {
|
||||
if (result[i] != 0) {
|
||||
actualLength = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result.slice(0, actualLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Unpacks field elements to a string
|
||||
* @param pubSignals Array of public signals
|
||||
* @param startIndex Starting index in pubSignals
|
||||
* @param paddedSize Original padded size of the string
|
||||
* @return The unpacked string
|
||||
*/
|
||||
function unpackString(
|
||||
uint256[] calldata pubSignals,
|
||||
uint256 startIndex,
|
||||
uint256 paddedSize
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (string memory)
|
||||
{
|
||||
return string(unpackFields2Bytes(pubSignals, startIndex, paddedSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Unpacks a bytes32 value from public signals
|
||||
* @param pubSignals Array of public signals
|
||||
* @param startIndex Starting index in pubSignals
|
||||
* @return The unpacked bytes32 value
|
||||
*/
|
||||
function unpackBytes32(uint256[] calldata pubSignals, uint256 startIndex) internal pure returns (bytes32) {
|
||||
return bytes32(pubSignals[startIndex]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Unpacks a uint256 value from public signals
|
||||
* @param pubSignals Array of public signals
|
||||
* @param startIndex Starting index in pubSignals
|
||||
* @return The unpacked uint256 value
|
||||
*/
|
||||
function unpackUint256(uint256[] calldata pubSignals, uint256 startIndex) internal pure returns (uint256) {
|
||||
return pubSignals[startIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Unpacks a boolean value from public signals
|
||||
* @param pubSignals Array of public signals
|
||||
* @param startIndex Starting index in pubSignals
|
||||
* @return The unpacked boolean value
|
||||
*/
|
||||
function unpackBool(uint256[] calldata pubSignals, uint256 startIndex) internal pure returns (bool) {
|
||||
return pubSignals[startIndex] == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Flattens multiple arrays of field elements into a single array
|
||||
* @param inputs The arrays of field elements to flatten
|
||||
* @param outLength The length of the flattened array
|
||||
* @return out The flattened array
|
||||
*/
|
||||
function flattenFields(uint256[][] memory inputs, uint256 outLength) internal pure returns (uint256[] memory out) {
|
||||
out = new uint256[](outLength);
|
||||
uint256 k = 0;
|
||||
for (uint256 i = 0; i < inputs.length; i++) {
|
||||
uint256[] memory arr = inputs[i];
|
||||
for (uint256 j = 0; j < arr.length; j++) {
|
||||
if (k >= outLength) revert InvalidPubSignalsLength();
|
||||
out[k++] = arr[j];
|
||||
}
|
||||
}
|
||||
if (k != outLength) revert InvalidPubSignalsLength();
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@ src = './'
|
||||
out = 'out'
|
||||
allow_paths = ['../../node_modules']
|
||||
libs = ['../../node_modules']
|
||||
solc_version = '0.8.26'
|
||||
solc_version = '0.8.30'
|
||||
|
||||
@@ -7,17 +7,18 @@
|
||||
"publish": "yarn npm publish --access=public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^5.0.0",
|
||||
"@openzeppelin/contracts-upgradeable": "^5.0.0",
|
||||
"@openzeppelin/contracts": "5.3.0",
|
||||
"@openzeppelin/contracts-upgradeable": "5.3.0",
|
||||
"dotenv": "^16.3.1"
|
||||
},
|
||||
"files": [
|
||||
"DKIMRegistry.sol",
|
||||
"UserOverrideableDKIMRegistry.sol",
|
||||
"CircuitUtils.sol",
|
||||
"/utils",
|
||||
"/interfaces"
|
||||
],
|
||||
"devDependencies": {
|
||||
"forge-std": "https://github.com/foundry-rs/forge-std"
|
||||
"forge-std": "https://github.com/foundry-rs/forge-std#v1.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@openzeppelin/contracts=../../node_modules/@openzeppelin/contracts
|
||||
@openzeppelin=../../node_modules/@openzeppelin/contracts
|
||||
@openzeppelin/contracts-upgradeable/=../../node_modules/@openzeppelin/contracts-upgradeable
|
||||
forge-std=../../node_modules/forge-std
|
||||
|
||||
@zk-email/email-tx-builder/=../../node_modules/@zk-email/email-tx-builder/packages/contracts/
|
||||
forge-std/=../../node_modules/forge-std/src
|
||||
90
packages/contracts/test/CircuitUtils/FlattenFields.t.sol
Normal file
90
packages/contracts/test/CircuitUtils/FlattenFields.t.sol
Normal 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, 60);
|
||||
}
|
||||
|
||||
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, 60);
|
||||
}
|
||||
|
||||
function test_zeroArrays() public {
|
||||
uint256[][] memory inputs = new uint256[][](0);
|
||||
vm.expectRevert(CircuitUtils.InvalidPubSignalsLength.selector);
|
||||
_helper.callFlattenFields(inputs, 60);
|
||||
}
|
||||
|
||||
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[] memory result = _helper.callFlattenFields(inputs, 60);
|
||||
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[] memory result = _helper.callFlattenFields(inputs, 60);
|
||||
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[] memory result = _helper.callFlattenFields(inputs, 60);
|
||||
for (uint256 i = 0; i < 60; i++) {
|
||||
assertEq(result[i], i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
124
packages/contracts/test/CircuitUtils/PackBytes2Fields.t.sol
Normal file
124
packages/contracts/test/CircuitUtils/PackBytes2Fields.t.sol
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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 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 {
|
||||
bytes memory data = "This is a longer string that should revert";
|
||||
vm.expectRevert(CircuitUtils.InvalidDataLength.selector);
|
||||
_helper.callPackBytes2Fields(data, 10);
|
||||
}
|
||||
}
|
||||
130
packages/contracts/test/CircuitUtils/UnpackFields2Bytes.t.sol
Normal file
130
packages/contracts/test/CircuitUtils/UnpackFields2Bytes.t.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
26
packages/contracts/test/CircuitUtils/_CircuitUtilsHelper.sol
Normal file
26
packages/contracts/test/CircuitUtils/_CircuitUtilsHelper.sol
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.30;
|
||||
|
||||
import { CircuitUtils } from "../../CircuitUtils.sol";
|
||||
|
||||
contract CircuitUtilsHelper {
|
||||
function callFlattenFields(uint256[][] memory inputs, uint256 outLength) external pure returns (uint256[] memory) {
|
||||
return CircuitUtils.flattenFields(inputs, outLength);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
pragma solidity ^0.8.12;
|
||||
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
import "forge-std/src/Test.sol";
|
||||
import "forge-std/src/console.sol";
|
||||
import "forge-std/Test.sol";
|
||||
import "forge-std/console.sol";
|
||||
import "../interfaces/IERC7969.sol";
|
||||
import "../DKIMRegistry.sol";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.12;
|
||||
|
||||
import "forge-std/src/Test.sol";
|
||||
import "forge-std/src/console.sol";
|
||||
import "forge-std/Test.sol";
|
||||
import "forge-std/console.sol";
|
||||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "../UserOverrideableDKIMRegistry.sol";
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
@@ -738,27 +738,27 @@ contract UserOverrideableDKIMRegistryTest is Test {
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testExpectRevertPublicKeyHashIsAlreadyRevokedByUser1() public {
|
||||
testRevokeDKIMPublicKeyHashByUser1();
|
||||
vm.startPrank(user1);
|
||||
function testExpectRevertPublicKeyHashIsAlreadyRevokedByUser1() public {
|
||||
testRevokeDKIMPublicKeyHashByUser1();
|
||||
vm.startPrank(user1);
|
||||
|
||||
vm.expectRevert("public key hash is already revoked");
|
||||
registry.setDKIMPublicKeyHash(
|
||||
domainName,
|
||||
publicKeyHash,
|
||||
user1,
|
||||
new bytes(0)
|
||||
);
|
||||
vm.expectRevert("public key hash is already revoked");
|
||||
registry.setDKIMPublicKeyHash(
|
||||
domainName,
|
||||
publicKeyHash,
|
||||
user1,
|
||||
new bytes(0)
|
||||
);
|
||||
|
||||
vm.expectRevert("public key hash is already revoked");
|
||||
registry.revokeDKIMPublicKeyHash(
|
||||
domainName,
|
||||
publicKeyHash,
|
||||
user1,
|
||||
new bytes(0)
|
||||
);
|
||||
vm.stopPrank();
|
||||
}
|
||||
vm.expectRevert("public key hash is already revoked");
|
||||
registry.revokeDKIMPublicKeyHash(
|
||||
domainName,
|
||||
publicKeyHash,
|
||||
user1,
|
||||
new bytes(0)
|
||||
);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testExpectRevertReactivateDKIMPublicKeyHashByMainAuthorizer()
|
||||
public
|
||||
|
||||
@@ -65,7 +65,7 @@ export function generatePartialSHA({
|
||||
throw new Error('Remaining body was not padded correctly with int64s');
|
||||
}
|
||||
|
||||
bodyRemaining = padUint8ArrayWithZeros(bodyRemaining, maxRemainingBodyLength);
|
||||
bodyRemaining = new Uint8Array(padUint8ArrayWithZeros(bodyRemaining, maxRemainingBodyLength));
|
||||
const precomputedSha = partialSha(precomputeText, shaCutoffIndex);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user