Skip to content

Commit

Permalink
Merge branch 'main' into schoppmp/216
Browse files Browse the repository at this point in the history
  • Loading branch information
schoppmp committed Oct 4, 2023
2 parents 0d8ab48 + 402438c commit f919202
Show file tree
Hide file tree
Showing 40 changed files with 2,986 additions and 2,212 deletions.
1,967 changes: 1,076 additions & 891 deletions draft-irtf-cfrg-vdaf.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion poc/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
test:
sage -python common.py
sage -python field.py
sage -python prg.py
sage -python xof.py
sage -python flp.py
sage -python flp_generic.py
sage -python idpf.py
Expand Down
4 changes: 2 additions & 2 deletions poc/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
TEST_VECTOR = False

# Document version, reved with each draft that contains breaking changes.
VERSION = 6
VERSION = 7

# Primitive types
Bool = bool
Expand Down Expand Up @@ -130,7 +130,7 @@ def front(length, vec):
def format_dst(algo_class: Unsigned,
algo: Unsigned,
usage: Unsigned) -> Bytes:
"""Format PRG domain separation tag for use within a (V)DAF."""
"""Format XOF domain separation tag for use within a (V)DAF."""
return concat([
to_be_bytes(VERSION, 1),
to_be_bytes(algo_class, 1),
Expand Down
65 changes: 36 additions & 29 deletions poc/daf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from functools import reduce

import field
from common import Bool, Bytes, Unsigned, Vec, gen_rand
from prg import PrgSha3
from common import Bool, Unsigned, gen_rand
from xof import XofShake128


class Daf:
Expand All @@ -30,9 +30,18 @@ class Daf:
# The aggregation parameter type.
AggParam = None

# The public share type.
PublicShare = None

# The input share type.
InputShare = None

# The output share type.
OutShare = None

# The aggregate share type.
AggShare = None

# The aggregate result type.
AggResult = None

Expand All @@ -41,7 +50,7 @@ def shard(Daf,
measurement: Measurement,
nonce: bytes["Daf.NONCE_SIZE"],
rand: bytes["Daf.RAND_SIZE"],
) -> tuple[Bytes, Vec[Bytes]]:
) -> tuple[PublicShare, list[InputShare]]:
"""
Shard a measurement into a public share and a sequence of input
shares, one for each Aggregator. This method is run by the Client.
Expand All @@ -62,8 +71,8 @@ def prep(Daf,
agg_id: Unsigned,
agg_param: AggParam,
nonce: bytes["Daf.NONCE_SIZE"],
public_share: bytes,
input_share: bytes) -> OutShare:
public_share: PublicShare,
input_share: InputShare) -> OutShare:
"""
Prepare an input share for aggregation. This algorithm takes in the
public share and one of the input shares generated by the Client. It
Expand All @@ -77,7 +86,7 @@ def prep(Daf,
@classmethod
def aggregate(Daf,
agg_param: AggParam,
out_shares: Vec[OutShare]) -> Bytes:
out_shares: list[OutShare]) -> AggShare:
"""
Merge a list of output shares into an aggregate share, encoded as a byte
string. This is called by an Aggregator after recovering a batch of
Expand All @@ -88,7 +97,7 @@ def aggregate(Daf,
@classmethod
def unshard(Daf,
agg_param: AggParam,
agg_shares: Vec[Bytes],
agg_shares: list[AggShare],
num_measurements: Unsigned) -> AggResult:
"""
Unshard the aggregate shares (encoded as byte strings) and compute the
Expand All @@ -99,8 +108,8 @@ def unshard(Daf,

def run_daf(Daf,
agg_param: Daf.AggParam,
measurements: Vec[Daf.Measurement],
nonces: Vec[Bytes[Daf.NONCE_SIZE]]):
measurements: list[Daf.Measurement],
nonces: list[bytes["Daf.NONCE_SIZE"]]):
"""Run a DAF on a list of measurements."""
out_shares = [[] for j in range(Daf.SHARES)]
for (measurement, nonce) in zip(measurements, nonces):
Expand Down Expand Up @@ -148,41 +157,39 @@ class TestDaf(Daf):
RAND_SIZE = 16

# Associated types
OutShare = Vec[Field]
Measurement = Unsigned
AggResult = Vec[Unsigned]
PublicShare = None
InputShare = Field
OutShare = Field
AggShare = Field
AggResult = Unsigned

@classmethod
def shard(cls, measurement, _nonce, rand):
helper_shares = PrgSha3.expand_into_vec(cls.Field,
rand,
b'',
b'',
cls.SHARES-1)
helper_shares = XofShake128.expand_into_vec(cls.Field,
rand,
b'',
b'',
cls.SHARES-1)
leader_share = cls.Field(measurement)
for helper_share in helper_shares:
leader_share -= helper_share
input_shares = [cls.Field.encode_vec([leader_share])]
for helper_share in helper_shares:
input_shares.append(cls.Field.encode_vec([helper_share]))
return (b'dummy public share', input_shares)
input_shares = [leader_share] + helper_shares
return (None, input_shares)

@classmethod
def prep(cls, _agg_id, _agg_param, _nonce, _public_share, input_share):
# For this simple test DAF, the output share is the same as the input
# share.
return cls.Field.decode_vec(input_share)
return input_share

@classmethod
def aggregate(cls, _agg_param, out_shares):
return cls.Field.encode_vec(
reduce(lambda x, y: [x[0] + y[0]], out_shares))
return reduce(lambda x, y: x + y, out_shares)

@classmethod
def unshard(cls, _agg_param, agg_shares, _num_measurements):
return [reduce(lambda x, y: [x[0] + y[0]],
map(lambda encoded: cls.Field.decode_vec(encoded),
agg_shares))[0].as_unsigned()]
return reduce(lambda x, y: x + y, agg_shares).as_unsigned()


def test_daf(Daf,
Expand All @@ -199,9 +206,9 @@ def test_daf(Daf,
measurements,
nonces)
if agg_result != expected_agg_result:
print('vdaf test failed ({} on {}): unexpected result: got {}; want {}'.format(
Daf.__classname__, measurements, agg_result, expected_agg_result))
print('daf test failed ({} on {}): unexpected result: got {}; want {}'.format(
Daf.__class__, measurements, agg_result, expected_agg_result))


if __name__ == '__main__':
test_daf(TestDaf, None, [1, 2, 3, 4], [10])
test_daf(TestDaf, None, [1, 2, 3, 4], 10)
40 changes: 40 additions & 0 deletions poc/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,39 @@ def decode_vec(Field, encoded: Bytes) -> Vec[Field]:
vec.append(Field(x))
return vec

@classmethod
def encode_into_bit_vector(Field,
val: Unsigned,
bits: Unsigned) -> Vec[Field]:
"""
Encode the bit representation of `val` with at most `bits` number
of bits, as a vector of field elements.
"""
if val >= 2 ** bits:
# Sanity check we are able to represent `val` with `bits`
# number of bits.
raise ValueError("Number of bits is not enough to represent "
"the input integer.")
encoded = []
for l in range(bits):
encoded.append(Field((val >> l) & 1))
return encoded

@classmethod
def decode_from_bit_vector(Field, vec: Vec[Field]) -> Field:
"""
Decode the field element from the bit representation, expressed
as a vector of field elements `vec`.
"""
bits = len(vec)
if Field.MODULUS >> bits == 0:
raise ValueError("Number of bits is too large to be "
"represented by field modulus.")
decoded = Field(0)
for (l, bit) in enumerate(vec):
decoded += Field(1 << l) * bit
return decoded

def __add__(self, other: Field) -> Field:
return self.__class__(self.val + other.val)

Expand Down Expand Up @@ -252,6 +285,13 @@ def test_field(cls):
got = cls.decode_vec(cls.encode_vec(want))
assert got == want

# Test encoding integer as bit vector.
vals = [i for i in range(15)]
bits = 4
for val in vals:
encoded = cls.encode_into_bit_vector(val, bits)
assert cls.decode_from_bit_vector(encoded).as_unsigned() == val


def test_fft_field(cls):
test_field(cls)
Expand Down
Loading

0 comments on commit f919202

Please sign in to comment.