Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,5 @@ coverage
*~

*temp

.act.env
1 change: 1 addition & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"compact:security": "compact-compiler --dir security",
"compact:token": "compact-compiler --dir token",
"compact:utils": "compact-compiler --dir utils",
"compact:math": "compact-compiler --dir math",
"build": "compact-builder",
"test": "compact-compiler --skip-zk && vitest run",
"types": "tsc -p tsconfig.json --noEmit",
Expand Down
127 changes: 127 additions & 0 deletions contracts/src/math/Bytes8.compact
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Midnight Apps Contracts v0.0.1-alpha.0 (math/Bytes8.compact)

pragma language_version >= 0.21.0;

/**
* @title Bytes8 module
* @description Canonical implementation for converting 8 bytes (as Vector or Bytes) to Uint<64>
* using little-endian (LE) byte ordering.
*
* @remarks
* Byte ordering convention (little-endian):
* - Element 0 is the least significant byte, element 7 is the most significant.
* - Works with Vector<8, Uint<8>> (e.g. from Uint64.toUnpackedBytes, or slices of Bytes32).
*
* Supported Operations:
* - Conversions:
* - pack(vec): Converts Vector<8, Uint<8>> to Bytes<8>.
* - unpack(bytes): Converts Bytes<8> to Vector<8, Uint<8>>.
* - toUint64(vec): Converts Vector<8, Uint<8>> to Uint<64>.
* - toUint64(bytes): Converts Bytes<8> to Uint<64>.
*/
module Bytes8 {
import { pack, unpack } from Pack<8> prefix Pack8_;

////////////////////////////////////////////////////////////////
// Conversions
////////////////////////////////////////////////////////////////

/**
* @title pack circuit
* @description Packs a Vector<8, Uint<8>> into a Bytes<8>.
*
* @remarks
* This circuit converts an 8-element vector of bytes to an 8-byte array
* using little-endian byte ordering.
*
* @circuitInfo k=12, rows=2412
*
* @param {Vector<8, Uint<8>>} vec - The vector of 8 bytes to convert.
*
* @returns {Bytes<8>} - The 8-byte array.
*/
export circuit pack(vec: Vector<8, Uint<8>>): Bytes<8> {
return Pack8_pack(vec);
}

/**
* @title unpack circuit
* @description Unpacks a Bytes<8> into a Vector<8, Uint<8>>.
*
* @remarks
* This circuit converts an 8-byte array to an 8-element vector of bytes
* using little-endian byte ordering.
*
* @circuitInfo k=12, rows=2486
*
* @param {Bytes<8>} bytes - The 8-byte array to convert.
*
* @returns {Vector<8, Uint<8>>} - The 8-element vector of bytes.
*/
export circuit unpack(bytes: Bytes<8>): Vector<8, Uint<8>> {
return Pack8_unpack(bytes);
}

/**
* @title _vector8ToUint64 internal circuit
* @description Converts a Vector<8, Uint<8>> to a Uint<64> using pure arithmetic.
*
* @remarks
* This circuit converts an 8-element vector of bytes to a 64-bit unsigned integer
* using little-endian byte ordering (element 0 is the LSB, element 7 is the MSB).
*
* @circuitInfo k=8, rows=193
*
* @param {Vector<8, Uint<8>>} vec - The vector of 8 bytes to convert.
*
* @returns {Uint<64>} - The 64-bit unsigned integer.
*/
pure circuit _toUint64(vec: Vector<8, Uint<8>>): Uint<64> {
return vec[0] +
vec[1] * 0x100 +
vec[2] * 0x10000 +
vec[3] * 0x1000000 +
vec[4] * 0x100000000 +
vec[5] * 0x10000000000 +
vec[6] * 0x1000000000000 +
vec[7] * 0x100000000000000;
}

/**
* @title toUint64 circuit (from Vector)
* @description Converts a Vector<8, Uint<8>> to a Uint<64> using pure arithmetic.
*
* @remarks
* This circuit converts an 8-element vector of bytes to a 64-bit unsigned integer
* using little-endian byte ordering (element 0 is the LSB, element 7 is the MSB).
*
* @circuitInfo k=8, rows=193
*
* @param {Vector<8, Uint<8>>} vec - The vector of 8 bytes to convert.
*
* @returns {Uint<64>} - The 64-bit unsigned integer.
*/
export pure circuit toUint64(vec: Vector<8, Uint<8>>): Uint<64> {
return _toUint64(vec);
}

/**
* @title toUint64 circuit (from Bytes)
* @description Converts a Bytes<8> to a Uint<64>.
*
* @remarks
* This circuit converts an 8-byte array to a 64-bit unsigned integer.
* The conversion is done by first unpacking to a vector via `unpack`,
* then converting to Uint<64>.
*
* @circuitInfo k=12, rows=2433
*
* @param {Bytes<8>} bytes - The 8-byte array to convert.
*
* @returns {Uint<64>} - The 64-bit unsigned integer.
*/
export circuit toUint64(bytes: Bytes<8>): Uint<64> {
return _toUint64(unpack(bytes));
}
}
74 changes: 74 additions & 0 deletions contracts/src/math/Pack.compact
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Midnight Apps Contracts v0.0.1-alpha.0 (math/Pack.compact)

