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

Batch PointOp verification #11

@oleganza

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions