Skip to content

feat(frost): real-crypto multi-node e2e + pre-merge cgo CI gate (handoff Items 1+2)#4102

Merged
mswilkison merged 2 commits into
feat/frost-schnorr-migration-scaffoldfrom
feat/frost-realcgo-multinode-e2e
Jun 21, 2026
Merged

feat(frost): real-crypto multi-node e2e + pre-merge cgo CI gate (handoff Items 1+2)#4102
mswilkison merged 2 commits into
feat/frost-schnorr-migration-scaffoldfrom
feat/frost-realcgo-multinode-e2e

Conversation

@mswilkison

@mswilkison mswilkison commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Pre-production handoff Items 1 and 2, bundled because the CI gate's whole purpose is to run the new e2e (and the gate's require-mode enforcement lives in the e2e's helper file).

Item 1 — real-crypto multi-node e2e

The union of the two half-faked e2es — roast_runner_bus_net_e2e (real runner+transport, fake engine) and roast_real_cgo_interactive_e2e (real engine, no runner/transport). Neither exercises the real-engine ⇄ real-pkg/net-transport ⇄ runner seam together; this does: n runners, each over its own real pkg/net bus, driving the real cgo engine to a real BIP-340 signature in one process (full-included 2-of-2 and t-of-included 3/threshold-2 subset).

  • Wiring discovery (no engine change): the Go RFC-21 election seeds on SHA256(dkgGroupPublicKey ‖ sessionID ‖ messageDigest) and the Rust engine on SHA256(keyGroup ‖ …); passing []byte(keyGroup) as the binding's dkgGroupPublicKey makes them agree, so the runner's engine-vs-binding coordinator cross-check — never before run against the real engine — passes.
  • Faithful shared-engine boundary surfaced by the seam: one process-global engine ⇒ the per-attempt aggregate idempotency marker lets exactly one seat aggregate the signature; co-resident seats observe interactive_attempt_already_aggregated after completing their full transport. In production each node has its own engine. Asserted precisely (exactly one winner + Succeeded; all others reached aggregate and hit the marker).

Item 2 — pre-merge cgo CI gate

