Skip to content

perf(bp): collapse aggregated verifier into single vendored MSM#104

Open
tesseract-ripple wants to merge 2 commits into
XRPLF:mainfrom
tesseract-ripple:issue-100-bp-verify-msm
Open

perf(bp): collapse aggregated verifier into single vendored MSM#104
tesseract-ripple wants to merge 2 commits into
XRPLF:mainfrom
tesseract-ripple:issue-100-bp-verify-msm

Conversation

@tesseract-ripple

Copy link
Copy Markdown
Collaborator

Closes #100. Stacked on #89 (vendored mpt_msm_variable_time); the
last commit (2cd20ac9) is the only #100 change. Review just that
commit — the preceding commit is #89's vendor as already up for
review there.

What

Collapses secp256k1_bulletproof_verify_agg's two equality checks
(the range-relation LHS == RHS and the inner-product-collapsed
P + sum L,R == a*Gf + b*Hf + a*b*ux*U) into a single
mpt_msm_variable_time call that must return the identity.

Equation

With a fresh Fiat–Shamir batching weight $c$ (BBB+18-style RLC), the
check $E_1 + c \cdot E_2 = 0$ unrolls to a
$(2n + 2 \log n + m + 6)$-term MSM plus an optional $G$ coefficient
$t_{\hat{}} - \delta$ passed via inp_g_sc_be32. The $H_k$ coefficient
absorbs the $y^{-k}$ factor that the previous code applied via a
separately-built Hprime vector. The per-term $s_k$ product (the
IPA fold weight) is computed inline matching fold_generators()'s
G-fold pattern; $s_k^{-1}$ matches the H-fold pattern, recomputed
directly to avoid $n$ scalar inversions.

Soundness

$c$ is bound to the entire proof (u_{rounds-1} + tau_x + mu +
a + b) via SHA-256 with the dedicated tag
MPT_BP_VERIFY_BATCH_RLC. A malicious prover that makes $E_1$ and
$E_2$ individually non-zero would need to predict $c$ before committing
the proof; Schwartz–Zippel gives the standard $1/q$ bound. The
$c = 0$ case ($\sim 1/2^{256}$) is explicitly rejected to keep the
soundness reasoning clean.

Perf (Apple M-series, m=2, n=128, 5-iteration avg)

verify time
before (vendor-msm-pippenger tip) 10.0 ms
after (this PR) 1.68 ms
speedup ~6×

Exceeds the 2–4× estimate in #100; the extra factor comes from also
folding the $(m+4)$-term range check and the $\log n$ IPA folding
mults into the same MSM (where the constant-fan-out terms contribute
to the GLV/Pippenger amortisation).

Other changes

  • secp256k1_bulletproof_ipa_msm is untouched. The prover's
    calculate_commitment_term still routes through the per-term loop,
    preserving the prover CT contract. The constant-time MSM profile
    for the prover is tracked separately in mpt_msm_constant_time: constant-time MSM profile for the prover path #87.
  • The static helper ipa_verify_explicit is removed; tests/test_ipa.c
    carries its own copy of the round-by-round IPA verify check.
  • fold_generators and apply_ipa_folding_to_P are retained because
    tests/test_ipa.c still calls them.

Tests

11/11 ctest green. test_bulletproof_agg exercises positive and
negative paths for $m \in {1, 2}$ including $v = 0$, $v = 1$,
$v = \text{UINT64_MAX}$, and the two tampered-commitment cases that
trigger the rejection branch of the consolidated MSM.

Out of scope (tracked separately)

Self-contained vendor of secp256k1_ecmult_multi_var (Pippenger/Straus
MSM) from libsecp256k1 v0.7.1, supporting the upcoming *_verify_batch
API for compact-sigma + aggregated-BP batch verification.

Vendor layout (third_party/secp256k1-msm/)
------------------------------------------
- 39 .h files + precomputed_ecmult.c (~2.3 MB of generator table
  data) copied from libsecp256k1 src/ at tag v0.7.1, commit
  1a53f4961f337b4d166c25fce72ef0dc88806618. See PROVENANCE.
- mpt_msm.c: single wrapper translation unit that includes the
  vendored headers and exposes the one external API symbol,
  mpt_msm_variable_time. All upstream functions are file-static;
  no link-time collision with the still-linked libsecp256k1.
- Two compile-time -D renames (-Dsecp256k1_pre_g=mpt_secp256k1_pre_g
  and the _128 variant) to namespace the only non-static data
  symbols (the generator precomputation tables, which the linked
  libsecp256k1 also exports).
- Local edit to util.h: relative include "../include/secp256k1.h"
  changed to <secp256k1.h> so it resolves through the linked
  install. Only edit to upstream sources.

