Skip to content

feat(msm): vendor libsecp256k1 ecmult_multi_var as mpt_msm_variable_time#89

Open
tesseract-ripple wants to merge 1 commit into
XRPLF:mainfrom
tesseract-ripple:vendor-msm-pippenger
Open

feat(msm): vendor libsecp256k1 ecmult_multi_var as mpt_msm_variable_time#89
tesseract-ripple wants to merge 1 commit into
XRPLF:mainfrom
tesseract-ripple:vendor-msm-pippenger

Conversation

@tesseract-ripple

@tesseract-ripple tesseract-ripple commented May 12, 2026

Copy link
Copy Markdown
Collaborator

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 (#88).
Companion CT prover-side profile tracked in #87.

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 only; the
verifier has no secret inputs. The two-profile API shape
(mpt_msm_variable_time vs the planned mpt_msm_constant_time
#87) makes the constant-time requirement audit-visible at the call
site. Full threat-model treatment in cmpt-ct-and-batch.tex
(internal-ripplex-research, proposed-changes-writeup branch).

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 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.

All 11 existing tests continue to pass.

Drift detection

None. We follow the same operational model as the Conan lockfile
under conan/lockfile/ (#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)

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.
tesseract-ripple added a commit to tesseract-ripple/mpt-crypto that referenced this pull request May 29, 2026
…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).
tesseract-ripple added a commit to tesseract-ripple/mpt-crypto that referenced this pull request May 29, 2026
…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.

1 participant