.github/workflows/frost-cgo-integration.yml: checks out the Go branch + a pinned mirror commit (ci/frost-signer-pin.env), builds libfrost_tbtc, verifies exported frost_tbtc_* symbols, and runs the cgo-tagged tests with skips forbidden (KEEP_CORE_FROST_REQUIRE_CGO=true flips skipFrostUnavailable's lib-unavailable skip → fatal). An explicit SHA pin (not the moving tip) makes an engine/bridge drift fail loudly in one reviewed unit instead of silently dropping coverage. -Wl,--no-as-needed retains the DT_NEEDED under the bridge's dlsym(RTLD_DEFAULT) model.

Validation

  • Item 1: against the rebuilt lib both tests pass, -race -count=3 clean (exactly one winner, no races), skip without the lib, cgo vet + gofmt clean.
  • Item 2: require-mode fails without the lib (was a skip), skips when unset, passes with the lib; workflow YAML parses (7 steps); the verified ABI symbols are present in the built lib.
  • A CI workflow can only be fully exercised by running it on a PR — the require-mode mechanism + YAML are locally validated; the job itself runs here.

Deliberately deferred (noted in the handoff)

  • Production binary packaging (ship .so alongside keep-client, rpath, fail-closed startup preflight) — release-pipeline / MacLane.
  • Structured ABI-version assertion (the current frost_tbtc_version string isn't an ABI contract) — mirror.

CI does not otherwise build the cgo tags; without this gate the path is inert in CI. The gate is the fix.

🤖 Generated with Claude Code

… real engine

Item 1 of the pre-production handoff (design locked via Codex consult, shape A): the
union of the two half-faked e2es. roast_runner_bus_net_e2e proves runner+transport with
a FAKE engine; roast_real_cgo_interactive_e2e proves the REAL engine with no runner and
no transport. Neither exercises the real-engine <-> real-pkg/net-transport <-> runner
seam together. This does: n interactive signing runners, each over its own real pkg/net
BroadcastChannel bus, driving the real cgo engine to a real BIP-340 signature, in one
process (full-included 2-of-2 and t-of-included 3/threshold-2 subset).

Key wiring discovery (no engine change needed): the Go RFC-21 coordinator election seeds
on SHA256(dkgGroupPublicKey || sessionID || messageDigest), and the Rust engine seeds the
same shuffle on SHA256(keyGroup || sessionID || messageDigest) where keyGroup is the DKG
handle string. Passing []byte(keyGroup) as the binding's dkgGroupPublicKey makes the two
independent derivations agree, so the runner's engine-vs-binding coordinator cross-check
(never before exercised against the real engine) passes. RunDKG persists a resolvable key
group, so there is no loadInteractiveKeyGroup gap on this path.

Shape (A) shares ONE process-global engine across all seats (ENGINE_STATE is a
process-global OnceLock<Mutex>; the multi-seat fix #4098 is what lets one engine serve
every local member). The seam surfaced a real, correct boundary: the engine's aggregate
completion marker is per-ATTEMPT (attempt_id, message, root), not per-member, so the first
seat to aggregate produces the signature and co-resident seats observe
interactive_attempt_already_aggregated. That is faithful — in production each node has its
own engine — and every seat still drives its FULL transport (commitments + shares
broadcast/collected over real pkg/net) before aggregate. Assertion: exactly one seat
aggregates a real 64-byte signature and reaches Succeeded; every other seat reached
aggregate and hit the per-attempt idempotency marker (no other failure).

Skip-guarded (absent/stale libfrost_tbtc skips at the up-front DKG). Verified vs the
rebuilt lib: both tests pass, -race -count=3 clean (exactly one winner, no races), skip
without the lib, cgo vet + gofmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f0883f24-503a-4a40-b425-b633b926c114

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/frost-realcgo-multinode-e2e

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…crypto tests

Item 2 of the pre-production handoff (design locked via Codex consult). CI builds NONE
of the frost_native/frost_tbtc_signer/cgo tags, so the real-crypto interactive tests
(incl. the multi-node e2e in this PR) are validated locally only and can silently rot.

Adds .github/workflows/frost-cgo-integration.yml: it checks out the Go branch, checks
out a PINNED mirror commit (ci/frost-signer-pin.env - an explicit SHA, not the moving
tip, so an engine/bridge drift fails CI loudly in one reviewed unit instead of silently
turning the real-crypto tests into skips), builds libfrost_tbtc, verifies the exported
frost_tbtc_* ABI symbols, and runs the cgo-tagged tests with skips FORBIDDEN. The
require-cgo gate is KEEP_CORE_FROST_REQUIRE_CGO=true, which flips skipFrostUnavailable's
lib-unavailable SKIP to a FATAL - so an absent/stale/unloadable lib fails the job rather
than quietly dropping coverage. Linker note (Codex): -Wl,--no-as-needed retains the
DT_NEEDED on the lib even though the bridge references it only via dlsym(RTLD_DEFAULT).

The Go interactive code and the signer crate live on separate branches today, so the
gate checks the pinned mirror commit out into a side path and builds from there; after
the branches merge, swap the cross-branch checkout for an in-tree cargo build and keep
the gate.

Locally validated: require-mode FAILS without the lib (was a skip), SKIPS when unset,
PASSES with the lib; the workflow YAML parses (7 steps); the verified ABI symbols are
present in the built lib. A CI workflow can only be fully exercised by running it on a
PR. Production binary packaging + a structured ABI-version assertion are deliberately
left as follow-ups (release pipeline / mirror), noted in the handoff.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mswilkison mswilkison changed the title test(frost): real-crypto multi-node e2e — runners over real pkg/net + real engine feat(frost): real-crypto multi-node e2e + pre-merge cgo CI gate (handoff Items 1+2) Jun 21, 2026
@mswilkison mswilkison merged commit 8747c82 into feat/frost-schnorr-migration-scaffold Jun 21, 2026
16 of 17 checks passed
@mswilkison mswilkison deleted the feat/frost-realcgo-multinode-e2e branch June 21, 2026 19:02
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