Skip to content

Commit

Permalink
Rename FFT to NTT
Browse files Browse the repository at this point in the history
"Number theoretic transform" is the term preferred by most
cryptographers.

Also, remove an out-of-date comment related to NTT and padding.
  • Loading branch information
cjpatton committed Aug 20, 2024
1 parent 8736056 commit e37da53
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 41 deletions.
31 changes: 18 additions & 13 deletions draft-irtf-cfrg-vdaf.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,15 @@ informative:
date: 2020
target: https://web.archive.org/web/20221025174046/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/collection/origin.html

SML24:
title: "A Complete Beginner Guide to the Number Theoretic Transform (NTT)"
target: https://eprint.iacr.org/2024/585
author:
- ins: A. Satriawan
- ins: R. Mareta
- ins: H. Lee
date: 2024

--- abstract

This document describes Verifiable Distributed Aggregation Functions (VDAFs), a
Expand Down Expand Up @@ -1942,22 +1951,22 @@ def vec_neg(vec: list[F]) -> list[F]:
~~~
{: #field-helper-functions title="Common functions for finite fields."}

### FFT-Friendly Fields {#field-fft-friendly}
### NTT-Friendly Fields {#field-ntt-friendly}

Some VDAFs require fields that are suitable for efficient computation of the
discrete Fourier transform, as this allows for fast polynomial interpolation.
(One example is Prio3 ({{prio3}}) when instantiated with the FLP of
{{flp-bbcggi19-construction}}.) Specifically, a field is said to be
"FFT-friendly" if, in addition to satisfying the interface described in
number theoretic transform (NTT) {{SML24}}, as this allows for fast polynomial
interpolation. (One example is Prio3 ({{prio3}}) when instantiated with the FLP
of {{flp-bbcggi19-construction}}.) Specifically, a field is said to be
"NTT-friendly" if, in addition to satisfying the interface described in
{{field}}, it implements the following method:

* `Field.gen() -> Field` returns the generator of a large subgroup of the
multiplicative group. To be FFT-friendly, the order of this subgroup MUST be a
multiplicative group. To be NTT-friendly, the order of this subgroup MUST be a
power of 2. In addition, the size of the subgroup dictates how large
interpolated polynomials can be. It is RECOMMENDED that a generator is chosen
with order at least `2^20`.

FFT-friendly fields also define the following parameter:
NTT-friendly fields also define the following parameter:

* `GEN_ORDER: int` is the order of a multiplicative subgroup generated by
`Field.gen()`.
Expand Down Expand Up @@ -2445,7 +2454,7 @@ of proofs.
## Construction {#prio3-construction}

This section specifies `Prio3`, an implementation of the `Vdaf` interface
({{vdaf}}). It has three generic parameters: an `FftField ({{fft-field}}), an
({{vdaf}}). It has three generic parameters: an `NttField ({{ntt-field}}), an
`Flp` ({{flp}}) and a `Xof` ({{xof}}). It also has an associated constant,
`PROOFS`, with a value within the range of `[1, 256)`, denoting the number of
FLPs generated by the Client ({{multiproofs}}).
Expand Down Expand Up @@ -3294,7 +3303,7 @@ fixed points `alpha[0], ..., alpha[M-1]`, other than to require that the points
are distinct. In this document, the fixed points are chosen so that the gadget
polynomial can be constructed efficiently using the Cooley-Tukey FFT ("Fast
Fourier Transform") algorithm. Note that this requires the field to be
"FFT-friendly" as defined in {{field-fft-friendly}}.
"NTT-friendly" as defined in {{field-ntt-friendly}}.

Finally, the validity circuit in our FLP may have any number of outputs (at
least one). The input is said to be valid if each of the outputs is zero. To
Expand Down Expand Up @@ -3446,10 +3455,6 @@ follows:

* Let `padded_w = w + field.zeros(P_i - len(w))`.

> NOTE We pad `w` to the nearest power of 2 so that we can use FFT for
> interpolating the wire polynomials. Perhaps there is some clever math for
> picking `wire_inp` in a way that avoids having to pad.

* Let `poly_wire_i[j-1]` be the lowest degree polynomial for which
`poly_wire_i[j-1](alpha_i^k) == padded_w[k]` for all `k` in `[P_i]`.

Expand Down
12 changes: 6 additions & 6 deletions poc/tests/test_field.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import unittest

from vdaf_poc.field import (FftField, Field, Field2, Field64, Field96,
Field128, Field255, poly_eval, poly_interp)
from vdaf_poc.field import (Field, Field2, Field64, Field96, Field128,
Field255, NttField, poly_eval, poly_interp)


class TestFields(unittest.TestCase):
Expand Down Expand Up @@ -41,20 +41,20 @@ def run_field_test(self, cls: type[Field]) -> None:
self.assertTrue(cls.decode_from_bit_vector(
encoded).as_unsigned() == val)

def run_fft_field_test(self, cls: type[FftField]) -> None:
def run_ntt_field_test(self, cls: type[NttField]) -> None:
self.run_field_test(cls)

# Test generator.
self.assertTrue(cls.gen()**cls.GEN_ORDER == cls(1))

def test_field64(self) -> None:
self.run_fft_field_test(Field64)
self.run_ntt_field_test(Field64)

def test_field96(self) -> None:
self.run_fft_field_test(Field96)
self.run_ntt_field_test(Field96)

def test_field128(self) -> None:
self.run_fft_field_test(Field128)
self.run_ntt_field_test(Field128)

def test_field255(self) -> None:
self.run_field_test(Field255)
Expand Down
10 changes: 5 additions & 5 deletions poc/tests/test_flp_bbcggi19.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import TypeVar

from vdaf_poc.field import FftField, Field64, Field96, Field128
from vdaf_poc.field import Field64, Field96, Field128, NttField
from vdaf_poc.flp_bbcggi19 import (Count, FlpBBCGGI19, Histogram, Mul,
MultihotCountVec, PolyEval, Range2, Sum,
SumOfRangeCheckedInputs, SumVec, Valid)
from vdaf_poc.test_utils import TestFlpBBCGGI19

Measurement = TypeVar("Measurement")
AggResult = TypeVar("AggResult")
F = TypeVar("F", bound=FftField)
F = TypeVar("F", bound=NttField)


class MultiGadget(Valid[int, int, Field64]):
Expand Down Expand Up @@ -140,22 +140,22 @@ def test_small(self) -> None:


class TestSumVec(TestFlpBBCGGI19):
def run_encode_truncate_decode_with_fft_fields_test(
def run_encode_truncate_decode_with_ntt_fields_test(
self,
measurements: list[list[int]],
length: int,
bits: int,
chunk_length: int) -> None:
for field in [Field64, Field96, Field128]:
sumvec = SumVec[FftField](field, length, bits, chunk_length)
sumvec = SumVec[NttField](field, length, bits, chunk_length)
self.assertEqual(sumvec.field, field)
self.assertTrue(isinstance(sumvec, SumVec))
self.run_encode_truncate_decode_test(
FlpBBCGGI19(sumvec), measurements)

def test(self) -> None:
# SumVec with length 2, bits 4, chunk len 1.
self.run_encode_truncate_decode_with_fft_fields_test(
self.run_encode_truncate_decode_with_ntt_fields_test(
[[1, 2], [3, 4], [5, 6], [7, 8]],
2,
4,
Expand Down
4 changes: 2 additions & 2 deletions poc/tests/test_vdaf_prio3.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

from tests.test_flp import FlpTest
from tests.test_flp_bbcggi19 import TestAverage
from vdaf_poc.field import FftField, Field64, Field128
from vdaf_poc.field import Field64, Field128, NttField
from vdaf_poc.flp_bbcggi19 import FlpBBCGGI19
from vdaf_poc.test_utils import TestVdaf
from vdaf_poc.vdaf_prio3 import (Prio3, Prio3Count, Prio3Histogram,
Prio3MultihotCountVec, Prio3Sum, Prio3SumVec,
Prio3SumVecWithMultiproof)
from vdaf_poc.xof import XofTurboShake128

F = TypeVar("F", bound=FftField)
F = TypeVar("F", bound=NttField)


class Prio3Average(Prio3):
Expand Down
14 changes: 10 additions & 4 deletions poc/vdaf_poc/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,13 @@ def as_unsigned(self) -> int:
return int(self.gf(self.val))


class FftField(Field):
class NttField(Field):
"""
A field that is suitable for use with the NTT ("number theoretic
transform") algorithm for efficient polynomial interpolation. Such a field
defines a large multiplicative subgroup whose order is a power of 2.
"""

# Order of the multiplicative group generated by `Field.gen()`.
GEN_ORDER: int

Expand Down Expand Up @@ -188,7 +194,7 @@ def conditional_select(self, inp: bytes) -> bytes:
return bytes(map(lambda x: m & x, inp))


class Field64(FftField):
class Field64(NttField):
"""The finite field GF(2^32 * 4294967295 + 1)."""

MODULUS = 2**32 * 4294967295 + 1
Expand All @@ -203,7 +209,7 @@ def gen(cls) -> Self:
return cls(7)**4294967295


class Field96(FftField):
class Field96(NttField):
"""The finite field GF(2^64 * 4294966555 + 1)."""

MODULUS = 2**64 * 4294966555 + 1
Expand All @@ -218,7 +224,7 @@ def gen(cls) -> Self:
return cls(3)**4294966555


class Field128(FftField):
class Field128(NttField):
"""The finite field GF(2^66 * 4611686018427387897 + 1)."""

MODULUS = 2**66 * 4611686018427387897 + 1
Expand Down
12 changes: 5 additions & 7 deletions poc/vdaf_poc/flp_bbcggi19.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
from typing import Any, Generic, Optional, TypeVar, cast

from vdaf_poc.common import front, next_power_of_2
from vdaf_poc.field import (FftField, poly_eval, poly_interp, poly_mul,
from vdaf_poc.field import (NttField, poly_eval, poly_interp, poly_mul,
poly_strip)
from vdaf_poc.flp import Flp

Measurement = TypeVar("Measurement")
AggResult = TypeVar("AggResult")
F = TypeVar("F", bound=FftField)
F = TypeVar("F", bound=NttField)


class Gadget(Generic[F], metaclass=ABCMeta):
Expand Down Expand Up @@ -52,10 +52,10 @@ class Valid(Generic[Measurement, AggResult, F], metaclass=ABCMeta):
Generic type parameters:
Measurement -- the measurement type
AggResult -- the aggregate result type
Field -- An FFT-friendly field
Field -- An NTT-friendly field
Attributes:
field -- Class object for the FFT-friendly field.
field -- Class object for the NTT-friendly field.
MEAS_LEN -- Length of the encoded measurement input to the validity
circuit.
JOINT_RAND_LEN -- Length of the random input of the validity circuit.
Expand Down Expand Up @@ -320,9 +320,7 @@ def prove(self, meas: list[F], prove_rand: list[F], joint_rand: list[F]) -> list
# Compute the wire polynomials for this gadget.
#
# NOTE We pad the wire inputs to the nearest power of 2 so that we
# can use FFT for interpolating the wire polynomials. Perhaps there
# is some clever math for picking `wire_inp` in a way that avoids
# having to pad.
# can use NTT for interpolating the wire polynomials.
assert self.field.GEN_ORDER % P == 0
alpha = self.field.gen() ** (self.field.GEN_ORDER // P)
wire_inp = [alpha ** k for k in range(P)]
Expand Down
4 changes: 2 additions & 2 deletions poc/vdaf_poc/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from vdaf_poc.common import (gen_rand, next_power_of_2, print_wrapped_line,
to_le_bytes)
from vdaf_poc.field import FftField, poly_eval
from vdaf_poc.field import NttField, poly_eval
from vdaf_poc.flp import Flp, run_flp
from vdaf_poc.flp_bbcggi19 import FlpBBCGGI19, Gadget
from vdaf_poc.vdaf import Vdaf, run_vdaf
Expand All @@ -20,7 +20,7 @@
PrepState = TypeVar("PrepState")
PrepShare = TypeVar("PrepShare")
PrepMessage = TypeVar("PrepMessage")
F = TypeVar("F", bound=FftField)
F = TypeVar("F", bound=NttField)


def test_vec_gen_rand(length: int) -> bytes:
Expand Down
4 changes: 2 additions & 2 deletions poc/vdaf_poc/vdaf_prio3.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from vdaf_poc import flp_bbcggi19
from vdaf_poc.common import byte, concat, front, vec_add, vec_sub, zeros
from vdaf_poc.field import FftField, Field64, Field128
from vdaf_poc.field import Field64, Field128, NttField
from vdaf_poc.flp import Flp
from vdaf_poc.vdaf import Vdaf
from vdaf_poc.xof import Xof, XofTurboShake128
Expand All @@ -20,7 +20,7 @@

Measurement = TypeVar("Measurement")
AggResult = TypeVar("AggResult")
F = TypeVar("F", bound=FftField)
F = TypeVar("F", bound=NttField)

Prio3InputShare: TypeAlias = \
tuple[ # leader input share
Expand Down

0 comments on commit e37da53

Please sign in to comment.