Skip to content
This repository was archived by the owner on Apr 5, 2019. It is now read-only.
This repository was archived by the owner on Apr 5, 2019. It is now read-only.

Batch PointOp verification #11

Description

@oleganza

Point ops need to be batch-verified for efficiency. This is how it's done:

If one has two statements x·A + y·B == 0 and z·C + w·D == 0 (number of terms could vary), they can be combined together with a free variable e:

x·A + y·B == 0
+
e·(z·C + w·D == 0)
=
x·A + y·B + e·z·C + e·w·D == 0

If the latter statement is true for all e, then it implies that each individual of first two statements is also true. The free variable e will be sampled randomly via Scalar::random(&mut rand::thread_rng()) to make it completely non-deterministic (unlike fiat-shamir challenges the prover does not ever need to know e here, and must not know it to prevent malicious cancellation of some terms in one statement by terms in another).

The inexpensive scalar-scalar multiplications are precomputed (e·z and e·w) and then the resulting arrays of scalar factors and points are sent into RistrettoPoint::optional_multiscalar_mul.

RistrettoPoint::optional_multiscalar_mul(
  [x, y, e·z, e·w],
  [A, B, C, D].map(|p|p.decompress()),
)

Note 1: the optional_multiscalar_mul takes iterators of Option<RistrettoPoint> to handle decompression errors in the inlined implementation and spare the user from allocating an intermediate vector.
Note 2: the optional_multiscalar_mul takes iterators instead of slices or vectors, so that the user can pass a combination of .maps that do premultiplication by e w/o ever having to .collect() the iterator into a container.

How does this scale to N point ops? Easy: just generate a random e[i] for each ith statement (including the first one to avoid special cases). Premultiply all scalars with e[i] in the ith statement and then pass the concatenated arrays into multiscalar_mul.

Since the number of operations is not static, we'd have to allocate intermediate buffer anyway (could not keep them as chained-iterator), but we can do it once (once per scalars, once per points) using Vec::with_capacity() using the sum of lengths of all terms from all statements.

For reference, there's an old PR on Bulletproofs (dalek-cryptography/bulletproofs#86) where batching is done for rangeproofs. The code is the easiest to see here: https://github.com/dalek-cryptography/bulletproofs/blob/oleg/batch-verify/src/range_proof/mod.rs (note use of Verification object similar to PointOp and batch_challenges that protect statements from cancelling each other maliciously).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions