mina-attestations
Version:
Private Attestations on Mina
169 lines • 6.79 kB
JavaScript
import { Field, Int64, Provable, Sign, UInt32, UInt64, UInt8, } from 'o1js';
import { assert } from "../util.js";
export { Numeric, numericMaximumType };
// each of these types is naturally included in the next one
const numericTypeOrder = [UInt8, UInt32, UInt64, Int64, Field];
/**
* Arithmetic and comparison gadgets that work on all pairs of numeric types:
* Field, Int64, UInt64, UInt32, and UInt8.
*
* Example: check if a UInt64 is less than a Field, or compute the sum of a UInt32 and an Int64 (as an Int64).
*
* The general strategy is to convert both inputs to the "bigger" type, and then perform the operation there.
* We also add int64 comparisons which are missing from o1js.
*/
const Numeric = {
/**
* Add two numeric values.
* The returned type is the larger of the two input types, and follows the overflow rules of that type.
*
* Example:
* ```ts
* const sum: UInt32 = Numeric.add(UInt8.from(5), UInt32.from(10));
* ```
*/
add(left, right) {
let [a, b] = convertToMaximum(left, right);
return a.add(b);
},
/**
* Subtract two numeric values.
* The returned type is the larger of the two input types, and follows the overflow rules of that type.
*
* Example:
* ```ts
* const difference: Field = Numeric.subtract(Int64.from(10), Field.from(15));
* ```
*/
subtract(left, right) {
let [a, b] = convertToMaximum(left, right);
return a.sub(b);
},
/**
* Multiply two numeric values.
* The returned type is the larger of the two input types, and follows the overflow rules of that type.
*/
multiply(left, right) {
let [a, b] = convertToMaximum(left, right);
return a.mul(b);
},
/**
* Divide two numeric values.
* The returned type is the larger of the two input types, and follows the overflow rules of that type.
*/
divide(left, right) {
let [a, b] = convertToMaximum(left, right);
return a.div(b);
},
/**
* Compares two numeric values and returns true if `left < right`.
*
* **Warning**: when comparing an `Int64` and a `Field`, negative `Int64` are treated as large numbers close to the field size.
* This is to be consistent with how order of field elements is understood in general.
*/
lessThan(left, right) {
let [a, b] = convertToMaximum(left, right);
if (a instanceof Int64) {
return lessThanInt64(a, b);
}
return a.lessThan(b);
},
/**
* See {@link Numeric.lessThan}.
*/
lessThanOrEqual(left, right) {
let [a, b] = convertToMaximum(left, right);
if (a instanceof Int64) {
return lessThanOrEqualInt64(a, b);
}
return a.lessThanOrEqual(b);
},
/**
* See {@link Numeric.lessThan}.
*/
greaterThan(left, right) {
return Numeric.lessThan(right, left);
},
/**
* See {@link Numeric.lessThanOrEqual}.
*/
greaterThanOrEqual(left, right) {
return Numeric.lessThanOrEqual(right, left);
},
};
function numericMaximumType(left, right) {
let leftTypeIndex = numericTypeOrder.findIndex((t) => left === t);
let rightTypeIndex = numericTypeOrder.findIndex((t) => right === t);
assert(leftTypeIndex !== -1, 'left is not a numeric type');
assert(rightTypeIndex !== -1, 'right is not a numeric type');
return numericTypeOrder[Math.max(leftTypeIndex, rightTypeIndex)];
}
function convertToMaximum(left, right) {
const leftTypeIndex = numericTypeOrder.findIndex((type) => left instanceof type);
const rightTypeIndex = numericTypeOrder.findIndex((type) => right instanceof type);
assert(leftTypeIndex !== -1, 'left is not a numeric type');
assert(rightTypeIndex !== -1, 'right is not a numeric type');
let leftType = numericTypeOrder[leftTypeIndex];
let rightType = numericTypeOrder[rightTypeIndex];
let resultType = numericTypeOrder[Math.max(leftTypeIndex, rightTypeIndex)];
let leftConverted = leftTypeIndex < rightTypeIndex
? resultType === Field
? leftType === Int64
? left.toField()
: leftType.toFields(left)[0]
: resultType === Int64
? leftType === UInt64
? Int64.fromUnsigned(left)
: Int64.fromUnsigned(left.toUInt64())
: resultType === UInt64
? left.toUInt64()
: left.toUInt32()
: left;
let rightConverted = leftTypeIndex > rightTypeIndex
? resultType === Field
? rightType === Int64
? right.toField()
: rightType.toFields(right)[0]
: resultType === Int64
? rightType === UInt64
? Int64.fromUnsigned(right)
: Int64.fromUnsigned(right.toUInt64())
: resultType === UInt64
? right.toUInt64()
: right.toUInt32()
: right;
return [leftConverted, rightConverted];
}
// Int64 comparisons
function lessThanInt64(left, right) {
let magnitudeLessThan = left.magnitude.lessThan(right.magnitude);
let magnitudeEqual = left.magnitude.equals(right.magnitude);
let magnitudeGreaterThan = magnitudeLessThan.not().and(magnitudeEqual.not());
let unequalSign = left.sgn.mul(right.sgn).equals(Sign.minusOne);
let leftNegative = left.sgn.equals(Sign.minusOne);
return Provable.if(unequalSign,
// if the signs are unequal, left < right <=> left < 0
// (this works because sign == -1 guarantees magnitude != 0)
leftNegative,
// if the signs are equal, and
// negative: left < right <=> left.magnitude > right.magnitude
// positive: left < right <=> left.magnitude < right.magnitude
Provable.if(leftNegative, magnitudeGreaterThan, magnitudeLessThan));
}
function lessThanOrEqualInt64(left, right) {
let magnitudeLessThanOrEqual = left.magnitude.lessThanOrEqual(right.magnitude);
let magnitudeEqual = left.magnitude.equals(right.magnitude);
let magnitudeGreaterThanOrEqual = magnitudeLessThanOrEqual
.not()
.or(magnitudeEqual);
let unequalSign = left.sgn.mul(right.sgn).equals(Sign.minusOne);
let leftNegative = left.sgn.equals(Sign.minusOne);
return Provable.if(unequalSign,
// if the signs are unequal, left <= right <=> left < 0
leftNegative,
// if the signs are equal, and
// negative: left <= right <=> left.magnitude >= right.magnitude
// positive: left <= right <=> left.magnitude <= right.magnitude
Provable.if(leftNegative, magnitudeGreaterThanOrEqual, magnitudeLessThanOrEqual));
}
//# sourceMappingURL=gadgets-numeric.js.map