-
Couldn't load subscription status.
- Fork 6
Batch PointOp verification #11
Description
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).