mirror of
https://github.com/zkonduit/ezkl.git
synced 2026-01-14 08:48:01 -05:00
136 lines
6.1 KiB
Solidity
136 lines
6.1 KiB
Solidity
// SPDX-License-Identifier: GPL-3.0
|
|
|
|
pragma solidity ^0.8.17;
|
|
|
|
contract QuantizeData {
|
|
/**
|
|
* @notice EZKL P value
|
|
* @dev In order to prevent the verifier from accepting two version of the same instance, n and the quantity (n + P), where n + P <= 2^256, we require that all instances are stricly less than P. a
|
|
* @dev The reason for this is that the assmebly code of the verifier performs all arithmetic operations modulo P and as a consequence can't distinguish between n and n + P.
|
|
*/
|
|
uint256 constant ORDER =
|
|
uint256(
|
|
0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
|
|
);
|
|
|
|
/**
|
|
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
|
|
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
|
|
* with further edits by Uniswap Labs also under MIT license.
|
|
*/
|
|
function mulDiv(
|
|
uint256 x,
|
|
uint256 y,
|
|
uint256 denominator
|
|
) internal pure returns (uint256 result) {
|
|
unchecked {
|
|
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
|
|
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
|
|
// variables such that product = prod1 * 2^256 + prod0.
|
|
uint256 prod0; // Least significant 256 bits of the product
|
|
uint256 prod1; // Most significant 256 bits of the product
|
|
assembly {
|
|
let mm := mulmod(x, y, not(0))
|
|
prod0 := mul(x, y)
|
|
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
|
|
}
|
|
|
|
// Handle non-overflow cases, 256 by 256 division.
|
|
if (prod1 == 0) {
|
|
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
|
|
// The surrounding unchecked block does not change this fact.
|
|
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
|
|
return prod0 / denominator;
|
|
}
|
|
|
|
// Make sure the result is less than 2^256. Also prevents denominator == 0.
|
|
require(denominator > prod1, "Math: mulDiv overflow");
|
|
|
|
///////////////////////////////////////////////
|
|
// 512 by 256 division.
|
|
///////////////////////////////////////////////
|
|
|
|
// Make division exact by subtracting the remainder from [prod1 prod0].
|
|
uint256 remainder;
|
|
assembly {
|
|
// Compute remainder using mulmod.
|
|
remainder := mulmod(x, y, denominator)
|
|
|
|
// Subtract 256 bit number from 512 bit number.
|
|
prod1 := sub(prod1, gt(remainder, prod0))
|
|
prod0 := sub(prod0, remainder)
|
|
}
|
|
|
|
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
|
|
// See https://cs.stackexchange.com/q/138556/92363.
|
|
|
|
// Does not overflow because the denominator cannot be zero at this stage in the function.
|
|
uint256 twos = denominator & (~denominator + 1);
|
|
assembly {
|
|
// Divide denominator by twos.
|
|
denominator := div(denominator, twos)
|
|
|
|
// Divide [prod1 prod0] by twos.
|
|
prod0 := div(prod0, twos)
|
|
|
|
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
|
|
twos := add(div(sub(0, twos), twos), 1)
|
|
}
|
|
|
|
// Shift in bits from prod1 into prod0.
|
|
prod0 |= prod1 * twos;
|
|
|
|
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
|
|
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
|
|
// four bits. That is, denominator * inv = 1 mod 2^4.
|
|
uint256 inverse = (3 * denominator) ^ 2;
|
|
|
|
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
|
|
// in modular arithmetic, doubling the correct bits in each step.
|
|
inverse *= 2 - denominator * inverse; // inverse mod 2^8
|
|
inverse *= 2 - denominator * inverse; // inverse mod 2^16
|
|
inverse *= 2 - denominator * inverse; // inverse mod 2^32
|
|
inverse *= 2 - denominator * inverse; // inverse mod 2^64
|
|
inverse *= 2 - denominator * inverse; // inverse mod 2^128
|
|
inverse *= 2 - denominator * inverse; // inverse mod 2^256
|
|
|
|
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
|
|
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
|
|
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
|
|
// is no longer required.
|
|
result = prod0 * inverse;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
function quantize_data(
|
|
bytes[] memory data,
|
|
uint256[] memory decimals,
|
|
uint256[] memory scales
|
|
) external pure returns (int256[] memory quantized_data) {
|
|
quantized_data = new int256[](data.length);
|
|
for (uint i; i < data.length; i++) {
|
|
int x = abi.decode(data[i], (int256));
|
|
bool neg = x < 0;
|
|
if (neg) x = -x;
|
|
uint denom = 10 ** decimals[i];
|
|
uint scale = 1 << scales[i];
|
|
uint output = mulDiv(uint256(x), scale, denom);
|
|
if (mulmod(uint256(x), scale, denom) * 2 >= denom) {
|
|
output += 1;
|
|
}
|
|
|
|
quantized_data[i] = neg ? -int256(output) : int256(output);
|
|
}
|
|
}
|
|
|
|
function to_field_element(
|
|
int128[] memory quantized_data
|
|
) public pure returns (uint256[] memory output) {
|
|
output = new uint256[](quantized_data.length);
|
|
for (uint i; i < quantized_data.length; i++) {
|
|
output[i] = uint256(quantized_data[i] + int(ORDER)) % ORDER;
|
|
}
|
|
}
|
|
}
|