awesome-blockchain/src/ConfidentialTx/crypto/secp256k1/libsecp256k1/src/modules/bulletproofs/inner_product_impl.h

849 lines
39 KiB
C

/**********************************************************************
* Copyright (c) 2018 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODULE_BULLETPROOF_INNER_PRODUCT_IMPL
#define SECP256K1_MODULE_BULLETPROOF_INNER_PRODUCT_IMPL
#include "group.h"
#include "scalar.h"
#include "modules/bulletproofs/main_impl.h"
#include "modules/bulletproofs/util.h"
#define POPCOUNT(x) (__builtin_popcountl((unsigned long)(x))) /* TODO make these portable */
#define CTZ(x) (__builtin_ctzl((unsigned long)(x)))
/* Number of scalars that should remain at the end of a recursive proof. The paper
* uses 2, by reducing the scalars as far as possible. We stop one recursive step
* early, trading two points (L, R) for two scalars, which reduces verification
* and prover cost.
*
* For the most part, all comments assume this value is at 4.
*/
#define IP_AB_SCALARS 4
/* Bulletproof inner products consist of the four scalars and `2[log2(n) - 1]` points
* `a_1`, `a_2`, `b_1`, `b_2`, `L_i` and `R_i`, where `i` ranges from 0 to `log2(n)-1`.
*
* The prover takes as input a point `P` and scalar `c`. It proves that there exist
* scalars `a_i`, `b_i` for `i` ranging from 0 to `n-1`, such that
* `P = sum_i [a_i G_i + b_i H_i]` and `<{a_i}, {b_i}> = c`.
* where `G_i` and `H_i` are standard NUMS generators.
*
* Verification of the proof comes down to a single multiexponentiation of the form
*
* P + (c - a_1*b_1 - a_2*b_2)*x*G
* - sum_{i=1}^n [s'_i*G_i + s_i*H_i]
* + sum_{i=1}^log2(n) [x_i^-2 L_i + x_i^2 R_i]
*
* which will equal infinity if the inner product proof is correct. Here
* - `G` is the standard secp generator
* - `x` is a hash of `commit` and is used to rerandomize `c`. See Protocol 2 vs Protocol 1 in the paper.
* - `x_i = H(x_{i-1} || L_i || R_i)`, where `x_{-1}` is passed through the `commit` variable and
* must be a commitment to `P` and `c`.
* - `s_i` and `s'_i` are computed as follows.
*
* Letting `i_j` be defined as 1 if `i & 2^j == 1`, and -1 otherwise,
* - For `i` from `1` to `n/2`, `s'_i = a_1 * prod_{j=1}^log2(n) x_j^i_j`
* - For `i` from `n/2 + 1` to `n`, `s'_i = a_2 * prod_{j=1}^log2(n) x_j^i_j`
* - For `i` from `1` to `n/2`, `s_i = b_1 * prod_{j=1}^log2(n) x_j^-i_j`
* - For `i` from `n/2 + 1` to `n`, `s_i = b_2 * prod_{j=1}^log2(n) x_j^-i_j`
*
* Observe that these can be computed iteratively by labelling the coefficients `s_i` for `i`
* from `0` to `2n-1` rather than 1-indexing and distinguishing between `s_i'`s and `s_i`s:
*
* Start with `s_0 = a_1 * prod_{j=1}^log2(n) x_j^-1`, then for later `s_i`s,
* - For `i` from `1` to `n/2 - 1`, multiply some earlier `s'_j` by some `x_k^2`
* - For `i = n/2`, multiply `s_{i-1} by `a_2/a_1`.
* - For `i` from `n/2 + 1` to `n - 1`, multiply some earlier `s'_j` by some `x_k^2`
* - For `i = n`, multiply `s'_{i-1}` by `b_1/a_2` to get `s_i`.
* - For `i` from `n + 1` to `3n/2 - 1`, multiply some earlier `s_j` by some `x_k^-2`
* - For `i = 3n/2`, multiply `s_{i-1}` by `b_2/b_1`.
* - For `i` from `3n/2 + 1` to `2n - 1`, multiply some earlier `s_j` by some `x_k^-2`
* where of course, the indices `j` and `k` must be chosen carefully.
*
* The bulk of `secp256k1_bulletproof_innerproduct_vfy_ecmult_callback` involves computing
* these indices, given `a_2/a_1`, `b_1/a_1`, `b_2/b_1`, and the `x_k^2`s as input. It
* computes `x_k^-2` as a side-effect of its other computation.
*/
typedef int (secp256k1_bulletproof_vfy_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, secp256k1_scalar *randomizer, size_t idx, void *data);
/* used by callers to wrap a proof with surrounding context */
typedef struct {
const unsigned char *proof;
secp256k1_scalar p_offs;
secp256k1_scalar yinv;
unsigned char commit[32];
secp256k1_bulletproof_vfy_callback *rangeproof_cb;
void *rangeproof_cb_data;
size_t n_extra_rangeproof_points;
} secp256k1_bulletproof_innerproduct_context;
/* used internally */
typedef struct {
const secp256k1_bulletproof_innerproduct_context *proof;
secp256k1_scalar abinv[IP_AB_SCALARS];
secp256k1_scalar xsq[SECP256K1_BULLETPROOF_MAX_DEPTH + 1];
secp256k1_scalar xsqinv[SECP256K1_BULLETPROOF_MAX_DEPTH + 1];
secp256k1_scalar xsqinvy[SECP256K1_BULLETPROOF_MAX_DEPTH + 1];
secp256k1_scalar xcache[SECP256K1_BULLETPROOF_MAX_DEPTH + 1];
secp256k1_scalar xsqinv_mask;
const unsigned char *serialized_lr;
} secp256k1_bulletproof_innerproduct_vfy_data;
/* used by callers to modify the multiexp */
typedef struct {
size_t n_proofs;
secp256k1_scalar p_offs;
const secp256k1_ge *g;
const secp256k1_ge *geng;
const secp256k1_ge *genh;
size_t vec_len;
size_t lg_vec_len;
int shared_g;
secp256k1_scalar *randomizer;
secp256k1_bulletproof_innerproduct_vfy_data *proof;
} secp256k1_bulletproof_innerproduct_vfy_ecmult_context;
size_t secp256k1_bulletproof_innerproduct_proof_length(size_t n) {
if (n < IP_AB_SCALARS / 2) {
return 32 * (1 + 2 * n);
} else {
size_t bit_count = POPCOUNT(n);
size_t log = secp256k1_floor_lg(2 * n / IP_AB_SCALARS);
return 32 * (1 + 2 * (bit_count - 1 + log) + IP_AB_SCALARS) + (2*log + 7) / 8;
}
}
/* Our ecmult_multi function takes `(c - a*b)*x` directly and multiplies this by `G`. For every other
* (scalar, point) pair it calls the following callback function, which takes an index and outputs a
* pair. The function therefore has three regimes:
*
* For the first `n` invocations, it returns `(s'_i, G_i)` for `i` from 1 to `n`.
* For the next `n` invocations, it returns `(s_i, H_i)` for `i` from 1 to `n`.
* For the next `2*log2(n)` invocations it returns `(x_i^-2, L_i)` and `(x_i^2, R_i)`,
* alternating between the two choices, for `i` from 1 to `log2(n)`.
*
* For the remaining invocations it passes through to another callback, `rangeproof_cb_data` which
* computes `P`. The reason for this is that in practice `P` is usually defined by another multiexp
* rather than being a known point, and it is more efficient to compute one exponentiation.
*
* Inline we refer to the first `2n` coefficients as `s_i` for `i` from 0 to `2n-1`, since that
* is the more convenient indexing. In particular we describe (a) how the indices `j` and `k`,
* from the big comment block above, are chosen; and (b) when/how each `x_k^-2` is computed.
*/
static int secp256k1_bulletproof_innerproduct_vfy_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_bulletproof_innerproduct_vfy_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_vfy_ecmult_context *) data;
/* First 2N points use the standard Gi, Hi generators, and the scalars can be aggregated across proofs.
* Inside this if clause, `idx` corresponds to the index `i` in the big comment, and runs from 0 to `2n-1`.
* Also `ctx->vec_len` corresponds to `n`. */
if (idx < 2 * ctx->vec_len) {
/* Number of `a` scalars in the proof (same as number of `b` scalars in the proof). Will
* be 2 except for very small proofs that have fewer than 2 scalars as input. */
const size_t grouping = ctx->vec_len < IP_AB_SCALARS / 2 ? ctx->vec_len : IP_AB_SCALARS / 2;
const size_t lg_grouping = secp256k1_floor_lg(grouping);
size_t i;
VERIFY_CHECK(lg_grouping == 0 || lg_grouping == 1); /* TODO support higher IP_AB_SCALARS */
/* Determine whether we're multiplying by `G_i`s or `H_i`s. */
if (idx < ctx->vec_len) {
*pt = ctx->geng[idx];
} else {
*pt = ctx->genh[idx - ctx->vec_len];
}
secp256k1_scalar_clear(sc);
/* Loop over all the different inner product proofs we might be doing at once. Since they
* share generators `G_i` and `H_i`, we compute all of their scalars at once and add them.
* For each proof we start with the "seed value" `ctx->proof[i].xcache[0]` (see next comment
* for its meaning) from which every other scalar derived. We expect the caller to have
* randomized this to ensure that this wanton addition cannot enable cancellation attacks.
*/
for (i = 0; i < ctx->n_proofs; i++) {
/* To recall from the introductory comment: most `s_i` values are computed by taking an
* earlier `s_j` value and multiplying it by some `x_k^2`.
*
* We now explain the index `j`: it is the largest number with one fewer 1-bits than `i`.
* Alternately, the most recently returned `s_j` where `j` has one fewer 1-bits than `i`.
*
* To ensure that `s_j` is available when we need it, on each iteration we define the
* variable `cache_idx` which simply counts the 1-bits in `i`; before returning `s_i`
* we store it in `ctx->proof[i].xcache[cache_idx]`. Then later, when we want "most
* recently returned `s_j` with one fewer 1-bits than `i`, it'll be sitting right
* there in `ctx->proof[i].xcache[cache_idx - 1]`.
*
* Note that `ctx->proof[i].xcache[0]` will always equal `-a_1 * prod_{i=1}^{n-1} x_i^-2`,
* and we expect the caller to have set this.
*/
const size_t cache_idx = POPCOUNT(idx);
secp256k1_scalar term;
VERIFY_CHECK(cache_idx < SECP256K1_BULLETPROOF_MAX_DEPTH);
/* For the special case `cache_idx == 0` (which is true iff `idx == 0`) there is nothing to do. */
if (cache_idx > 0) {
/* Otherwise, check if this is one of the special indices where we transition from `a_1` to `a_2`,
* from `a_2` to `b_1`, or from `b_1` to `b_2`. (For small proofs there is only one transition,
* from `a` to `b`.) */
if (idx % (ctx->vec_len / grouping) == 0) {
const size_t abinv_idx = idx / (ctx->vec_len / grouping) - 1;
size_t prev_cache_idx;
/* Check if it's the even specialer index where we're transitioning from `a`s to `b`s, from
* `G`s to `H`s, and from `x_k^2`s to `x_k^-2`s. In rangeproof and circuit applications,
* the caller secretly has a variable `y` such that `H_i` is really `y^-i H_i` for `i` ranging
* from 0 to `n-1`. Rather than forcing the caller to tweak every `H_i` herself, which would
* be very slow and prevent precomputation, we instead multiply our cached `x_k^-2` values
* by `y^(-2^k)` respectively, which will ultimately result in every `s_i` we return having
* been multiplied by `y^-i`.
*
* This is an underhanded trick but the result is that all `n` powers of `y^-i` show up
* in the right place, and we only need log-many scalar squarings and multiplications.
*/
if (idx == ctx->vec_len) {
secp256k1_scalar yinvn = ctx->proof[i].proof->yinv;
size_t j;
prev_cache_idx = POPCOUNT(idx - 1);
for (j = 0; j < (size_t) CTZ(idx) - lg_grouping; j++) {
secp256k1_scalar_mul(&ctx->proof[i].xsqinvy[j], &ctx->proof[i].xsqinv[j], &yinvn);
secp256k1_scalar_sqr(&yinvn, &yinvn);
}
if (lg_grouping == 1) {
secp256k1_scalar_mul(&ctx->proof[i].abinv[2], &ctx->proof[i].abinv[2], &yinvn);
secp256k1_scalar_sqr(&yinvn, &yinvn);
}
} else {
prev_cache_idx = cache_idx - 1;
}
/* Regardless of specialness, we multiply by `a_2/a_1` or whatever the appropriate multiplier
* is. We expect the caller to have given these to us in the `ctx->proof[i].abinv` array. */
secp256k1_scalar_mul(
&ctx->proof[i].xcache[cache_idx],
&ctx->proof[i].xcache[prev_cache_idx],
&ctx->proof[i].abinv[abinv_idx]
);
/* If it's *not* a special index, just multiply by the appropriate `x_k^2`, or `x_k^-2` in case
* we're in the `H_i` half of the multiexp. At this point we can explain the index `k`, which
* is computed in the variable `xsq_idx` (`xsqinv_idx` respectively). In light of our discussion
* of `j`, we see that this should be "the least significant bit that's 1 in `i` but not `i-1`."
* In other words, it is the number of trailing 0 bits in the index `i`. */
} else if (idx < ctx->vec_len) {
const size_t xsq_idx = CTZ(idx);
secp256k1_scalar_mul(&ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xcache[cache_idx - 1], &ctx->proof[i].xsq[xsq_idx]);
} else {
const size_t xsqinv_idx = CTZ(idx);
secp256k1_scalar_mul(&ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xcache[cache_idx - 1], &ctx->proof[i].xsqinvy[xsqinv_idx]);
}
}
term = ctx->proof[i].xcache[cache_idx];
/* One last trick: compute `x_k^-2` while computing the `G_i` scalars, so that they'll be
* available when we need them for the `H_i` scalars. We can do this for every `i` value
* that has exactly one 0-bit, i.e. which is a product of all `x_i`s and one `x_k^-1`. By
* multiplying that by the special value `prod_{i=1}^n x_i^-1` we obtain simply `x_k^-2`.
* We expect the caller to give us this special value in `ctx->proof[i].xsqinv_mask`. */
if (idx < ctx->vec_len / grouping && POPCOUNT(idx) == ctx->lg_vec_len - 1) {
const size_t xsqinv_idx = CTZ(~idx);
secp256k1_scalar_mul(&ctx->proof[i].xsqinv[xsqinv_idx], &ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xsqinv_mask);
}
/* Finally, if the caller, in its computation of `P`, wants to multiply `G_i` or `H_i` by some scalar,
* we add that to our sum as well. Again, we trust the randomization in `xcache[0]` to prevent any
* cancellation attacks here. */
if (ctx->proof[i].proof->rangeproof_cb != NULL) {
secp256k1_scalar rangeproof_offset;
if ((ctx->proof[i].proof->rangeproof_cb)(&rangeproof_offset, NULL, &ctx->randomizer[i], idx, ctx->proof[i].proof->rangeproof_cb_data) == 0) {
return 0;
}
secp256k1_scalar_add(&term, &term, &rangeproof_offset);
}
secp256k1_scalar_add(sc, sc, &term);
}
/* Next 2lgN points are the L and R vectors */
} else if (idx < 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs)) {
size_t real_idx = idx - 2 * ctx->vec_len;
const size_t proof_idx = real_idx / (2 * ctx->lg_vec_len);
real_idx = real_idx % (2 * ctx->lg_vec_len);
secp256k1_bulletproof_deserialize_point(
pt,
ctx->proof[proof_idx].serialized_lr,
real_idx,
2 * ctx->lg_vec_len
);
if (idx % 2 == 0) {
*sc = ctx->proof[proof_idx].xsq[real_idx / 2];
} else {
*sc = ctx->proof[proof_idx].xsqinv[real_idx / 2];
}
secp256k1_scalar_mul(sc, sc, &ctx->randomizer[proof_idx]);
/* After the G's, H's, L's and R's, do the blinding_gen */
} else if (idx == 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs)) {
*sc = ctx->p_offs;
*pt = *ctx->g;
/* Remaining points are whatever the rangeproof wants */
} else if (ctx->shared_g && idx == 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs) + 1) {
/* Special case: the first extra point is independent of the proof, for both rangeproof and circuit */
size_t i;
secp256k1_scalar_clear(sc);
for (i = 0; i < ctx->n_proofs; i++) {
secp256k1_scalar term;
if ((ctx->proof[i].proof->rangeproof_cb)(&term, pt, &ctx->randomizer[i], 2 * (ctx->vec_len + ctx->lg_vec_len), ctx->proof[i].proof->rangeproof_cb_data) == 0) {
return 0;
}
secp256k1_scalar_add(sc, sc, &term);
}
} else {
size_t proof_idx = 0;
size_t real_idx = idx - 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs) - 1 - !!ctx->shared_g;
while (real_idx >= ctx->proof[proof_idx].proof->n_extra_rangeproof_points - !!ctx->shared_g) {
real_idx -= ctx->proof[proof_idx].proof->n_extra_rangeproof_points - !!ctx->shared_g;
proof_idx++;
VERIFY_CHECK(proof_idx < ctx->n_proofs);
}
if ((ctx->proof[proof_idx].proof->rangeproof_cb)(sc, pt, &ctx->randomizer[proof_idx], 2 * (ctx->vec_len + ctx->lg_vec_len), ctx->proof[proof_idx].proof->rangeproof_cb_data) == 0) {
return 0;
}
}
return 1;
}
/* nb For security it is essential that `commit_inp` already commit to all data
* needed to compute `P`. We do not hash it in during verification since `P`
* may be specified indirectly as a bunch of scalar offsets.
*/
static int secp256k1_bulletproof_inner_product_verify_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, const secp256k1_bulletproof_generators *gens, size_t vec_len, const secp256k1_bulletproof_innerproduct_context *proof, size_t n_proofs, size_t plen, int shared_g) {
secp256k1_sha256 sha256;
secp256k1_bulletproof_innerproduct_vfy_ecmult_context ecmult_data;
unsigned char commit[32];
size_t total_n_points = 2 * vec_len + !!shared_g + 1; /* +1 for shared G (value_gen), +1 for H (blinding_gen) */
secp256k1_gej r;
secp256k1_scalar zero;
size_t i;
if (plen != secp256k1_bulletproof_innerproduct_proof_length(vec_len)) {
return 0;
}
if (n_proofs == 0) {
return 1;
}
if (!secp256k1_scratch_allocate_frame(scratch, n_proofs * (sizeof(*ecmult_data.randomizer) + sizeof(*ecmult_data.proof)), 2)) {
return 0;
}
secp256k1_scalar_clear(&zero);
ecmult_data.n_proofs = n_proofs;
ecmult_data.g = gens->blinding_gen;
ecmult_data.geng = gens->gens;
ecmult_data.genh = gens->gens + gens->n / 2;
ecmult_data.vec_len = vec_len;
ecmult_data.lg_vec_len = secp256k1_floor_lg(2 * vec_len / IP_AB_SCALARS);
ecmult_data.shared_g = shared_g;
ecmult_data.randomizer = (secp256k1_scalar *)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*ecmult_data.randomizer));
ecmult_data.proof = (secp256k1_bulletproof_innerproduct_vfy_data *)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*ecmult_data.proof));
/* Seed RNG for per-proof randomizers */
secp256k1_sha256_initialize(&sha256);
for (i = 0; i < n_proofs; i++) {
secp256k1_sha256_write(&sha256, proof[i].proof, plen);
secp256k1_sha256_write(&sha256, proof[i].commit, 32);
secp256k1_scalar_get_b32(commit, &proof[i].p_offs);
secp256k1_sha256_write(&sha256, commit, 32);
}
secp256k1_sha256_finalize(&sha256, commit);
secp256k1_scalar_clear(&ecmult_data.p_offs);
for (i = 0; i < n_proofs; i++) {
const unsigned char *serproof = proof[i].proof;
unsigned char proof_commit[32];
secp256k1_scalar dot;
secp256k1_scalar ab[IP_AB_SCALARS];
secp256k1_scalar negprod;
secp256k1_scalar x;
int overflow;
size_t j;
const size_t n_ab = 2 * vec_len < IP_AB_SCALARS ? 2 * vec_len : IP_AB_SCALARS;
total_n_points += 2 * ecmult_data.lg_vec_len + proof[i].n_extra_rangeproof_points - !!shared_g; /* -1 for shared G */
/* Extract dot product, will always be the first 32 bytes */
secp256k1_scalar_set_b32(&dot, serproof, &overflow);
if (overflow) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
/* Commit to dot product */
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, proof[i].commit, 32);
secp256k1_sha256_write(&sha256, serproof, 32);
secp256k1_sha256_finalize(&sha256, proof_commit);
serproof += 32;
/* Extract a, b */
for (j = 0; j < n_ab; j++) {
secp256k1_scalar_set_b32(&ab[j], serproof, &overflow);
if (overflow) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
/* TODO our verifier currently bombs out with zeros because it uses
* scalar inverses gratuitously. Fix that. */
if (secp256k1_scalar_is_zero(&ab[j])) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
serproof += 32;
}
secp256k1_scalar_dot_product(&negprod, &ab[0], &ab[n_ab / 2], n_ab / 2);
ecmult_data.proof[i].proof = &proof[i];
/* set per-proof randomizer */
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, commit, 32);
secp256k1_sha256_finalize(&sha256, commit);
secp256k1_scalar_set_b32(&ecmult_data.randomizer[i], commit, &overflow);
if (overflow || secp256k1_scalar_is_zero(&ecmult_data.randomizer[i])) {
/* cryptographically unreachable */
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
/* Compute x*(dot - a*b) for each proof; add it and p_offs to the p_offs accumulator */
secp256k1_scalar_set_b32(&x, proof_commit, &overflow);
if (overflow || secp256k1_scalar_is_zero(&x)) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
secp256k1_scalar_negate(&negprod, &negprod);
secp256k1_scalar_add(&negprod, &negprod, &dot);
secp256k1_scalar_mul(&x, &x, &negprod);
secp256k1_scalar_add(&x, &x, &proof[i].p_offs);
secp256k1_scalar_mul(&x, &x, &ecmult_data.randomizer[i]);
secp256k1_scalar_add(&ecmult_data.p_offs, &ecmult_data.p_offs, &x);
/* Special-case: trivial proofs are valid iff the explicitly revealed scalars
* dot to the explicitly revealed dot product. */
if (2 * vec_len <= IP_AB_SCALARS) {
if (!secp256k1_scalar_is_zero(&negprod)) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
/* remaining data does not (and cannot) be computed for proofs with no a's or b's. */
if (vec_len == 0) {
continue;
}
}
/* Compute the inverse product and the array of squares; the rest will be filled
* in by the callback during the multiexp. */
ecmult_data.proof[i].serialized_lr = serproof; /* bookmark L/R location in proof */
negprod = ab[n_ab - 1];
ab[n_ab - 1] = ecmult_data.randomizer[i]; /* build r * x1 * x2 * ... * xn in last slot of `ab` array */
for (j = 0; j < ecmult_data.lg_vec_len; j++) {
secp256k1_scalar xi;
const size_t lidx = 2 * j;
const size_t ridx = 2 * j + 1;
const size_t bitveclen = (2 * ecmult_data.lg_vec_len + 7) / 8;
const unsigned char lrparity = 2 * !!(serproof[lidx / 8] & (1 << (lidx % 8))) + !!(serproof[ridx / 8] & (1 << (ridx % 8)));
/* Map commit -> H(commit || LR parity || Lx || Rx), compute xi from it */
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, proof_commit, 32);
secp256k1_sha256_write(&sha256, &lrparity, 1);
secp256k1_sha256_write(&sha256, &serproof[32 * lidx + bitveclen], 32);
secp256k1_sha256_write(&sha256, &serproof[32 * ridx + bitveclen], 32);
secp256k1_sha256_finalize(&sha256, proof_commit);
secp256k1_scalar_set_b32(&xi, proof_commit, &overflow);
if (overflow || secp256k1_scalar_is_zero(&xi)) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
secp256k1_scalar_mul(&ab[n_ab - 1], &ab[n_ab - 1], &xi);
secp256k1_scalar_sqr(&ecmult_data.proof[i].xsq[j], &xi);
}
/* Compute inverse of all a's and b's, except the last b whose inverse is not needed.
* Also compute the inverse of (-r * x1 * ... * xn) which will be needed */
secp256k1_scalar_inverse_all_var(ecmult_data.proof[i].abinv, ab, n_ab);
ab[n_ab - 1] = negprod;
/* Compute (-a0 * r * x1 * ... * xn)^-1 which will be used to mask out individual x_i^-2's */
secp256k1_scalar_negate(&ecmult_data.proof[i].xsqinv_mask, &ecmult_data.proof[i].abinv[0]);
secp256k1_scalar_mul(&ecmult_data.proof[i].xsqinv_mask, &ecmult_data.proof[i].xsqinv_mask, &ecmult_data.proof[i].abinv[n_ab - 1]);
/* Compute each scalar times the previous' inverse, which is used to switch between a's and b's */
for (j = n_ab - 1; j > 0; j--) {
size_t prev_idx;
if (j == n_ab / 2) {
prev_idx = j - 1; /* we go from a_n to b_0 */
} else {
prev_idx = j & (j - 1); /* but from a_i' to a_i, where i' is i with its lowest set bit unset */
}
secp256k1_scalar_mul(
&ecmult_data.proof[i].abinv[j - 1],
&ecmult_data.proof[i].abinv[prev_idx],
&ab[j]
);
}
/* Extract -a0 * r * (x1 * ... * xn)^-1 which is our first coefficient. Use negprod as a dummy */
secp256k1_scalar_mul(&negprod, &ecmult_data.randomizer[i], &ab[0]); /* r*a */
secp256k1_scalar_sqr(&negprod, &negprod); /* (r*a)^2 */
secp256k1_scalar_mul(&ecmult_data.proof[i].xcache[0], &ecmult_data.proof[i].xsqinv_mask, &negprod); /* -a * r * (x1 * x2 * ... * xn)^-1 */
}
/* Do the multiexp */
if (secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &r, NULL, secp256k1_bulletproof_innerproduct_vfy_ecmult_callback, (void *) &ecmult_data, total_n_points) != 1) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
secp256k1_scratch_deallocate_frame(scratch);
return secp256k1_gej_is_infinity(&r);
}
typedef struct {
secp256k1_scalar x[SECP256K1_BULLETPROOF_MAX_DEPTH];
secp256k1_scalar xinv[SECP256K1_BULLETPROOF_MAX_DEPTH];
secp256k1_scalar yinv;
secp256k1_scalar yinvn;
const secp256k1_ge *geng;
const secp256k1_ge *genh;
const secp256k1_ge *g;
const secp256k1_scalar *a;
const secp256k1_scalar *b;
secp256k1_scalar g_sc;
size_t grouping;
size_t n;
} secp256k1_bulletproof_innerproduct_pf_ecmult_context;
/* At each level i of recursion (i from 0 upto lg(vector size) - 1)
* L = a_even . G_odd + b_odd . H_even (18)
* which, by expanding the generators into the original G's and H's
* and setting n = (1 << i), can be computed as follows:
*
* For j from 1 to [vector size],
* 1. Use H[j] or G[j] as generator, starting with H and switching
* every n.
* 2. Start with b1 with H and a0 with G, and increment by 2 each switch.
* 3. For k = 1, 2, 4, ..., n/2, use the same algorithm to choose
* between a and b to choose between x and x^-1, except using
* k in place of n. With H's choose x then x^-1, with G's choose
* x^-1 then x.
*
* For R everything is the same except swap G/H and a/b and x/x^-1.
*/
static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_l(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data;
const size_t ab_idx = (idx / ctx->grouping) ^ 1;
size_t i;
/* Special-case the primary generator */
if (idx == ctx->n) {
*pt = *ctx->g;
*sc = ctx->g_sc;
return 1;
}
/* steps 1/2 */
if ((idx / ctx->grouping) % 2 == 0) {
*pt = ctx->genh[idx];
*sc = ctx->b[ab_idx];
/* Map h -> h' (eqn 59) */
secp256k1_scalar_mul(sc, sc, &ctx->yinvn);
} else {
*pt = ctx->geng[idx];
*sc = ctx->a[ab_idx];
}
/* step 3 */
for (i = 0; (1u << i) < ctx->grouping; i++) {
size_t grouping = (1u << i);
if ((((idx / grouping) % 2) ^ ((idx / ctx->grouping) % 2)) == 0) {
secp256k1_scalar_mul(sc, sc, &ctx->x[i]);
} else {
secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]);
}
}
secp256k1_scalar_mul(&ctx->yinvn, &ctx->yinvn, &ctx->yinv);
return 1;
}
/* Identical code except `== 0` changed to `== 1` twice, and the
* `+ 1` from Step 1/2 was moved to the other if branch. */
static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_r(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data;
const size_t ab_idx = (idx / ctx->grouping) ^ 1;
size_t i;
/* Special-case the primary generator */
if (idx == ctx->n) {
*pt = *ctx->g;
*sc = ctx->g_sc;
return 1;
}
/* steps 1/2 */
if ((idx / ctx->grouping) % 2 == 1) {
*pt = ctx->genh[idx];
*sc = ctx->b[ab_idx];
/* Map h -> h' (eqn 59) */
secp256k1_scalar_mul(sc, sc, &ctx->yinvn);
} else {
*pt = ctx->geng[idx];
*sc = ctx->a[ab_idx];
}
/* step 3 */
for (i = 0; (1u << i) < ctx->grouping; i++) {
size_t grouping = (1u << i);
if ((((idx / grouping) % 2) ^ ((idx / ctx->grouping) % 2)) == 1) {
secp256k1_scalar_mul(sc, sc, &ctx->x[i]);
} else {
secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]);
}
}
secp256k1_scalar_mul(&ctx->yinvn, &ctx->yinvn, &ctx->yinv);
return 1;
}
static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_g(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data;
size_t i;
*pt = ctx->geng[idx];
secp256k1_scalar_set_int(sc, 1);
for (i = 0; (1u << i) <= ctx->grouping; i++) {
if (idx & (1u << i)) {
secp256k1_scalar_mul(sc, sc, &ctx->x[i]);
} else {
secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]);
}
}
return 1;
}
static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_h(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data;
size_t i;
*pt = ctx->genh[idx];
secp256k1_scalar_set_int(sc, 1);
for (i = 0; (1u << i) <= ctx->grouping; i++) {
if (idx & (1u << i)) {
secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]);
} else {
secp256k1_scalar_mul(sc, sc, &ctx->x[i]);
}
}
secp256k1_scalar_mul(sc, sc, &ctx->yinvn);
secp256k1_scalar_mul(&ctx->yinvn, &ctx->yinvn, &ctx->yinv);
return 1;
}
/* These proofs are not zero-knowledge. There is no need to worry about constant timeness.
* `commit_inp` must contain 256 bits of randomness, it is used immediately as a randomizer.
*/
static int secp256k1_bulletproof_inner_product_real_prove_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, secp256k1_ge *out_pt, size_t *pt_idx, const secp256k1_ge *g, secp256k1_ge *geng, secp256k1_ge *genh, secp256k1_scalar *a_arr, secp256k1_scalar *b_arr, const secp256k1_scalar *yinv, const secp256k1_scalar *ux, const size_t n, unsigned char *commit) {
size_t i;
size_t halfwidth;
secp256k1_bulletproof_innerproduct_pf_ecmult_context pfdata;
pfdata.yinv = *yinv;
pfdata.g = g;
pfdata.geng = geng;
pfdata.genh = genh;
pfdata.a = a_arr;
pfdata.b = b_arr;
pfdata.n = n;
/* Protocol 1: Iterate, halving vector size until it is 1 */
for (halfwidth = n / 2, i = 0; halfwidth > IP_AB_SCALARS / 4; halfwidth /= 2, i++) {
secp256k1_gej tmplj, tmprj;
size_t j;
int overflow;
pfdata.grouping = 1u << i;
/* L */
secp256k1_scalar_clear(&pfdata.g_sc);
for (j = 0; j < halfwidth; j++) {
secp256k1_scalar prod;
secp256k1_scalar_mul(&prod, &a_arr[2*j], &b_arr[2*j + 1]);
secp256k1_scalar_add(&pfdata.g_sc, &pfdata.g_sc, &prod);
}
secp256k1_scalar_mul(&pfdata.g_sc, &pfdata.g_sc, ux);
secp256k1_scalar_set_int(&pfdata.yinvn, 1);
secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &tmplj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_l, (void *) &pfdata, n + 1);
secp256k1_ge_set_gej(&out_pt[(*pt_idx)++], &tmplj);
/* R */
secp256k1_scalar_clear(&pfdata.g_sc);
for (j = 0; j < halfwidth; j++) {
secp256k1_scalar prod;
secp256k1_scalar_mul(&prod, &a_arr[2*j + 1], &b_arr[2*j]);
secp256k1_scalar_add(&pfdata.g_sc, &pfdata.g_sc, &prod);
}
secp256k1_scalar_mul(&pfdata.g_sc, &pfdata.g_sc, ux);
secp256k1_scalar_set_int(&pfdata.yinvn, 1);
secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &tmprj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_r, (void *) &pfdata, n + 1);
secp256k1_ge_set_gej(&out_pt[(*pt_idx)++], &tmprj);
/* x, x^2, x^-1, x^-2 */
secp256k1_bulletproof_update_commit(commit, &out_pt[*pt_idx - 2], &out_pt[*pt_idx] - 1);
secp256k1_scalar_set_b32(&pfdata.x[i], commit, &overflow);
if (overflow || secp256k1_scalar_is_zero(&pfdata.x[i])) {
return 0;
}
secp256k1_scalar_inverse_var(&pfdata.xinv[i], &pfdata.x[i]);
/* update scalar array */
for (j = 0; j < halfwidth; j++) {
secp256k1_scalar tmps;
secp256k1_scalar_mul(&a_arr[2*j], &a_arr[2*j], &pfdata.x[i]);
secp256k1_scalar_mul(&tmps, &a_arr[2*j + 1], &pfdata.xinv[i]);
secp256k1_scalar_add(&a_arr[j], &a_arr[2*j], &tmps);
secp256k1_scalar_mul(&b_arr[2*j], &b_arr[2*j], &pfdata.xinv[i]);
secp256k1_scalar_mul(&tmps, &b_arr[2*j + 1], &pfdata.x[i]);
secp256k1_scalar_add(&b_arr[j], &b_arr[2*j], &tmps);
}
/* Combine G generators and recurse, if that would be more optimal */
if ((n > 2048 && i == 3) || (n > 128 && i == 2) || (n > 32 && i == 1)) {
secp256k1_scalar yinv2;
for (j = 0; j < halfwidth; j++) {
secp256k1_gej rj;
secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &rj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_g, (void *) &pfdata, 2u << i);
pfdata.geng += 2u << i;
secp256k1_ge_set_gej(&geng[j], &rj);
secp256k1_scalar_set_int(&pfdata.yinvn, 1);
secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &rj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_h, (void *) &pfdata, 2u << i);
pfdata.genh += 2u << i;
secp256k1_ge_set_gej(&genh[j], &rj);
}
secp256k1_scalar_sqr(&yinv2, yinv);
for (j = 0; j < i; j++) {
secp256k1_scalar_sqr(&yinv2, &yinv2);
}
if (!secp256k1_bulletproof_inner_product_real_prove_impl(ecmult_ctx, scratch, out_pt, pt_idx, g, geng, genh, a_arr, b_arr, &yinv2, ux, halfwidth, commit)) {
return 0;
}
break;
}
}
return 1;
}
static int secp256k1_bulletproof_inner_product_prove_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, unsigned char *proof, size_t *proof_len, const secp256k1_bulletproof_generators *gens, const secp256k1_scalar *yinv, const size_t n, secp256k1_ecmult_multi_callback *cb, void *cb_data, const unsigned char *commit_inp) {
secp256k1_sha256 sha256;
size_t i;
unsigned char commit[32];
secp256k1_scalar *a_arr;
secp256k1_scalar *b_arr;
secp256k1_ge *out_pt;
secp256k1_ge *geng;
secp256k1_ge *genh;
secp256k1_scalar ux;
int overflow;
size_t pt_idx = 0;
secp256k1_scalar dot;
size_t half_n_ab = n < IP_AB_SCALARS / 2 ? n : IP_AB_SCALARS / 2;
if (*proof_len < secp256k1_bulletproof_innerproduct_proof_length(n)) {
return 0;
}
*proof_len = secp256k1_bulletproof_innerproduct_proof_length(n);
/* Special-case lengths 0 and 1 whose proofs are just expliict lists of scalars */
if (n <= IP_AB_SCALARS / 2) {
secp256k1_scalar a[IP_AB_SCALARS / 2];
secp256k1_scalar b[IP_AB_SCALARS / 2];
for (i = 0; i < n; i++) {
cb(&a[i], NULL, 2*i, cb_data);
cb(&b[i], NULL, 2*i+1, cb_data);
}
secp256k1_scalar_dot_product(&dot, a, b, n);
secp256k1_scalar_get_b32(proof, &dot);
for (i = 0; i < n; i++) {
secp256k1_scalar_get_b32(&proof[32 * (i + 1)], &a[i]);
secp256k1_scalar_get_b32(&proof[32 * (i + n + 1)], &b[i]);
}
VERIFY_CHECK(*proof_len == 32 * (2 * n + 1));
return 1;
}
/* setup for nontrivial proofs */
if (!secp256k1_scratch_allocate_frame(scratch, 2 * n * (sizeof(secp256k1_scalar) + sizeof(secp256k1_ge)) + 2 * secp256k1_floor_lg(n) * sizeof(secp256k1_ge), 5)) {
return 0;
}
a_arr = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_scalar));
b_arr = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_scalar));
geng = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_ge));
genh = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_ge));
out_pt = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, 2 * secp256k1_floor_lg(n) * sizeof(secp256k1_ge));
VERIFY_CHECK(a_arr != NULL);
VERIFY_CHECK(b_arr != NULL);
VERIFY_CHECK(gens != NULL);
for (i = 0; i < n; i++) {
cb(&a_arr[i], NULL, 2*i, cb_data);
cb(&b_arr[i], NULL, 2*i+1, cb_data);
geng[i] = gens->gens[i];
genh[i] = gens->gens[i + gens->n/2];
}
/* Record final dot product */
secp256k1_scalar_dot_product(&dot, a_arr, b_arr, n);
secp256k1_scalar_get_b32(proof, &dot);
/* Protocol 2: hash dot product to obtain G-randomizer */
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, commit_inp, 32);
secp256k1_sha256_write(&sha256, proof, 32);
secp256k1_sha256_finalize(&sha256, commit);
proof += 32;
secp256k1_scalar_set_b32(&ux, commit, &overflow);
if (overflow || secp256k1_scalar_is_zero(&ux)) {
/* cryptographically unreachable */
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
if (!secp256k1_bulletproof_inner_product_real_prove_impl(ecmult_ctx, scratch, out_pt, &pt_idx, gens->blinding_gen, geng, genh, a_arr, b_arr, yinv, &ux, n, commit)) {
secp256k1_scratch_deallocate_frame(scratch);
return 0;
}
/* Final a/b values */
for (i = 0; i < half_n_ab; i++) {
secp256k1_scalar_get_b32(&proof[32 * i], &a_arr[i]);
secp256k1_scalar_get_b32(&proof[32 * (i + half_n_ab)], &b_arr[i]);
}
proof += 64 * half_n_ab;
secp256k1_bulletproof_serialize_points(proof, out_pt, pt_idx);
secp256k1_scratch_deallocate_frame(scratch);
return 1;
}
#undef IP_AB_SCALARS
#endif