Skip to content

fix(pedersen): collapse to single canonical PEDERSEN_SEEDED_GEN_V1 DST (LP-137 N3)#1

Open
abhicris wants to merge 1 commit intoluxfi:mainfrom
abhicris:kcolb/pedersen-dst-collapse-n3
Open

fix(pedersen): collapse to single canonical PEDERSEN_SEEDED_GEN_V1 DST (LP-137 N3)#1
abhicris wants to merge 1 commit intoluxfi:mainfrom
abhicris:kcolb/pedersen-dst-collapse-n3

Conversation

@abhicris
Copy link
Copy Markdown

@abhicris abhicris commented May 1, 2026

Summary

Closes LP-137 RED-FINAL §2.4 / N3 — the Pedersen DST mismatch between the Go canonical and the C++/Metal/CUDA/WGSL implementations. The audit's normative ask:

Collapse Pedersen DST to one canonical `PEDERSEN_SEEDED_GEN_V1`.

Today, `pedersen.go::NewGenerators` and `pedersen.go::DeterministicGenerators` both used a two-DST scheme (`PEDERSEN_G_V1` / `PEDERSEN_H_V1`), which the C++ side does not match. `pedersen_seed.go::NewGeneratorsFromSeed` already had the canonical single-DST + counter-encoded scheme (matching `luxcpp/crypto/pedersen/cpp/pedersen.hpp:47` `DST_SEEDED_GEN`); this PR routes both legacy entry points through that path so there is exactly one derivation in the codebase.

What changes

`pedersen.go`

  • `NewGenerators(rng)` — read 32 bytes from `rng` as the seed; dispatch to `NewGeneratorsFromSeed`.
  • `DeterministicGenerators(seed)` — accept arbitrary-length input as before; compress via `SHA-256(seed)` to a 32-byte key; dispatch to `NewGeneratorsFromSeed`.
  • Both branches now produce `HashToG1(key || u64_le(i), DST=SeededGenDST)` with `i=0` for `G` and `i=1` for `H` — byte-equivalent to C++ `from_seed`.
  • Removed the `crypto/sha256` + `encoding/binary` imports and the per-counter retry loop they fed.

`pedersen_test.go`

  • KAT vectors (`katCases`) regenerated under the new derivation. The comment block now references `SeededGenDST` instead of the removed `PEDERSEN_G_V1`/`PEDERSEN_H_V1` strings.
  • New `TestCanonicalSeededDST` locks the on-wire DST literal — any future drift fails loudly before it can re-open N3 by breaking GPU↔CPU byte-equality.
  • New `TestDeterministicMatchesFromSeed` proves the `SHA-256(seed) → NewGeneratorsFromSeed` reduction explicitly, so the contract is enforced by code, not just docs.

`pedersen_seed.go` — untouched. It was already canonical; this PR makes the rest of the package agree with it.

Tests

$ go test ./pedersen/... -count 1 -v
=== RUN   TestNewGeneratorsFromSeed_Deterministic       PASS
=== RUN   TestNewGeneratorsFromSeed_DistinctSeeds       PASS
=== RUN   TestNewGeneratorsFromSeed_CommitsHomomorphic  PASS
=== RUN   TestNewGeneratorsFromSeed_GoldenVector        PASS   <- cross-lang anchor unchanged
=== RUN   TestKATs                                      PASS   <- regenerated vectors
=== RUN   TestCommitmentHomomorphism                    PASS
=== RUN   TestNewGeneratorsIndependent                  PASS
=== RUN   TestCommitBatch                               PASS
=== RUN   TestCommitBatchLengthMismatch                 PASS
=== RUN   TestCanonicalSeededDST                        PASS   <- NEW
=== RUN   TestDeterministicMatchesFromSeed              PASS   <- NEW
PASS
ok      github.com/luxfi/crypto/pedersen        0.647s

`go vet ./pedersen/...` clean. `go build ./pedersen/...` clean.

Verifying against C++ canonical

The cross-language anchor is `pedersen_seed_test.go::TestNewGeneratorsFromSeed_GoldenVector` (untouched), which freezes `G`'s coordinates derived from `SHA-256("lux-pedersen-seed-test-v1")`. The C++ `luxcpp/crypto/pedersen/test/pedersen_kat.h` uses the same `from_seed` path with a different test seed; mapping the seeds through `pedersen/test/tools/gen_pedersen_kat.go` (referenced by `pedersen_kat.h`) closes the cross-language KAT loop. That tool is not yet present in `luxfi/crypto/pedersen/` — happy to add it as a follow-up so future regenerations are a one-shot in either language.

Backwards compatibility

  • `NewGenerators(rng)` and `DeterministicGenerators(seed)` keep their existing signatures.
  • `SeededGenDST` and `NewGeneratorsFromSeed` are unchanged.
  • Output bytes change because the derivation changed — that is the intent of the audit fix. Any downstream consumer that pinned the old two-DST output must regenerate. No callers in this repo did.

Risks / scope notes

  • Verkle / IPA: Banderwagon Pedersen lives under a different namespace (LP-119 `Lux_Pedersen_H_Generator`). Out of scope.
  • Rust C-ABI: `luxfi/crypto/rust/lux-crypto-pedersen` links against `pedersen_cpu` (luxcpp), already canonical. Untouched.
  • Solidity precompiles: `luxfi/standard` defines precompile addresses only, no DST string. Untouched.
  • GPU kernels: take generators as input from the host, do not derive them (per LP-165 §2). Generator-source integrity is the host driver's contract, which this PR fixes on the Go side.

Authored by kcolbchain (@abhicris). Coordinated with the LP-137 reconciliation track.

Closes LP-137 RED-FINAL §2.4 (issue N3): collapse the legacy two-DST
PEDERSEN_G_V1 / PEDERSEN_H_V1 path to a single canonical
PEDERSEN_SEEDED_GEN_V1 with counter-indexed generation, matching the
C++ canonical at luxcpp/crypto/pedersen/cpp/pedersen.hpp:47.

Both NewGenerators(rng) and DeterministicGenerators(seed) now flow
through pedersen_seed.NewGeneratorsFromSeed:

  - NewGenerators reads 32 bytes from rng as the seed.
  - DeterministicGenerators compresses arbitrary-length input via
    SHA-256 to a 32-byte key, then dispatches.
  - Both branches use HashToG1(key || u64_le(i), DST=SeededGenDST)
    with i=0 for G and i=1 for H — byte-equivalent to C++ from_seed.

KAT vectors (pedersen_test.go::katCases) regenerated under the new
derivation. Two new tests:

  - TestCanonicalSeededDST locks the on-wire DST string so any drift
    fails loudly before it can break GPU↔CPU byte-equality.
  - TestDeterministicMatchesFromSeed proves DeterministicGenerators
    reduces to NewGeneratorsFromSeed(SHA-256(seed)) — the contract the
    post-N3 collapse promises.

All 11 pedersen tests pass; existing TestNewGeneratorsFromSeed_GoldenVector
remains the cross-language anchor (matches luxcpp pedersen_kat.h G[0].x).
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