Skip to content

Commit

Permalink
Merge pull request #248 from privacy-scaling-explorations/fix/field-f1
Browse files Browse the repository at this point in the history
More comments for explicit mod reduction on inputs
  • Loading branch information
cedoor authored Apr 17, 2024
2 parents 4e6d3b2 + a79dfc4 commit a2df127
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 71 deletions.
18 changes: 14 additions & 4 deletions packages/utils/src/f1-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import * as scalar from "./scalar"
* and inversion, all performed modulo the field's order. It's designed to work with bigints,
* supporting large numbers for cryptographic purposes and other applications requiring
* modular arithmetic.
* Note that the outputs of the functions will always be within the field if and only if
* the input values are within the field. Devs need to make sure of that.
*
* @property one Represents the scalar value 1 in the field.
* @property zero Represents the scalar value 0 in the field.
Expand All @@ -15,8 +17,8 @@ import * as scalar from "./scalar"
* @property _negone The scalar value -1 in the field, represented positively.
*/
export default class F1Field {
one = BigInt(1)
zero = BigInt(0)
one = 1n
zero = 0n

_order: bigint
_half: bigint
Expand All @@ -36,6 +38,7 @@ export default class F1Field {
*/
e(res: bigint): bigint {
res %= this._order

return res < 0 ? res + this._order : res
}

Expand All @@ -50,7 +53,8 @@ export default class F1Field {
}

/**
* Subtracts one bigint from another under modulus, ensuring the result is within the field.
* Subtracts one bigint from another under modulus.
* It ensures the result is within the field if and only if the input values are within the field.
* @param a The value from which to subtract.
* @param b The value to be subtracted.
* @returns The difference of 'a' and 'b' modulo the field's order.
Expand All @@ -60,7 +64,8 @@ export default class F1Field {
}

/**
* Adds two bigint values together under modulus, ensuring the result is within the field.
* Adds two bigint values together under modulus.
* It ensures the result is within the field if and only if the input values are within the field.
* @param a The first value.
* @param b The second value.
* @returns The sum of 'a' and 'b' modulo the field's order.
Expand Down Expand Up @@ -112,6 +117,7 @@ export default class F1Field {

/**
* Checks if two bigint values are equal within the context of the field.
* It ensures the result is within the field if and only if the input values are within the field.
* @param a The first value to compare.
* @param b The second value to compare.
* @returns True if 'a' equals 'b', false otherwise.
Expand All @@ -124,6 +130,7 @@ export default class F1Field {
* Squares a bigint value within the field.
* This is a specific case of multiplication where the value is multiplied by itself,
* optimized for performance where applicable.
* It ensures the result is within the field if and only if the input values are within the field.
* @param a The value to square.
* @returns The square of 'a' modulo the field's order.
*/
Expand All @@ -134,6 +141,7 @@ export default class F1Field {
/**
* Compares two bigint values to determine if the first is less than the second,
* taking into account the field's order for modular comparison.
* It ensures the result is within the field if and only if the input values are within the field.
* @param a The first value to compare.
* @param b The second value to compare.
* @returns True if 'a' is less than 'b', false otherwise.
Expand All @@ -148,6 +156,7 @@ export default class F1Field {
/**
* Compares two bigint values to determine if the first is greater than or equal to the second,
* considering the field's modular context.
* It ensures the result is within the field if and only if the input values are within the field.
* @param a The first value to compare.
* @param b The second value to compare.
* @returns True if 'a' is greater than or equal to 'b', false otherwise.
Expand All @@ -163,6 +172,7 @@ export default class F1Field {
* Computes the negation of a bigint value within the field.
* The result is the modular additive inverse that, when added to the original value,
* yields zero in the field's modulus.
* It ensures the result is within the field if and only if the input values are within the field.
* @param a The value to negate.
* @returns The negation of 'a' modulo the field's order.
*/
Expand Down
136 changes: 69 additions & 67 deletions packages/utils/tests/f1-field.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,120 +4,122 @@ describe("F1Field", () => {
let field: F1Field

beforeEach(() => {
field = new F1Field(BigInt(12))
field = new F1Field(12n)
})

it("Should create a finite field with a specific order", async () => {
expect(field.one).toBe(BigInt(1))
expect(field.zero).toBe(BigInt(0))
expect(field._order).toBe(BigInt(12))
expect(field._half).toBe(BigInt(12) >> BigInt(1))
expect(field._negone).toBe(BigInt(12) - BigInt(1))
it("Should create a finite field with a specific order", () => {
expect(field.one).toBe(1n)
expect(field.zero).toBe(0n)
expect(field._order).toBe(12n)
expect(field._half).toBe(12n >> 1n)
expect(field._negone).toBe(12n - 1n)
})

it("Should map the value back into the finite field", async () => {
const a = field.e(BigInt(24))
const b = field.e(BigInt(-2))
const c = field.e(BigInt(-13))
it("Should map the value back into the finite field", () => {
const a = field.e(24n)
const b = field.e(-2n)
const c = field.e(-13n)

expect(a).toBe(BigInt(0))
expect(b).toBe(BigInt(10))
expect(c).toBe(BigInt(11))
expect(a).toBe(0n)
expect(b).toBe(10n)
expect(c).toBe(11n)
})

it("Should add into the finite field", async () => {
const a = field.e(BigInt(2))
const b = field.e(BigInt(20))
it("Should add into the finite field", () => {
const a = field.e(2n)
const b = field.e(20n)
const c = field.e(12n)

expect(field.add(a, a)).toBe(BigInt(4))
expect(field.add(b, a)).toBe(BigInt(10))
expect(field.add(a, a)).toBe(4n)
expect(field.add(b, a)).toBe(10n)
expect(field.add(c, c)).toBe(0n)
})

it("Should sub into the finite field", async () => {
const a = field.e(BigInt(4))
const b = field.e(BigInt(2))
it("Should sub into the finite field", () => {
const a = field.e(4n)
const b = field.e(2n)

expect(field.sub(a, b)).toBe(BigInt(2))
expect(field.sub(b, a)).toBe(BigInt(10))
expect(field.sub(a, b)).toBe(2n)
expect(field.sub(b, a)).toBe(10n)
})

it("Should mul into the finite field", async () => {
const a = field.e(BigInt(2))
it("Should mul into the finite field", () => {
const a = field.e(2n)

expect(field.mul(a, a)).toBe(BigInt(4))
expect(field.mul(a, a)).toBe(4n)
})

it("Should div into the finite field", async () => {
const a = field.e(BigInt(2))
const b = field.e(BigInt(4))
it("Should div into the finite field", () => {
const a = field.e(2n)
const b = field.e(4n)

expect(field.div(a, b)).toBe(BigInt(2))
expect(field.div(a, b)).toBe(2n)
})

it("Should eq into the finite field", async () => {
const a = field.e(BigInt(2))
const b = field.e(BigInt(3))
it("Should eq into the finite field", () => {
const a = field.e(2n)
const b = field.e(3n)

expect(field.eq(a, a)).toBeTruthy()
expect(field.eq(a, b)).toBeFalsy()
})

it("Should square into the finite field", async () => {
const a = field.e(BigInt(2))
it("Should square into the finite field", () => {
const a = field.e(2n)

expect(field.square(a)).toBe(BigInt(4))
expect(field.square(a)).toBe(4n)
})

it("Should inv into the finite field", async () => {
const a = field.e(BigInt(2))
const b = field.e(BigInt(11))
it("Should inv into the finite field", () => {
const a = field.e(2n)
const b = field.e(11n)

expect(field.inv(a)).toBe(BigInt(1))
expect(field.inv(b)).toBe(BigInt(11))
expect(field.inv(a)).toBe(1n)
expect(field.inv(b)).toBe(11n)
})

it("Should lt into the finite field", async () => {
const a = field.e(BigInt(2))
const b = field.e(BigInt(3))
it("Should lt into the finite field", () => {
const a = field.e(2n)
const b = field.e(3n)

expect(field.lt(a, b)).toBeTruthy()
expect(field.lt(b, a)).toBeFalsy()
})

it("Should geq into the finite field", async () => {
const a = field.e(BigInt(2))
const b = field.e(BigInt(3))
it("Should geq into the finite field", () => {
const a = field.e(2n)
const b = field.e(3n)

expect(field.geq(a, b)).toBeFalsy()
expect(field.geq(b, a)).toBeTruthy()
})

it("Should neg into the finite field", async () => {
const a = field.e(BigInt(2))
const b = field.e(BigInt(-3))
it("Should neg into the finite field", () => {
const a = field.e(2n)
const b = field.e(-3n)

expect(field.neg(a)).toBe(BigInt(10))
expect(field.neg(b)).toBe(BigInt(3))
expect(field.neg(a)).toBe(10n)
expect(field.neg(b)).toBe(3n)
})

it("Should isZero into the finite field", async () => {
const a = field.e(BigInt(0))
const b = field.e(BigInt(1))
it("Should isZero into the finite field", () => {
const a = field.e(0n)
const b = field.e(1n)

expect(field.isZero(a)).toBeTruthy()
expect(field.isZero(b)).toBeFalsy()
})

it("Should pow into the finite field", async () => {
const a = field.e(BigInt(0))
const b = field.e(BigInt(1))
const c = field.e(BigInt(2))
const d = field.e(BigInt(-1))

expect(field.pow(b, a)).toBe(BigInt(1))
expect(field.pow(b, c)).toBe(BigInt(1))
expect(field.pow(a, b)).toBe(BigInt(0))
expect(field.pow(a, d)).toBe(BigInt(0))
expect(field.pow(d, a)).toBe(BigInt(1))
it("Should pow into the finite field", () => {
const a = field.e(0n)
const b = field.e(1n)
const c = field.e(2n)
const d = field.e(-1n)

expect(field.pow(b, a)).toBe(1n)
expect(field.pow(b, c)).toBe(1n)
expect(field.pow(a, b)).toBe(0n)
expect(field.pow(a, d)).toBe(0n)
expect(field.pow(d, a)).toBe(1n)
})
})

0 comments on commit a2df127

Please sign in to comment.