pragma language_version >= 0.21.0;

/**
* @title Pack module
* @description Generic module for packing and unpacking between Vector<N, Uint<8>> and Bytes<N>
* for any byte length N. Uses little-endian byte ordering (element 0 is the LSB).
*
* @remarks
* This module has no dependencies and exists so that BytesN and UintN modules can import
* pack/unpack without creating cyclic dependencies (e.g. Bytes32 needs Uint256_lt for
* comparisons while Uint256 needs pack for toBytes).
*
* Instantiate with a size parameter, e.g. Pack<8>, Pack<16>, Pack<32>.
*
* Supported Operations:
* - pack(vec): Converts Vector<N, Uint<8>> to Bytes<N>.
* - unpack(bytes): Converts Bytes<N> to Vector<N, Uint<8>> (uses witness, then verifies in-circuit).
*/
module Pack<#N> {
/**
* @title wit_unpackBytes witness
* @description Unpacks Bytes<N> into Vector<N, Uint<8>> off-chain. Implementation is supplied
* in TypeScript; the unpack circuit verifies the result by re-packing and asserting equality.
*
* @param bytes - The byte array to unpack.
* @returns A vector of N bytes where element 0 is the LSB.
*/
witness wit_unpackBytes(bytes: Bytes<N>): Vector<N, Uint<8>>;

/**
* @title pack circuit
* @description Packs a Vector<N, Uint<8>> into Bytes<N>.
*
* @remarks
* This pure circuit converts an N-element vector of bytes to an N-byte array
* using little-endian byte ordering.
*
* @circuitInfo depends on N:
* - Pack<8>: k=12, rows=2412
* - Pack<16>: k=13, rows=5118
* - Pack<32>: k=14, rows=10231
*
* @param vec - The vector of N bytes to convert.
* @returns The byte array of length N.
*/
export pure circuit pack(vec: Vector<N, Uint<8>>): Bytes<N> {
return Bytes[...vec];
}

/**
* @title unpack circuit
* @description Unpacks Bytes<N> into a Vector<N, Uint<8>>.
*
* @remarks
* Calls the wit_unpackBytes witness off-chain, then verifies in-circuit that
* pack(vec) equals the input bytes.
*
* @circuitInfo depends on N:
* - Pack<8>: k=12, rows=2486
* - Pack<16>: k=13, rows=5262
* - Pack<32>: k=14, rows=10521
*
* @param bytes - The byte array to unpack.
* @returns The vector of N bytes (element 0 is the LSB).
*/
export circuit unpack(bytes: Bytes<N>): Vector<N, Uint<8>> {
const vec = wit_unpackBytes(bytes);
assert(pack(vec) == bytes, "Pack: unpack verification failed");
return vec;
}
}
49 changes: 49 additions & 0 deletions contracts/src/math/Types.compact
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Midnight Apps Contracts v0.0.1-alpha.0 (math/types/Types.compact)

pragma language_version >= 0.20.0;

/**
* @title Types
* @dev Shared type definitions for U128 and U256 structs, used by Uint128 and Uint256 modules.
*
* These types exist in a separate module to avoid cyclic dependencies between Uint128 and
* Uint256. U256 is used as a return type for Uint128 operations that may overflow 128 bits
* (e.g. addition, multiplication), while U256 itself is composed of two U128 values.
*
* Import chain:
* - Types has no external dependencies (base types)
* - Uint128 imports U128 and U256 from Types
* - Uint256 imports U128 and U256 from Types
*/
module Types {
/**
* @description A struct representing a 128-bit unsigned integer as two 64-bit parts.
* The value is computed as: high * 2^64 + low
*/
export struct U128 {
/**
* @description The least significant 64 bits (bits 0-63) of the 128-bit number
*/
low: Uint<64>,
/**
* @description The most significant 64 bits (bits 64-127) of the 128-bit number
*/
high: Uint<64>
}

/**
* @description A struct representing a 256-bit unsigned integer as two 128-bit parts.
* The value is computed as: high * 2^128 + low
*/
export struct U256 {
/**
* @description The least significant 128 bits (bits 0-127) of the 256-bit number
*/
low: U128,
/**
* @description The most significant 128 bits (bits 128-255) of the 256-bit number
*/
high: U128
}
}
Loading
Loading