Self-containment
----------------
The vendor includes dependent internal types (secp256k1_gej,
secp256k1_scalar, secp256k1_fe, scratch-space layouts), not just
ecmult_impl.h and the WNAF helpers. This decouples the vendored
MSM's correctness from the linked libsecp256k1 binary's version,
so this PR does NOT change the existing libsecp256k1 Conan pin.

Threat model
------------
mpt_msm_variable_time is variable-time. Validator path (D4) only;
the verifier has no secret inputs. The two-profile API shape
(mpt_msm_variable_time vs the planned mpt_msm_constant_time -- see
XRPLF#87) makes the constant-time requirement audit-visible at the call
site.

Build integration
-----------------
- CMake OBJECT library mpt-crypto-msm-vendor; linked into mpt-crypto.
- -Wno-pedantic only on the vendored TU; main library remains under
  -Wall -Wextra -Wpedantic -Werror.
- .pre-commit-config.yaml: third_party/ excluded from style hooks so
  the vendored upstream files stay close to their upstream form
  (clean diffs on re-sync). Large-file limit raised to 4 MB to admit
  precomputed_ecmult.c.

Tests
-----
tests/test_mpt_msm.c: calibration test that compares the vendored
MSM output to a reference computed via the public libsecp256k1 API
(tweak_mul + pubkey_combine in a loop) on randomized inputs. Bit-
identical match required across N_TRIALS. Confirms scalar/point
encoding conventions, group-element layout, and end-to-end
correctness of the vendoring.

Drift detection
---------------
None. We follow the same operational model as the Conan lockfile
under conan/lockfile/ (XRPLF#95): pin known-good state, re-sync
deliberately when there is a reason (security fix, performance
gain). No automated tripwire -- same as for Conan-managed
dependencies.

Out of scope (tracked separately)
---------------------------------
- mpt_msm_constant_time profile for the prover path: XRPLF#87.
- *_verify_batch API for the four sigma proofs and aggregated BP:
  XRPLF#88. (Design in cmpt-ct-and-batch.tex.)
- Lift the BP m in {1,2} aggregation restriction: XRPLF#46.
- rippled-side integration.
- Any change to the existing libsecp256k1 dep pin.
…RPLF#100)

Collapse secp256k1_bulletproof_verify_agg's two equality checks (the
range-relation LHS/RHS and the inner-product-collapsed P+IPA check)
into one mpt_msm_variable_time call that must return the identity.
Variable-time MSM dispatch via the vendored Pippenger/Straus ecmult
landed in PR XRPLF#89 (mpt-crypto-msm-vendor); this PR consumes it.

Equation. With a fresh Fiat-Shamir batching weight c (BBB+18-style
random-linear-combination), the check E1 + c*E2 = 0 unrolls to a
(2n + 2*log n + m + 6)-term MSM plus an optional G coefficient
(t_hat - delta) passed via inp_g_sc_be32. The H_k coefficient absorbs
the y^{-k} factor that was previously applied to a separately-built
Hprime vector, and the per-term s_k product (the IPA fold weight) is
computed inline matching fold_generators()'s G-fold pattern; s_k^{-1}
matches the H-fold pattern, recomputed directly to avoid n scalar
inversions.

Soundness. c is bound to the entire proof (last IPA round challenge
+ tau_x + mu + a + b) via SHA-256 with the dedicated tag
"MPT_BP_VERIFY_BATCH_RLC". A malicious prover that makes E1 and E2
individually non-zero would need to predict c before committing the
proof; Schwartz-Zippel gives the standard 1/q bound. The c == 0 case
(~1/2^256) is explicitly rejected to keep the soundness reasoning
clean.

Perf (Apple M-series, m=2, n=128, 5-iteration avg):
  before: 10.0 ms
  after:   1.68 ms
  speedup: ~6x

This exceeds the 2-4x estimate in XRPLF#100; the extra factor comes from
also folding the (m+4)-term range check and the rounds-many IPA
folding mults into the same MSM, where the constant-fan-out terms
contribute to the GLV/Pippenger amortisation.

Other changes.
* secp256k1_bulletproof_ipa_msm() is untouched; the prover's
  calculate_commitment_term() still routes through it. Verifier-only
  swap, prover CT contract preserved. The constant-time MSM profile
  for the prover is tracked separately in XRPLF#87.
* Static helper ipa_verify_explicit() removed; tests/test_ipa.c
  carries its own copy of the round-by-round IPA-verify check.
* fold_generators() and apply_ipa_folding_to_P() retained because
  tests/test_ipa.c still uses them.

Tests. 11/11 ctest green (test_bulletproof_agg covers positive +
negative paths for m in {1, 2} including v=0, v=1, v=UINT64_MAX, and
the two tampered-commitment cases that exercise the rejection branch
of the consolidated MSM).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bulletproof_aggregated_verify: use vendored MSM (mpt_msm_variable_time) for single-proof verification

1 participant