Skip to content

Instantly share code, notes, and snippets.

@denniswon
Created March 7, 2026 02:08
Show Gist options
  • Select an option

  • Save denniswon/32ba5dad6d44ee72de34710735a8d03e to your computer and use it in GitHub Desktop.

Select an option

Save denniswon/32ba5dad6d44ee72de34710735a8d03e to your computer and use it in GitHub Desktop.
Newton Privacy Layer vs Fairblock: Technical Comparison Analysis
---
Newton Privacy Layer vs Fairblock: Technical Comparison Analysis
★ Insight ─────────────────────────────────────
This comparison covers two fundamentally different approaches to onchain privacy:
- Newton extends an existing EigenLayer AVS (policy evaluation) with privacy primitives — privacy is an add-on to an existing
validator network
- Fairblock is a purpose-built privacy infrastructure chain (FairyRing) that exports cryptographic services to other chains
- The cryptographic primitives overlap significantly (threshold DKG, IBE-like schemes), but the trust models and integration patterns
diverge sharply
─────────────────────────────────────────────────
---
1. Architecture Overview
Newton Privacy Layer
┌──────────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ Component │ Description │
├──────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ Secure Envelope │ HPKE (RFC 9180) encryption with AAD context binding (X25519/ChaCha20Poly1305) │
├──────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ Authorization │ Offchain Ed25519 consent signatures validated by gateway │
├──────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ Operator Network │ Existing EigenLayer AVS operators hold threshold key shares │
├──────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ Gateway │ Trusted intermediary (Phase 1.5) → replaced by threshold DKG (Phase 2) │
├──────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ Storage │ Encrypted data refs stored in gateway PostgreSQL (encrypted_data_refs table) │
└──────────────────┴───────────────────────────────────────────────────────────────────────────────┘
Data Flow (Phase 1.5 — current target):
Client → HPKE encrypt to gateway pubkey → store encrypted envelope in DB
→ createTask with encrypted_data_refs
→ Gateway decrypts (single trusted party)
→ Operators evaluate policy on plaintext
→ BLS aggregate signatures → respond on-chain
Data Flow (Phase 2 — future):
Client → HPKE encrypt to threshold pubkey (DKG-derived)
→ Prepare Phase: each operator produces partial decryption
→ t-of-n threshold decryption reconstructs plaintext
→ Operators evaluate policy → BLS sign → aggregate
Fairblock
┌───────────────────┬───────────────────────────────────────────────────────────────────────┐
│ Component │ Description │
├───────────────────┼───────────────────────────────────────────────────────────────────────┤
│ FairyRing │ Cosmos SDK app-chain for decryption key management │
├───────────────────┼───────────────────────────────────────────────────────────────────────┤
│ x/keyshare module │ Manages DKG, validator keyshare submission, aggregation │
├───────────────────┼───────────────────────────────────────────────────────────────────────┤
│ x/pep module │ Pre-execution privacy: stores encrypted txs, decrypts at target block │
├───────────────────┼───────────────────────────────────────────────────────────────────────┤
│ FairyPort │ IBC/cross-chain relayer for key distribution │
├───────────────────┼───────────────────────────────────────────────────────────────────────┤
│ FairyKits │ Integration SDKs for Cosmos, EVM, RustVM chains │
├───────────────────┼───────────────────────────────────────────────────────────────────────┤
│ Encryption SDK │ Client-side browser encryption (no third-party dependency) │
└───────────────────┴───────────────────────────────────────────────────────────────────────┘
Data Flow:
Client → encrypt tx with Master Public Key + condition ID (e.g. block height)
→ submit encrypted tx to destination chain (stored in x/pep)
→ FairyRing validators monitor conditions
→ When condition met: validators submit keyshares
→ On-chain aggregation (2/3+1 threshold)
→ Aggregated key relayed to destination chain
→ x/pep decrypts + executes tx at BeginBlock
---
2. Cryptographic Primitives Comparison
┌─────────────────┬──────────────────────────────────────────────────┬───────────────────────────────────────────────────────────┐
│ Dimension │ Newton Privacy Layer │ Fairblock │
├─────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Core Encryption │ HPKE (X25519, HKDF-SHA256, ChaCha20Poly1305) per │ Threshold IBE (Boneh-Franklin on BLS12-381) + hybrid │
│ │ RFC 9180 │ ChaCha20Poly1305 │
├─────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Key Exchange │ X25519 Diffie-Hellman (DHKEM) │ Identity-Based Encryption (condition = "identity") │
├─────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Threshold │ Pedersen DKG (Phase 2, planned) │ Joint Feldman DKG (implemented, live) │
│ Scheme │ │ │
├─────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Signature │ Ed25519 (authorization) + BLS (attestation) │ Cosmos validator signatures │
├─────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Advanced │ MPC computation + ZK selective disclosure (Phase │ FHE (CKKS), SPDZ MPC, Twisted ElGamal, witness encryption │
│ (future) │ 3) │ │
├─────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ AAD Binding │ Secure Envelope binds ciphertext to │ Condition ID binds to block height/price/event │
│ │ domain/policy/chain │ │
└─────────────────┴──────────────────────────────────────────────────┴───────────────────────────────────────────────────────────┘
★ Insight ─────────────────────────────────────
IBE vs HPKE is the key architectural divergence:
- Fairblock's IBE allows encrypting to a condition (block height, price, event) without knowing the decryption key in advance — the
key is derived later by validators. This enables batch decryption: one key decrypts ALL txs for that condition, saving N×S bandwidth
to just S.
- Newton's HPKE encrypts to a specific public key (gateway or threshold group). Decryption requires the key holder to actively
participate. Each envelope must be decrypted individually.
- Fairblock's IBE approach is provably more bandwidth-efficient for batch scenarios (their ePrint paper shows 99.6% bandwidth savings
vs standard threshold decryption for 1000 messages).
─────────────────────────────────────────────────
---
3. Trust Model Comparison
┌─────────────────┬─────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────┐
│ Dimension │ Newton │ Fairblock │
├─────────────────┼─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Phase 1.5 │ Gateway is a single trusted decryptor — │ N/A (already threshold from start) │
│ (current) │ full plaintext access │ │
├─────────────────┼─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Phase 2 │ t-of-n threshold — no single operator sees │ 2/3+1 validator threshold for key aggregation │
│ (planned) │ plaintext │ │
├─────────────────┼─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Validator Set │ EigenLayer restaked operators (ETH economic │ Cosmos SDK validators (staked FAIRY tokens) │
│ │ security) │ │
├─────────────────┼─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Slashing │ EigenLayer slashing conditions │ Custom slashing: slash_fraction_no_keyshare, │
│ │ │ slash_fraction_wrong_keyshare │
├─────────────────┼─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Key Custody │ Operators hold HPKE key shares (Phase 2) │ Share Generation Client creates MSK each epoch, splits to │
│ │ │ validators, destroys MSK │
├─────────────────┼─────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Compliance │ Offchain Ed25519 consent signatures; audit │ Selective per-transaction disclosure; OFAC/MiCA compatible │
│ │ trail │ │
└─────────────────┴─────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────┘
Newton's trust evolution:
- Phase 1.5: Centralized trust — gateway sees all plaintext (stepping stone)
- Phase 2: Threshold trust — t-of-n operators, no single party decrypts
- Phase 3: MPC + ZK — computation on encrypted data, selective disclosure
Fairblock's trust model:
- Threshold from day one — 2/3+1 validators must collude to compromise
- Epoch-based key rotation — MSK destroyed after distribution
- On-chain aggregation — key reconstruction happens transparently on FairyRing
---
4. Decryption Model Comparison
┌──────────────────┬──────────────────────────────────────────────────────┬──────────────────────────────────────────────────────┐
│ Dimension │ Newton │ Fairblock │
├──────────────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
│ Trigger │ Task creation (explicit RPC call) │ Condition satisfaction (block height, price, event, │
│ │ │ ZK proof) │
├──────────────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
│ Granularity │ Per-envelope (each encrypted data ref decrypted │ Per-condition (one key decrypts ALL txs under that │
│ │ individually) │ condition) │
├──────────────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
│ Timing │ On-demand during Prepare Phase │ Automatic at BeginBlock when condition met │
├──────────────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
│ Bandwidth │ O(N × t) for N envelopes with t-of-n threshold │ O(S) for S validators, regardless of N encrypted txs │
├──────────────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
│ Privacy Window │ Until operator decrypts during task evaluation │ Until condition is satisfied (can be indefinite) │
├──────────────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
│ Who Sees │ Operators (during policy evaluation) │ Destination chain (after BeginBlock decryption) │
│ Plaintext │ │ │
└──────────────────┴──────────────────────────────────────────────────────┴──────────────────────────────────────────────────────┘
★ Insight ─────────────────────────────────────
Fairblock's conditional decryption is fundamentally more expressive. Newton decrypts when someone requests it (consent-gated).
Fairblock decrypts when arbitrary on-chain conditions are met — block height, price feeds, ZK proof verification, governance vote
deadlines. This enables use cases like sealed-bid auctions and MEV protection that Newton's consent model doesn't naturally support.
However, Newton's consent model is more suited for compliance-oriented privacy — user controls who sees their data and when.
Fairblock's condition-based model is more suited for market fairness (everyone's data revealed simultaneously).
─────────────────────────────────────────────────
---
5. Cross-Chain & Integration
┌─────────────────┬───────────────────────────────────────────────────┬──────────────────────────────────────────────────────────┐
│ Dimension │ Newton │ Fairblock │
├─────────────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Native Chain │ No own chain — runs on EigenLayer (Ethereum) │ FairyRing (Cosmos SDK app-chain) │
├─────────────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ EVM Support │ Native (ECS Fargate services on Ethereum/L2s) │ Via FairyKits + precompiles embedded in EVM │
├─────────────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Cosmos Support │ Not supported │ Native via x/pep module + IBC │
├─────────────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Solana Support │ Not supported │ Planned │
├─────────────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Cross-Chain │ Newton's multichain architecture (source → │ FairyPort (IBC relayer) + direct FairyKit integration │
│ Relay │ destination chains) │ │
├─────────────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Integration │ SDK extension (@newton-protocol/sdk) + RPC calls │ FairyKit deployment + module integration or precompile │
│ Effort │ │ │
├─────────────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ User Experience │ Encrypt via SDK, submit via RPC │ Encrypt in browser with Encryption SDK, submit to │
│ │ │ destination chain directly │
└─────────────────┴───────────────────────────────────────────────────┴──────────────────────────────────────────────────────────┘
---
6. Product & Use Case Comparison
┌────────────────────────────┬─────────────────────────────────────────────────┬──────────────────────────────────────────────────┐
│ Use Case │ Newton │ Fairblock │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Private policy evaluation │ Core use case — privacy for Rego policy inputs │ Not applicable │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Sealed-bid auctions │ Not natively supported │ Core use case — condition-based reveal │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ MEV/frontrunning │ Not designed for this │ Core use case — pre-execution encryption │
│ protection │ │ │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Private governance │ Possible via privacy extension │ Native support via encrypted votes │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Confidential stablecoins │ Not in scope │ StableTrust product (Twisted ElGamal + FHE) │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Compliance/KYC privacy │ Core design — consent-gated selective │ Supported via post-execution selective │
│ │ disclosure │ disclosure │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ DeFi order privacy │ Possible but not primary │ Protected Trading product │
├────────────────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Private AI inference │ Not in scope │ Planned (FHE + MPC) │
└────────────────────────────┴─────────────────────────────────────────────────┴──────────────────────────────────────────────────┘
---
7. Maturity & Implementation Status
┌────────────────────────────┬──────────────────────────────────────────────────┬─────────────────────────────────────────────────┐
│ Dimension │ Newton │ Fairblock │
├────────────────────────────┼──────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Phase 1 (Foundation) │ 88% complete — HPKE, Ed25519, Secure Envelope, │ ✅ Live — IBE, DKG, keyshare aggregation, │
│ │ DB storage │ public testnet │
├────────────────────────────┼──────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Phase 1.5 (Gateway │ 25% complete — gateway as trusted decryptor │ N/A (skipped centralized step entirely) │
│ Decryption) │ │ │
├────────────────────────────┼──────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Phase 2 (Threshold DKG) │ 0% — Pedersen DKG, threshold decryption planned │ ✅ Live — Joint Feldman DKG with validator key │
│ │ │ shares │
├────────────────────────────┼──────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Phase 3 (MPC/ZK) │ 0% — MPC framework, ZK selective disclosure │ In progress — CKKS FHE, SPDZ, confidential │
│ │ planned │ transfers │
├────────────────────────────┼──────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Phase 4 (Ecosystem) │ 0% — L2 deployment, operator onboarding planned │ Active — Arbitrum, CoW Swap, AltLayer │
│ │ │ integrations │
├────────────────────────────┼──────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Funding │ Self-funded (Magic Labs / Newton Foundation) │ $2.5M pre-seed (Galileo, Lemniscap, Dialectic, │
│ │ │ etc.) │
├────────────────────────────┼──────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Team/Origin │ Extension of existing Newton Prover AVS │ Academic research from U of Waterloo (ePrint │
│ │ │ 2022/1066) │
└────────────────────────────┴──────────────────────────────────────────────────┴─────────────────────────────────────────────────┘
---
8. Pros & Cons
Newton Privacy Layer
Pros:
1. Leverages existing infrastructure — EigenLayer economic security, existing operator set, existing BLS aggregation. No need to
bootstrap a new validator set.
2. Compliance-first design — consent-based authorization with Ed25519 signatures and audit trails is naturally suited for regulated
use cases (KYC/AML).
3. Progressive decentralization — pragmatic phased approach (gateway → threshold → MPC) reduces initial complexity and time-to-market.
4. Standards-based cryptography — HPKE (RFC 9180) is a well-audited, widely-implemented standard. No novel cryptographic assumptions.
5. Deep integration with policy evaluation — privacy is not a standalone product but an enhancement to an existing product with users
(Rego policy engine).
6. Ethereum-native — no bridging, no Cosmos dependencies, no additional token.
Cons:
1. Phase 1.5 is centralized — gateway as single decryptor is a significant trust assumption. One compromise exposes all privacy data.
2. No conditional decryption — decrypt-on-demand model doesn't support time-locked or condition-gated reveals (sealed auctions, MEV
protection).
3. Per-envelope decryption overhead — each encrypted ref must be decrypted individually; no batch efficiency like IBE provides.
4. Narrow use case — primarily designed for private policy inputs. Not a general-purpose privacy layer for DeFi, governance, or
stablecoins.
5. DKG not yet implemented — Phase 2 threshold operations are planned but 0% complete. The real decentralization story is still
theoretical.
6. No own execution layer — depends on ECS Fargate infrastructure and gateway availability. Not censorship-resistant at the protocol
level.
Fairblock
Pros:
1. Threshold from genesis — never had a centralized decryption phase. 2/3+1 validator threshold from day one.
2. IBE bandwidth efficiency — one decryption key covers ALL transactions under a condition. 99.6% bandwidth savings vs per-tx
threshold decryption (proven in ePrint paper).
3. Expressive decryption conditions — block height, price, events, ZK proofs, custom conditions. Enables sealed auctions, MEV
protection, private governance natively.
4. Multimodal cryptography — IBE, FHE (CKKS), MPC (SPDZ), witness encryption, Twisted ElGamal — selects the right tool per use case.
5. Purpose-built chain — FairyRing optimized for cryptographic operations with native BeginBlock decryption and execution.
6. Broad ecosystem — Cosmos, EVM (Arbitrum), rollups (AltLayer), CoW Swap integration, Solana planned.
7. Academic foundation — published research (ePrint 2022/1066), formal security analysis.
Cons:
1. Requires own chain — FairyRing is an additional trust assumption and infrastructure dependency. Users/apps depend on Cosmos
validator liveness.
2. New token requirement — validators stake FAIRY tokens; adds token economics complexity and bootstrapping challenge.
3. Cross-chain latency — IBC/FairyPort relaying introduces latency between condition satisfaction and destination chain execution.
4. Not Ethereum-native — EVM support via precompiles/FairyKits, but it's fundamentally a Cosmos-first architecture. Ethereum
integration is bolt-on.
5. Centralized key generation step — Share Generation Client creates full MSK before splitting. If compromised during this brief
window, all keys are exposed (mitigated by destroying MSK after distribution, but the window exists).
6. Complexity — multimodal cryptography means larger attack surface and more code to audit across IBE, FHE, MPC, and ZK subsystems.
7. No consent-based model — condition-gated decryption means data is revealed when conditions are met, regardless of whether the data
owner still wants disclosure. Less suited for user-sovereign privacy.
---
9. What We Can Learn from Fairblock
Architectural Lessons
1. Skip the centralized stepping stone. Fairblock launched with threshold DKG from day one. Our Phase 1.5 (gateway as trusted
decryptor) is a pragmatic shortcut, but it introduces a real vulnerability window and delays the trust-minimized story. Consider
accelerating Phase 2 DKG to reduce time spent in the centralized trust model.
2. Conditional decryption is a powerful primitive. Newton's consent-based model ("user signs to authorize decryption") is great for
compliance but limited for market fairness use cases. Consider adding condition-gated decryption as a complementary mode — e.g.,
"decrypt this data at block height X" or "decrypt when price oracle reports Y." This could open sealed-auction and MEV-protection use
cases without building a separate product.
3. Batch decryption via IBE. Our per-envelope HPKE decryption scales as O(N×t). Fairblock's IBE allows one key to decrypt all N
envelopes under the same condition. If we ever need to decrypt many privacy envelopes simultaneously (e.g., batch policy evaluation),
IBE-style schemes would be significantly more efficient. Worth evaluating for Phase 3.
4. On-chain key aggregation. Fairblock aggregates keyshares on-chain (in the x/keyshare module), making the process transparent and
verifiable. Our Phase 2 plans to aggregate partial decryptions off-chain during the Prepare Phase. Consider whether some aggregation
should be verifiable on-chain for auditability.
5. Epoch-based key rotation with MSK destruction. Fairblock's pattern of creating a new MSK each epoch, splitting it, and destroying
the original is a clean forward-secrecy model. Our Phase 2 key rotation (NEWT-174/NEWT-189) should adopt a similar
destroy-after-distribution pattern.
6. Validator slashing for privacy failures. Fairblock slashes validators who fail to submit keyshares or submit wrong ones
(slash_fraction_no_keyshare, slash_fraction_wrong_keyshare). Our EigenLayer operators don't currently have privacy-specific slashing
conditions. Phase 2 should define slashing for operators who fail to provide partial decryptions or provide incorrect ones.
Product Lessons
7. Confidential stablecoins are a market opportunity. Fairblock's StableTrust product targets a clear institutional need. Newton's
privacy layer could potentially be extended to support private transfer amounts in policy evaluation (e.g., "approve this transfer
without revealing the amount to validators").
8. Client-side encryption SDK. Fairblock emphasizes that encryption happens entirely in the browser with no third-party dependency.
Our Newton SDK (NEWT-182) should similarly ensure all HPKE encryption is client-local with no server round-trips for the encryption
step.
9. Precompile-based integration for EVMs. Fairblock embeds cryptographic operations as EVM precompiles, making integration as simple
as calling a contract. For Phase 4 L2 deployment, consider whether Newton privacy operations could be offered as precompiles rather
than requiring SDK integration.
What NOT to Copy
10. Don't build a separate chain. Fairblock's FairyRing adds significant infrastructure and token economics overhead. Newton's
strength is being Ethereum-native on EigenLayer — no bridging, no new token, no Cosmos dependency. Keep it that way.
11. Don't over-generalize. Fairblock tries to be everything (IBE + FHE + MPC + ZK + witness encryption). Newton should stay focused on
consent-gated privacy for policy evaluation and expand narrowly to compliance use cases rather than trying to cover sealed auctions,
MEV protection, and confidential stablecoins simultaneously.
---
10. Strategic Positioning
┌─────────────────────────────────────────────┐
│ PRIVACY DESIGN SPECTRUM │
│ │
User-Sovereign │ Newton ◄────────────────► Fairblock │
(Consent-Based) │ "I choose who sees" "Conditions decide"│
│ │
Compliance-First │ Newton ████████████░░ Fairblock ████░░░░ │
│ │
Market Fairness │ Newton ███░░░░░░░░░░ Fairblock █████████ │
│ │
Ethereum-Native │ Newton ██████████████ Fairblock ███░░░░░ │
│ │
Multi-Ecosystem │ Newton ███░░░░░░░░░░ Fairblock █████████ │
│ │
Production Ready │ Newton ████░░░░░░░░░ Fairblock ████████░ │
└─────────────────────────────────────────────┘
Bottom line: Newton and Fairblock occupy complementary niches. Newton is consent-based, compliance-first, Ethereum-native privacy for
policy evaluation. Fairblock is condition-based, market-fairness-first, multi-ecosystem privacy for DeFi/trading. The biggest
actionable takeaways for Newton are: (1) accelerate Phase 2 DKG to exit the centralized trust model, (2) consider adding conditional
decryption as a complementary mode, and (3) adopt epoch-based key rotation with MSK destruction from the start.
@denniswon
Copy link
Author

PR created: https://github.com/newt-foundation/newton-prover-avs/pull/426

★ Insight ─────────────────────────────────────
Threshold decryption integration architecture: The key design choice here is the fallback pattern — when threshold
prerequisites aren't met (no threshold_ctx, insufficient partials, deserialization failure), the gateway
seamlessly falls back to centralized HPKE decryption (Phase 1 behavior). This means the feature can be deployed
without breaking existing privacy flows. Operators without threshold key shares simply don't return
partial_decryption data, and the gateway handles this gracefully.

Wire type separation: The PartialDecryptionData (serde-friendly Vec fields) vs dkg::PartialDecryption (curve
point/scalar types) split is intentional — JSON-RPC can't natively serialize Edwards points, so the wire format
uses raw bytes with From/TryFrom conversions at the boundary. This keeps crypto types out of the network layer.
─────────────────────────────────────────────────

Here's a summary of what NEWT-178 now covers across the two commits:

┌──────────┬─────────┬───────────────────────────────────────────────────────────────────────────────────┐
│ Commit │ Layer │ What │
├──────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────┤
│ b23496c1 │ Layer 1 │ Decouple privacy data from policyTaskData — separate privacy_data channel │
├──────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────┤
│ a93aad57 │ Layer 2 │ Threshold decryption integration — operator partial DH + gateway Lagrange combine │
└──────────┴─────────┴───────────────────────────────────────────────────────────────────────────────────┘

Remaining for full Phase 2 completion (separate tickets):

  • NEWT-630: E2E test for threshold privacy + two-phase consensus
  • NEWT-629: Gateway threshold config loading from DKG ceremony output (Phase 2C)
  • Phase 2B: PSS epoch rotation, EpochRegistry contract
  • Phase 2C: FROST ceremony replacing trusted dealer

@denniswon
Copy link
Author


Current Status: Phase 2 Threshold DKG

Branch/PR Stack (bottom to top)

main
└── #421 privacy-data-flow-fix ← fix: privacy data via separate channel
└── #422 multichain-routing-e2e-options ← feat: two-phase + attestation E2E options
└── #423 unified-e2e-runner ← feat: unified E2E with scenario presets
└── #424 phase2-threshold-dkg-docs ← docs: THRESHOLD_DKG.md
└── threshold-dkg ← empty (merge point, no PR yet)
└── #425 threshold-dkg-phase2a ← NEWT-176: Feldman VSS + DLEQ primitives
└── #426 threshold-consensus-integration ← NEWT-178: Two-Phase Consensus wiring

What's Implemented (3 commits, 3 branches)

Commit: 9ecf60ba
Branch: threshold-dkg-phase2a (PR #425)
Linear Ticket: NEWT-176
What: DKG primitives: crates/core/src/dkg/ — Feldman VSS, DLEQ proofs, Lagrange interpolation,
threshold_decrypt(),
compute_partial_decryption(), ThresholdConfig, KeyShare, unit tests
────────────────────────────────────────
Commit: b23496c1
Branch: threshold-consensus-integration (PR #426)
Linear Ticket: NEWT-178 (Layer 1)
What: Decouple privacy data from policyTaskData — separate ConsensusCommitRequest.privacy_data channel
────────────────────────────────────────
Commit: a93aad57
Branch: threshold-consensus-integration (PR #426)
Linear Ticket: NEWT-178 (Layer 2)
What: Wire threshold decryption into Two-Phase Consensus: operator computes partial DH in Prepare, gateway
combines
via Lagrange + fallback to centralized

Linear Ticket Status

┌──────────┬───────────────────────────────────┬─────────────┬────────────────────────────────────────────────┐
│ Ticket │ Title │ Status │ Notes │
├──────────┼───────────────────────────────────┼─────────────┼────────────────────────────────────────────────┤
│ │ HPKE crate augmentation — │ │ Implemented in │
│ NEWT-638 │ open_with_precomputed_dh │ Done │ crates/core/src/crypto/threshold.rs (post-DH │
│ │ │ │ pipeline) │
├──────────┼───────────────────────────────────┼─────────────┼────────────────────────────────────────────────┤
│ NEWT-176 │ Threshold decryption (t-of-n) — │ In Review │ Core DKG primitives, feature-gated behind │
│ │ Feldman VSS + DLEQ │ (PR #425) │ threshold │
├──────────┼───────────────────────────────────┼─────────────┼────────────────────────────────────────────────┤
│ NEWT-178 │ Threshold Decryption for │ In Review │ Operator + gateway wiring, fallback to │
│ │ Two-Phase Consensus │ (PR #426) │ centralized │
├──────────┼───────────────────────────────────┼─────────────┼────────────────────────────────────────────────┤
│ NEWT-177 │ DKG protocol — FROST on │ Backlog │ Phase 2C — full FROST ceremony replacing │
│ │ Ristretto255 │ │ trusted dealer │
├──────────┼───────────────────────────────────┼─────────────┼────────────────────────────────────────────────┤
│ NEWT-628 │ Epoch-based key rotation with PSS │ Backlog │ Phase 2B │
├──────────┼───────────────────────────────────┼─────────────┼────────────────────────────────────────────────┤
│ NEWT-630 │ On-chain aggregation commitments │ Backlog │ Phase 2C — depends on NEWT-178 │
├──────────┼───────────────────────────────────┼─────────────┼────────────────────────────────────────────────┤
│ NEWT-629 │ Privacy-specific operator │ Backlog │ Phase 2D — depends on NEWT-630 + NEWT-628 │
│ │ slashing │ │ │
└──────────┴───────────────────────────────────┴─────────────┴────────────────────────────────────────────────┘

What's NOT Done Yet

Phase 2A remaining:

  1. E2E test for threshold privacy + two-phase consensus — verifies the full pipeline with a trusted dealer
    distributing shares, operators computing partials, gateway combining
  2. Gateway threshold config loading — threshold_ctx is hardcoded to None; needs to be populated from a config file
    or DKG ceremony output
  3. Operator non-RPC path — core.rs:fetch_policy_data() (non-two-phase operator flow) doesn't have enc_point +
    key_share wiring yet

Phase 2B (Weeks 5-7): PSS epoch rotation, EpochRegistry contract, destruction attestation

Phase 2C (Weeks 8-10): Full FROST ceremony (replace trusted dealer), on-chain Merkle root commitments, EigenDA
share storage

Phase 2D (Weeks 11-12): PrivacySlasher contract, DleqVerifier library (EIP-196), liveness slashing

Dependency Graph — Progress Overlay

NEWT-638 ✅ → NEWT-176 🔄 → NEWT-178 🔄 → NEWT-630 ⬜ → NEWT-629 ⬜
NEWT-177 ⬜ ──────────────↗ ↗
NEWT-177 ⬜ → NEWT-628 ⬜ ─────────────────────────↗

✅ = Done, 🔄 = In Review, ⬜ = Backlog

Recommended Next Steps (in order)

  1. Get PRs #421 → #422 → #423 → #424 merged — these are the base of the stack and need to land on main before the
    DKG PRs can merge
  2. Write Phase 2A E2E test — trusted dealer distributes Feldman VSS shares to 3 operators, run threshold privacy +
    two-phase consensus E2E, verify decryption works
  3. Add gateway threshold config loading — load ThresholdDecryptionContext from a JSON config file (operator public
    shares, threshold params) so the gateway can actually use threshold mode
  4. Update Linear tickets — mark NEWT-638 as Done, move NEWT-176 and NEWT-178 to In Review
  5. Phase 2B kickoff — NEWT-628 (PSS rotation) and NEWT-639 (EpochRegistry contract) can start once 2A is merged

★ Insight ─────────────────────────────────────
Phase 2A is architecturally complete — all the crypto primitives (Feldman VSS, DLEQ proofs, Lagrange
interpolation) and the Two-Phase Consensus wiring (operator partial DH + gateway combine + fallback) are
implemented and compiling. What remains is operationalization: config loading, E2E testing, and getting the base
PR stack merged. The critical path blocker is the 6-PR stack depth — PRs #421-#424 need to land before the DKG
code can reach main.

The fallback pattern is key to safe rollout: even after deploying threshold code, the gateway falls back to
centralized HPKE decryption when threshold_ctx is None or insufficient partials arrive. This means threshold mode
can be enabled per-deployment without breaking existing privacy flows.
─────────────────────────────────────────────────

@denniswon
Copy link
Author

★ Insight ─────────────────────────────────────

Three fixes applied to get clean compilation:

  1. Vec → &[u8; 32] (line 194): hex::decode returns Vec, but montgomery_to_edwards expects a fixed-size
    array reference. The .as_slice().try_into() pattern is idiomatic Rust for this conversion — TryFrom<&[u8]> for
    &[u8; N] checks length at runtime and returns an error if it doesn't match.
  2. Ambiguous AsRef (line 422): alloy::primitives::FixedBytes<32> (returned by keccak256) implements both
    AsRef<[u8; 32]> and AsRef<[u8]>. hex::encode accepts AsRef<[u8]>, but the compiler can't choose between the two
    AsRef impls. Using .as_slice() explicitly selects the &[u8] variant.
  3. Dead imports: ed25519_to_x25519_public, reqwest::Client, serde_json::Value, and uuid::Uuid were imported but
    unused. The test utilities use the privacy module's upload_encrypted_data (which owns the HTTP client internally)
    rather than constructing one directly.

─────────────────────────────────────────────────

Here's a summary of everything delivered:

File: integration-tests/src/threshold.rs
Change: New — DKG ceremony, threshold envelope creation, smoke test, full E2E runner (~467 lines)
────────────────────────────────────────
File: integration-tests/tests/threshold_e2e.rs
Change: New — test harness on port 8086
────────────────────────────────────────
File: integration-tests/src/lib.rs
Change: Added pub mod threshold;
────────────────────────────────────────
File: integration-tests/Cargo.toml
Change: Added threshold feature to core/gateway/operator deps
────────────────────────────────────────
File: crates/gateway/tests/{send_task,secrets_encrypted,chain_routing,watcher_endpoint}.rs
Change: Added #[cfg(feature = "threshold")] threshold: None
────────────────────────────────────────
File: Makefile
Change: Added threshold-e2e target with TWO_PHASE_CONSENSUS=true PRIVACY_ENABLED=true

Run with: make threshold-e2e

@denniswon
Copy link
Author

★ Insight ─────────────────────────────────────

Threshold decryption and multichain routing are orthogonal concerns. Threshold is about who decrypts (centralized
gateway vs distributed operators). Multichain routing is about where tasks go (which chain's ChainService handles
the task). The threshold code path in sync.rs (extract_enc_point_for_threshold, try_threshold_decrypt) is
identical regardless of which chain the task targets — it operates on the ChainServiceCtx.threshold_ctx which is
cloned from the gateway-level config to every chain service.

─────────────────────────────────────────────────

My recommendation: skip integration into multichain-routing-e2e for now. Here's why:

  1. Config construction mismatch: multichain-routing-e2e uses MultichainRoutingConfig →
    MultiChainConfig::from_network() → Gateway::from_multi_chain_config(). Injecting ThresholdGatewayConfig into that
    pipeline requires modifying MultichainRoutingConfig and the MultiChainConfig builder — nontrivial plumbing for a
    feature that's still Phase 2A.
  2. Encryption target changes: Privacy in multichain-routing encrypts to the gateway's ECDSA-derived HPKE key.
    Threshold encrypts to the DKG master public key. These are different keys with different derivation paths.
    Supporting both in one test means conditional encryption logic and more config branching.
  3. No new code path exercised: The threshold logic in sync.rs doesn't branch on chain_id or multichain mode. If
    threshold works for chain 31337, it works identically for chain 31338 — the same threshold_ctx is shared across
    all ChainService instances.
  4. Phase 2C is the right time: When FROST DKG replaces the trusted dealer, the DKG ceremony itself becomes
    multichain-aware (operators on different chains participate). That's when integrating into multichain-routing-e2e
    makes architectural sense.

What threshold-e2e tests (the current spec)

The test runs two phases:

Phase A — In-process crypto smoke test (no infrastructure)

  • Runs 2-of-3 Feldman VSS DKG ceremony
  • Encrypts test data ({"kyc_verified": true, "risk_score": 42}) to the threshold MPK
  • Computes 2 partial decryptions from shares 1 and 3 (any 2-of-3 subset)
  • Combines via Lagrange interpolation and verifies decrypted plaintext matches original
  • Validates the full crypto pipeline works correctly before touching any infrastructure

Phase B — Full E2E with gateway + operator (requires anvil + Postgres)

  • Runs 1-of-1 DKG ceremony (single operator can satisfy threshold)
  • Injects ThresholdGatewayConfig into gateway and ThresholdOperatorConfig into operator
  • Starts operator with key share on port 127.0.0.1:9013
  • Starts gateway on port 8086 with threshold decryption context
  • Encrypts privacy data to threshold MPK (not gateway's centralized key)
  • Uploads encrypted data via newt_uploadEncryptedData
  • Computes dual Ed25519 signatures (user + app) for privacy authorization
  • Submits task with encrypted_data_refs + use_two_phase = true
  • Verifies: task succeeds, BLS aggregation response is valid
  • During Prepare phase: operator receives enc_point, computes partial DH + DLEQ proof
  • During Commit: gateway combines partials, decrypts privacy data, passes via ConsensusCommitRequest.privacy_data

Run with: make threshold-e2e

@denniswon
Copy link
Author


SDK Privacy PRs — Protocol Alignment Analysis

PR #115 (feature/sdk-privacy-module) — HPKE Encryption

┌────────────────────────────────────┬────────┬───────────────────────────────────────────────────────────────────────────────┐
│ Check │ Status │ Detail │
├────────────────────────────────────┼────────┼───────────────────────────────────────────────────────────────────────────────┤
│ HPKE suite │ Match │ Both: X25519 KEM + HKDF-SHA256 + ChaCha20-Poly1305, Base mode │
├────────────────────────────────────┼────────┼───────────────────────────────────────────────────────────────────────────────┤
│ HPKE info param │ Match │ Both pass empty bytes (&[] / new Uint8Array(0)) │
├────────────────────────────────────┼────────┼───────────────────────────────────────────────────────────────────────────────┤
│ AAD computation │ Match │ Both: keccak256(encodePacked(policy_client_bytes, chain_id_be_u64)), strip 0x │
│ │ │ prefix │
├────────────────────────────────────┼────────┼───────────────────────────────────────────────────────────────────────────────┤
│ SecureEnvelope fields │ Match │ Identical: enc, ciphertext, policy_client, chain_id, recipient_pubkey │
├────────────────────────────────────┼────────┼───────────────────────────────────────────────────────────────────────────────┤
│ UploadEncryptedDataRpcRequest │ Match │ All 7 fields match Rust's UploadEncryptedDataRequest including required │
│ fields │ │ chain_id │
├────────────────────────────────────┼────────┼───────────────────────────────────────────────────────────────────────────────┤
│ chain_id required (not optional) │ Match │ Both sides: required u64/number per PR #421 fix │
└────────────────────────────────────┴────────┴───────────────────────────────────────────────────────────────────────────────┘

★ Insight ─────────────────────────────────────
The AAD computation is the most critical interop point. If SDK and Rust compute different AAD, the gateway cannot decrypt what the
SDK encrypts. Both implementations: (1) strip 0x prefix from policy_client, (2) decode to raw address bytes (20 bytes), (3)
encode chain_id as big-endian u64 (8 bytes), (4) concatenate, (5) keccak256. The SDK uses DataView.setUint32 twice for the u64 to
avoid BigInt — clever and correct.
─────────────────────────────────────────────────

PR #116 (feature/sdk-privacy-apis) — Dual Signatures + Key Generation

┌───────────────────────────────────────┬────────────┬────────────────────────────────────────────────────────────────────────┐
│ Check │ Status │ Detail │
├───────────────────────────────────────┼────────────┼────────────────────────────────────────────────────────────────────────┤
│ User message construction │ Match │ Both: encodePacked(policy_client, intent_hash, ref_id_1, ref_id_2, │
│ │ │ ...) where ref_ids are UTF-8 bytes │
├───────────────────────────────────────┼────────────┼────────────────────────────────────────────────────────────────────────┤
│ App message construction │ Match │ Both: encodePacked(policy_client, intent_hash, user_signature_bytes) │
├───────────────────────────────────────┼────────────┼────────────────────────────────────────────────────────────────────────┤
│ Digest computation │ Match │ Both: keccak256(message) then Ed25519 sign the 32-byte digest │
├───────────────────────────────────────┼────────────┼────────────────────────────────────────────────────────────────────────┤
│ Ed25519 sign/verify │ Compatible │ Rust: ed25519_dalek, SDK: @noble/curves/ed25519 — both implement │
│ │ │ standard Ed25519 (RFC 8032) │
├───────────────────────────────────────┼────────────┼────────────────────────────────────────────────────────────────────────┤
│ Key zeroing │ Match │ Both zeroize private key bytes after use │
├───────────────────────────────────────┼────────────┼────────────────────────────────────────────────────────────────────────┤
│ SubmitEvaluationRequestParams privacy │ Match │ encryptedDataRefs, userSignature, appSignature — all optional, │
│ fields │ │ matching Rust's CreateTaskRequest │
└───────────────────────────────────────┴────────────┴────────────────────────────────────────────────────────────────────────┘

PR #117 (feature/sdk-privacy-docs) — Documentation

┌─────────────────────────────────────┬─────────┬─────────────────────────────────────────────────────────────────────┐
│ Check │ Status │ Detail │
├─────────────────────────────────────┼─────────┼─────────────────────────────────────────────────────────────────────┤
│ Architecture diagram (README.md) │ Correct │ Shows gateway decrypts, broadcasts plaintext to operators │
├─────────────────────────────────────┼─────────┼─────────────────────────────────────────────────────────────────────┤
│ Signature scheme docs (concepts.md) │ Correct │ Accurately describes user + app dual-signature construction │
├─────────────────────────────────────┼─────────┼─────────────────────────────────────────────────────────────────────┤
│ AAD binding explanation │ Correct │ Correctly explains keccak256(encodePacked(policy_client, chain_id)) │
├─────────────────────────────────────┼─────────┼─────────────────────────────────────────────────────────────────────┤
│ Phase 1 limitations │ Correct │ Documents centralized gateway trust point, Phase 2 threshold DKG │
└─────────────────────────────────────┴─────────┴─────────────────────────────────────────────────────────────────────┘


Issues Found

  1. Stale doc: "merged into evaluation context" (PR #117, examples.md)

The last line of Example 4 in examples.md:

"The Rego policy receives all decrypted data merged into its evaluation context, so it can cross-reference identity and financial
information in a single evaluation pass."

After PR #421, privacy data is namespaced under data.data.privacy in the Rego context — it is NOT flat-merged into policyTaskData.
Data flows through the separate ConsensusCommitRequest.privacy_data channel. The statement should say something like: "The Rego
policy receives all decrypted data under data.data.privacy in the evaluation context."

Severity: Low — describes the internal data layout that SDK consumers don't directly control, but it could mislead Rego policy
authors about where to find privacy fields.

  1. recipient_pubkey naming confusion (both SDK and Rust)

In UploadEncryptedDataRequest/UploadEncryptedDataRpcRequest, the field recipient_pubkey actually receives the sender's
(uploader's) Ed25519 public key — used by the gateway to verify the envelope signature. The Rust doc comment says "Recipient
operator Ed25519 public key" which is inaccurate.

In the SDK: recipient_pubkey: senderPublicKey — the variable name makes the confusion explicit.

Meanwhile, SecureEnvelope.recipient_pubkey stores the gateway's X25519 key (the actual encryption recipient).

Two fields named recipient_pubkey meaning different things in different structs.

Severity: Low — functionally correct (SDK and Rust agree on what goes where), but naming is misleading. Should be renamed to
sender_pubkey or signer_pubkey on the upload request to distinguish from the envelope's recipient_pubkey. This should be a
coordinated rename across both repos.

  1. Missing user_pubkey/app_pubkey in CreateTaskRequest (known gap)

The Rust validate_privacy_signatures() requires user_pubkey: &[u8; 32] and app_pubkey: &[u8; 32], but:

  • CreateTaskRequest only has user_signature and app_signature — no public key fields
  • validate_privacy_signatures is not yet called from the task creation handler in sync.rs
  • The SDK's SubmitEvaluationRequestParams also has no public key fields

When the gateway enforces signature validation (presumably before production launch), either:

  • Both Rust CreateTaskRequest and SDK SubmitEvaluationRequestParams will need user_pubkey and app_pubkey fields added
  • OR the gateway derives user_pubkey from the upload record's recipient_pubkey (stored during uploadEncryptedData) and adds
    app_pubkey to the request only

The SDK already computes and returns both public keys from signPrivacyAuthorization() (userPublicKey, appPublicKey), so the data
is available — it just doesn't flow into the task submission request yet.

Severity: Medium — not broken today (validation isn't enforced), but will require a coordinated SDK + gateway change before
signature enforcement goes live. Track this.

  1. SendTaskRequest missing privacy fields (Rust side)

CreateTaskRequest (sync path) has encrypted_data_refs, user_signature, app_signature, but SendTaskRequest (async path, line 56 of
types/mod.rs) does NOT have these fields. Privacy-enabled tasks won't work through the async newt_sendTask endpoint.

Severity: Low for Phase 1 (privacy tasks use the sync path), but should be addressed when expanding privacy support to the async
path.


Summary

Phase 1 SDK-Protocol alignment is solid. The three critical interop points all match:

  1. HPKE encryption (suite, AAD, info, envelope format) — exact match
  2. Dual-signature scheme (message construction, digest, Ed25519 compat) — exact match
  3. RPC request types (UploadEncryptedDataRpcRequest, privacy fields on task creation) — exact match

The four issues found are all low-to-medium severity future work items, not blocking for Phase 1. The most actionable one is the
stale doc in examples.md (issue #1), which can be fixed in PR #117 before merge. The recipient_pubkey rename (issue #2) and
missing public keys for signature enforcement (issue #3) should be tracked as follow-up work.

★ Insight ─────────────────────────────────────
The SDK is slightly ahead of the gateway in one respect: it implements and returns the dual-signature public keys
(signPrivacyAuthorization returns userPublicKey and appPublicKey), but neither the gateway's CreateTaskRequest nor the SDK's
SubmitEvaluationRequestParams sends them. When the gateway wires validate_privacy_signatures into the task creation handler, both
sides need to add the pubkey fields — but the SDK already has the data ready.
─────────────────────────────────────────────────

@denniswon
Copy link
Author

Pushed 727f739. All deferred items are now done:

┌──────────────────────────────────┬───────────────────────────────────────────────────────────────────┬──────────────────────────────┐
│ Item │ Fix │ File │
├──────────────────────────────────┼───────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ Privacy methods undocumented │ Added full section with 6 methods, signatures, params, examples │ sdk-reference.mdx │
├──────────────────────────────────┼───────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ Privacy types missing │ Added 6 expandable type definitions │ sdk-reference.mdx │
├──────────────────────────────────┼───────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ precomputePolicyId missing │ Added optional blockTimestamp?: bigint, made expireAfter optional │ sdk-reference.mdx │
│ params │ │ │
├──────────────────────────────────┼───────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ any types in signatures │ Changed to unknown in evaluateIntentDirect and │ sdk-reference.mdx │
│ │ AggregationResponse │ │
├──────────────────────────────────┼───────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ console.log leak │ Removed all 3 console statements, added no-op handlers │ task-events.ts │
├──────────────────────────────────┼───────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ wasmArgs wrong level │ Removed invalid top-level wasmArgs from example │ frontend-sdk-integration.mdx │
├──────────────────────────────────┼───────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ Stale gateway URL in JSDoc │ Updated to gateway.testnet.newton.xyz │ task-events.ts │
└──────────────────────────────────┴───────────────────────────────────────────────────────────────────┴──────────────────────────────┘

Zero deferred items remaining.

@denniswon
Copy link
Author


What's Next for Phase 2

The next item to tackle is the Phase 2A gateway/operator integration gaps — the 5 remaining pieces that wire the
threshold decryption crypto (already code-complete in PRs #426-429) into the actual runtime.

However, these integration items depend on PRs #426-429 being merged to main first. So there's a fork:

Option A: Wait for PR merge, then integrate (recommended)

The PRs need reviewer attention. Once #426 merges, the integration work can start on a new branch off main. The 5 items
are:

┌─────┬────────────────────────────┬───────────────────────────────────────────────────────────┬─────────────────────┐
│ # │ Item │ Why It's Next │ Effort │
├─────┼────────────────────────────┼───────────────────────────────────────────────────────────┼─────────────────────┤
│ │ enc_point in │ Without this, the gateway can't send the HPKE │ Small — add one │
│ 1 │ ConsensusPrepareRequest │ encapsulated key to operators during Prepare phase — │ field to the RPC │
│ │ │ operators have nothing to compute partial decryptions on │ request type │
├─────┼────────────────────────────┼───────────────────────────────────────────────────────────┼─────────────────────┤
│ │ Threshold context on │ Gateway needs ThresholdDecryptionContext (MPK + operator │ Medium — config │
│ 2 │ ChainService │ public shares) loaded per-chain at startup so the │ loading + per-chain │
│ │ │ threshold decrypt path in sync.rs can actually run │ init │
├─────┼────────────────────────────┼───────────────────────────────────────────────────────────┼─────────────────────┤
│ │ Operator keyshare loading │ Operator must load ThresholdOperatorConfig from TOML and │ Small — config │
│ 3 │ at startup │ hold KeyShare in memory for partial decryption │ wiring in │
│ │ │ │ builder.rs │
├─────┼────────────────────────────┼───────────────────────────────────────────────────────────┼─────────────────────┤
│ │ newt_getPrivacyPublicKey │ RPC must return threshold MPK instead of gateway's │ Small — conditional │
│ 4 │ switch │ individual key when threshold is configured, so clients │ return │
│ │ │ encrypt to the right key │ │
├─────┼────────────────────────────┼───────────────────────────────────────────────────────────┼─────────────────────┤
│ │ Single-phase consensus │ Privacy tasks using single-phase mode have no Prepare │ Design decision — │
│ 5 │ decision │ phase. Need to either add a decrypt round or reject │ small code either │
│ │ │ privacy+single-phase │ way │
└─────┴────────────────────────────┴───────────────────────────────────────────────────────────┴─────────────────────┘

These are all in the newton-prover-avs repo and collectively complete NEWT-178 (the parent issue "Threshold Decryption
for Two-Phase Consensus").

Option B: Start Phase 2B (Epoch/PSS) in parallel

If you want to keep moving while PRs are in review, Phase 2B work is independent at the Solidity contract level —
EpochRegistry.sol (NEWT-639) can be designed and written without waiting for the Rust PRs to merge. This is net-new
contract code with no dependencies on the threshold Rust crate.

@denniswon
Copy link
Author

EXPANSION_COMPLETE

The analysis reveals something important: the 5 integration items I originally identified are already implemented on the
feature branches. The actual gaps are more subtle but critical:

┌──────────────────────────────────────────────┬─────────────────────────────────────────────────────────┐
│ Original Assumption │ Reality │
├──────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ enc_point in ConsensusPrepareRequest missing │ Already wired (rpc_server.rs:178, common.rs:366-396) │
├──────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Threshold context on ChainService missing │ Already loaded (handler/mod.rs:638-663, cloned at 1419) │
├──────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Operator keyshare loading missing │ Already done (builder.rs:247-270, core.rs:75) │
├──────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Full threshold decrypt pipeline missing │ Already wired (sync.rs:845-960, 1375-1545) │
├──────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ E2E test missing │ Already exists (threshold.rs, threshold_e2e.rs) │
└──────────────────────────────────────────────┴─────────────────────────────────────────────────────────┘

The real gaps are:

  1. newt_getPrivacyPublicKey always returns centralized key — clients encrypt to wrong key when threshold is configured
  2. No validation rejecting privacy+single-phase+threshold — silent decryption failure
  3. Multi-envelope threshold only handles first envelope — rest silently ignored
  4. Threshold→centralized fallback is useless — different keys, both fail (should remove misleading fallback)
  5. No Prometheus metric for threshold decrypt path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment