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

Given your focus on security, privacy preservation, and performance/reliability, here's the prioritized execution order for these
lessons, filtered through Newton's compliance-first philosophy.

★ Insight ─────────────────────────────────────
The prioritization principle: anything that prevents plaintext leakage or improves system reliability gets done first. Product
expansion (stablecoins, precompiles) is deferred until the core privacy guarantee is airtight and performant.
─────────────────────────────────────────────────


Priority Execution Order

Tier 1: Security & Privacy Foundation (Do First — Phase 2 prerequisites)

P1. Client-side encryption SDK (Lesson 8) — NEWT-182

  • Why first: If encryption leaks plaintext to any server before it reaches the gateway, everything downstream is compromised. This is
    the entry point of the entire privacy guarantee.
  • Requirement: HPKE encryption must happen entirely in-browser. Zero server round-trips. No key material ever leaves the client during
    encryption. SDK should work offline for the encryption step.
  • Effort: Low — this is a design constraint on existing NEWT-182 work, not a new feature.

P2. Epoch-based key rotation with MSK destruction (Lesson 5) — NEWT-174/NEWT-189

  • Why second: This is a pre-condition for secure Phase 2 DKG. Without it, a compromised key share from epoch 1 stays valid forever.
    Forward secrecy limits the blast radius of any compromise.
  • Design:
    • Generate new threshold keypair each epoch (e.g., every N blocks or every 24h)
    • Split MSK via Pedersen DKG — each operator gets a share
    • Destroy the full MSK immediately after share distribution (Fairblock pattern)
    • Old shares become useless for new envelopes; existing envelopes re-encrypt or use key escrow
  • Performance consideration: Key rotation frequency must balance security (more frequent = smaller compromise window) vs. overhead
    (DKG ceremony takes time). Start with longer epochs (24h), tune down.

P3. Validator slashing for privacy failures (Lesson 6)

  • Why third: Without economic penalties, operators have no disincentive to withhold partial decryptions, submit garbage, or leak
    plaintext. This is the enforcement mechanism for the privacy guarantee.
  • Design for EigenLayer:
    • Slash for: failure to submit partial decryption within timeout, submitting invalid partial decryption, provable plaintext leakage
    • Integrate with EigenLayer's existing slashing framework (ISlasher)
    • Define severity tiers: missed partial → small slash, invalid partial → medium slash, plaintext leak → full slash + ejection
  • Reliability impact: Slashing incentivizes operator uptime for decryption — directly improves reliability.

Tier 2: Performance & Reliability (Do Next — Phase 2 core)

P4. On-chain key aggregation verifiability (Lesson 4)

  • Why here: After the security foundation is set, the next question is: can we prove the threshold decryption was done correctly?
    Off-chain aggregation in the Prepare Phase is fast but opaque. On-chain verification adds auditability without necessarily moving the
    computation on-chain.
  • Pragmatic approach: Keep aggregation off-chain for performance, but publish a commitment (hash of partial decryptions + aggregated
    result) on-chain. Challengers can verify within a dispute window — mirrors Newton's existing challenger pattern.
  • Performance: No latency impact on the happy path. Verification only happens if challenged.

P5. Batch decryption evaluation (Lesson 3)

  • Why here: Once Phase 2 DKG is live with per-envelope HPKE decryption, measure the actual performance. If batch policy evaluations
    (multiple envelopes in one task) become common, O(N×t) will become a bottleneck.
  • Action: Benchmark Phase 2 threshold HPKE decryption under load (10, 100, 1000 envelopes). If latency exceeds targets, evaluate
    IBE-style batch decryption as a Phase 3 optimization.
  • Decision point: Don't implement IBE preemptively. HPKE is a proven, audited standard. Only switch if measured performance demands
    it.

Tier 3: Strategic Expansion (Do Later — Phase 3+)

P6. Conditional decryption as complementary mode (Lesson 2)

  • Why deferred: Newton is compliance-first, consent-based. Conditional decryption is a product expansion, not a security requirement.
    It opens new markets (auctions, MEV protection) but doesn't strengthen the core privacy guarantee.
  • If pursued: Implement as an optional mode alongside consent-based decryption. Condition types: block height, oracle price feed,
    governance vote deadline. Keep it simple — don't try to match Fairblock's full condition expressiveness.

P7. Precompile-based EVM integration (Lesson 9)

  • Why deferred: This is a Phase 4 distribution optimization. It makes integration easier for L2s but doesn't affect security or
    privacy. SDK integration works fine for initial deployments.
  • When relevant: Once Newton privacy is deployed to 3+ L2s and integration friction becomes the bottleneck to adoption.

P8. Confidential stablecoins / private amounts (Lesson 7)

  • Why last: This is a new product, not an improvement to existing privacy. It requires different cryptographic primitives (Twisted
    ElGamal, range proofs) and a separate security audit. Pursue only after the core privacy layer is battle-tested.

Execution Summary

PHASE 2 PREREQUISITES (security + privacy foundation)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
P1 Client-side-only encryption (NEWT-182) ← entry point security
P2 Epoch key rotation + MSK destruction ← forward secrecy
P3 Privacy-specific operator slashing ← enforcement mechanism

PHASE 2 CORE (performance + reliability)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
P4 On-chain aggregation commitments ← auditability
P5 Batch decryption benchmarking ← measure, then optimize

PHASE 3+ (strategic expansion)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
P6 Conditional decryption mode ← new markets
P7 EVM precompiles ← distribution
P8 Confidential amounts ← new product

★ Insight ─────────────────────────────────────
The "skip centralized stepping stone" lesson (Lesson 1) is intentionally NOT in this list. Per your philosophy, Phase 1.5
gateway-mediated decryption is the correct stepping stone — it validates the full pipeline securely before distributing trust. The
priority is to make Phase 2 DKG as robust as possible when it ships, not to skip Phase 1.5. P1-P3 above are the foundation that makes
Phase 2 DKG trustworthy when it arrives.

Performance note on threshold decryption: The biggest reliability risk in Phase 2 is operator liveness during the Prepare Phase. If 1
of t operators is down, threshold decryption fails. Mitigation: set t < n with margin (e.g., 3-of-5 not 4-of-5), implement timeout +
fallback, and use P3 slashing to incentivize uptime.
─────────────────────────────────────────────────

@denniswon
Copy link
Author

⏺ Both saved:

  • Local memory: memory/privacy-layer.md updated with full execution state, next TODOs, blocked items, and known limitations
  • Supermemory: Saved as project knowledge (ID: G9S3uYwXgTYtjj9pwLL8ok) — searchable across sessions and team members

Next time you pick up Privacy Layer work, run /super-search newton privacy layer execution state to reload context, or the local
memory will auto-load from privacy-layer.md.

@denniswon
Copy link
Author


Newton Privacy Layer — Full Project Status

Phase 1: Foundation (HPKE + Ed25519) — 95% complete

┌──────────┬───────┬──────────────────────────────────────────────────────────────────────────────┐
│ Status │ Count │ Issues │
├──────────┼───────┼──────────────────────────────────────────────────────────────────────────────┤
│ Done │ 18 │ NEWT-180, 181, 184, 185, 186, 187, 188, 431, 432, 433, 434, 435, 436, 437, │
│ │ │ 438, 470-476 │
├──────────┼───────┼──────────────────────────────────────────────────────────────────────────────┤
│ In │ 3 │ NEWT-627 (PR #115), NEWT-182 (PR #116), NEWT-179 (PR #117) │
│ Review │ │ │
├──────────┼───────┼──────────────────────────────────────────────────────────────────────────────┤
│ Canceled │ 1 │ NEWT-183 (Ed25519 Solidity verification — not needed) │
└──────────┴───────┴──────────────────────────────────────────────────────────────────────────────┘

The 3 "In Review" issues are all SDK-side, stacked PRs. Once they merge, Phase 1 is 100%.

Phase 1.5: Gateway-Mediated Decryption — 100% complete

All 7 issues Done (NEWT-470 through NEWT-476).

Phase 2: Threshold Operations (DKG) — 0%, Backlog

┌──────────┬─────────────────────────────────────────────────┬──────────┐
│ Issue │ Title │ Priority │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-177 │ Implement DKG protocol │ — │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-176 │ Implement threshold decryption (t-of-n) │ — │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-174 │ Implement key rotation and refresh protocol │ — │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-178 │ Threshold Decryption for Two-Phase Consensus │ — │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-157 │ Implement Threshold Decryption Protocol │ — │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-158 │ Implement DKG Protocol │ — │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-628 │ [P2] Epoch-based key rotation + MSK destruction │ High │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-629 │ [P3] Privacy-specific operator slashing │ — │
├──────────┼─────────────────────────────────────────────────┼──────────┤
│ NEWT-630 │ [P4] On-chain aggregation commitments │ — │
└──────────┴─────────────────────────────────────────────────┴──────────┘

Canceled: NEWT-189 (duplicate of NEWT-174), NEWT-190 (operator registry — already exists via
EigenLayer), NEWT-175 (same)

Phase 3: Advanced Privacy (MPC/ZK) — 0%, Backlog

┌──────────┬────────────────────────────────────────────────────┐
│ Issue │ Title │
├──────────┼────────────────────────────────────────────────────┤
│ NEWT-167 │ MPC and ZK Proof Integration Milestone │
├──────────┼────────────────────────────────────────────────────┤
│ NEWT-171 │ Threshold Attestation Engine for BLS Aggregation │
├──────────┼────────────────────────────────────────────────────┤
│ NEWT-172 │ ZK circuit library for selective disclosure │
├──────────┼────────────────────────────────────────────────────┤
│ NEWT-173 │ Privacy-Preserving Policy Evaluation with MPC/ZK │
├──────────┼────────────────────────────────────────────────────┤
│ NEWT-631 │ [P5] Benchmark threshold HPKE + evaluate IBE batch │
├──────────┼────────────────────────────────────────────────────┤
│ NEWT-632 │ [P6] Conditional decryption (complementary mode) │
└──────────┴────────────────────────────────────────────────────┘

Canceled: NEWT-168 (MPC framework), NEWT-169 (ZK circuits for selective disclosure via SP1)

Phase 4: Ecosystem Expansion — 0%, Backlog

┌──────────┬─────────────────────────────────────────────────────────┐
│ Issue │ Title │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-159 │ DeFi protocol integration examples │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-160 │ Extend newton-cli with Privacy Commands │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-161 │ Deploy Privacy Features to L2s │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-162 │ Multi-Chain Privacy with Newton Multichain Architecture │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-163 │ Operator Onboarding with Privacy Key Generation │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-164 │ Production monitoring and alerting │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-165 │ Deploy Privacy Features to Mainnet │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-166 │ External security audit │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-170 │ Ecosystem Expansion: Production and Integrations │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-633 │ [P7] EVM precompile integration for L2s │
├──────────┼─────────────────────────────────────────────────────────┤
│ NEWT-634 │ [P8] Confidential intent evaluation │
└──────────┴─────────────────────────────────────────────────────────┘


Recommended Next Steps (SDK side)

★ Insight ─────────────────────────────────────
The Newton Privacy Layer has a clear phase gate: Phase 1 SDK PRs merging unlocks Phase 2, which is
entirely protocol-side (DKG). The SDK has no actionable Phase 2 work until the protocol implements
Pedersen DKG and epoch-based key rotation. This means the SDK team's critical path is purely about
getting the 3 stacked PRs through review.
─────────────────────────────────────────────────

Immediate (this week):

  1. Get PRs #115 → #116 → #117 reviewed and merged. This is the only blocker to closing Phase 1. All
    code is written, tested, rebased onto latest main.

After Phase 1 closes:
2. Phase 2 has no SDK work yet — it's all protocol-side (Pedersen DKG in Rust, NEWT-177). When that
ships, the SDK will need epoch-aware public key caching and auto-refresh.
3. Potential cleanup work (not tied to privacy): the 38 noExplicitAny biome warnings in the SDK could
be addressed incrementally.

Observation — Phase 2 issue duplication:
NEWT-157/NEWT-158 overlap significantly with NEWT-176/NEWT-177. Consider consolidating:

  • NEWT-157 ("Implement Threshold Decryption Protocol") ↔ NEWT-176 ("Implement threshold decryption
    t-of-n")
  • NEWT-158 ("Implement DKG Protocol") ↔ NEWT-177 ("Implement DKG protocol")

These pairs appear to be the same work described at different times. Worth deduplicating before Phase
2 kicks off.

@denniswon
Copy link
Author

Newton Privacy Layer — Current State & Next Steps

Linear Issue Status

All Phase 1 + 1.5 issues are Done (14 issues). Two items remain In Review:

┌──────────┬───────────────────────────────────────────┬───────────┬─────────────────────────┐
│ Ticket │ Title │ Status │ Owner │
├──────────┼───────────────────────────────────────────┼───────────┼─────────────────────────┤
│ NEWT-179 │ Write developer integration documentation │ In Review │ AVS-side (this repo) │
├──────────┼───────────────────────────────────────────┼───────────┼─────────────────────────┤
│ NEWT-182 │ Extend Newton SDK with Privacy APIs │ In Review │ SDK-side (other device) │
└──────────┴───────────────────────────────────────────┴───────────┴─────────────────────────┘

Open PRs (Stacked Chain)

┌──────┬────────────────────────────────┬───────────────────────────────────────────────────────────────────────────┬────────────────┐
│ PR │ Branch │ Scope │ Status │
├──────┼────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────┤
│ #421 │ privacy-data-flow-fix │ Fix: privacy data via ConsensusCommitRequest.privacy_data, not │ Awaiting │
│ │ │ policyTaskData │ review │
├──────┼────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────┤
│ #422 │ multichain-routing-e2e-options │ Feat: privacy=true + two-phase + attestation options for │ Awaiting │
│ │ │ multichain-routing-e2e │ review │
├──────┼────────────────────────────────┼───────────────────────────────────────────────────────────────────────────┼────────────────┤
│ #423 │ unified-e2e-runner │ Feat: unified make e2e scenario=X runner + CI privacy/attestation steps │ Awaiting │
│ │ │ │ review │
└──────┴────────────────────────────────┴───────────────────────────────────────────────────────────────────────────┴────────────────┘

These are fixes and test infrastructure improvements to already-Done Phase 1 work. They don't unblock new tickets — they harden what's
already shipped.

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

  1. Phase 1 AVS-side is effectively complete. The only remaining item (NEWT-179, developer docs) is low priority and already In Review.
    All crypto, gateway decryption, metrics, auth, DB schema, and E2E tests are merged to main.
  2. The stacked PRs (#421-423) close the "test gap" — before these PRs, CI had zero coverage for privacy or attestation validation. After
    merge, CI will run multichain-routing-e2e privacy=true and validate_attestation=direct on every PR.
  3. Phase 2 (Threshold DKG) is the next major engineering effort — 7 Backlog issues, all High priority. This is where Newton exits the
    centralized trust model for privacy.
    ─────────────────────────────────────────────────

Next Steps — Priority Order (AVS-side only)

Immediate (this sprint):

  1. Get PRs #421-423 reviewed and merged — these are blocking CI coverage improvements
  2. Runtime-verify make e2e scenario=basic on dual anvil before merge of PR #423
  3. NEWT-179 (developer docs) — low priority but the only remaining Phase 1 AVS deliverable. Can write after PRs merge since the data flow
    changed in #421

Medium-term (next sprint):
4. NEWT-627 (Enforce client-side-only HPKE encryption in SDK) — Urgent priority, but this is SDK-side work (Track B on your other
device). Blocks the SDK from accidentally doing server-side encryption.

Future (Phase 2 — not yet started):
5. NEWT-177 (DKG protocol) + NEWT-176 (threshold decryption) — the two foundational Phase 2 items. These are the highest-impact next
steps once Phase 1 closes out, but require significant design work (Pedersen DKG, t-of-n threshold scheme selection).

What's NOT Blocking

  • All Phase 1.5 (gateway-mediated decryption) issues are Done
  • Privacy metrics (NEWT-436) are Done and shipping
  • Phase 3/4 items are all Backlog and not actionable until Phase 2 DKG is complete
  • The 4 Canceled issues (NEWT-168, 175, 183, 190) were correctly deduplicated or descoped

Summary

Phase 1 + 1.5 is 97% complete on AVS-side (20/22 Done, 1 In Review, 1 In Review on SDK). The PRs in flight (#421-423) are quality/CI
improvements, not new features. Once those merge and NEWT-179 (developer docs) ships, Phase 1 is fully closed on the AVS side. The
SDK-side work (NEWT-182, NEWT-627) is being handled in parallel on your other machine. Phase 2 (Threshold DKG) is the next engineering
frontier when you're ready to pick it up.

@denniswon
Copy link
Author

noExplicitAny cleanup (PR #120)

  • https://github.com/newt-foundation/newton-sdk/pull/120
  • 10 files edited across types, utils, modules, and service layers
  • Replaced any with proper types: unknown, WalletClient, PublicClient, PolicyParamsJson, JsonRpcRequestPayload, Record<string,
    unknown>
  • Added biome-ignore comments for legitimate any usage: Promise API conformance (5), EventEmitter dynamic dispatch (6), type guards
    (1)
  • Zero noExplicitAny warnings remain, TypeScript compiles clean, all 29 tests pass

NEWT-627 status: Already implemented in PR #115, status "In Review". No additional work needed — the client-side HPKE encryption
module is fully built and part of the stacked PR chain (#115→#116→#117) awaiting merge.

Phase 2 duplicate consolidation on Linear:

  • NEWT-157 → marked as duplicate of NEWT-176 (threshold decryption), status set to Canceled
  • NEWT-158 → marked as duplicate of NEWT-177 (DKG protocol), status set to Canceled
  • Phase 2 goes from 8 issues to 6 active issues
  • NEWT-176 and NEWT-177 are the canonical issues (they have Phase 2 milestone assignments and cross-references to
    NEWT-628/NEWT-629)

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

  • The duplicates arose because NEWT-157/158 were created on Jan 18 as high-level "Phase 2" items with the Feature label, while
    NEWT-176/177 were created the same day with detailed Rust implementation specs and milestone assignments — likely from two
    different planning passes
  • Keeping the more detailed issues (NEWT-176/177) preserves the existing relatedTo connections to NEWT-628 (epoch key rotation) and
    NEWT-629 (privacy slashing)
    ─────────────────────────────────────────────────

@denniswon
Copy link
Author


Newton Privacy Layer Phase 2: Threshold DKG — Research Synthesis

Executive Summary

Phase 2 replaces the gateway as sole decryptor with t-of-n threshold decryption across EigenLayer operators. The recommended architecture:

┌───────────────────────┬─────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────┐
│ Decision │ Recommendation │ Rationale │
├───────────────────────┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ DKG Protocol │ FROST DKG (RFC 9591) │ 2-round, production Rust crate (frost-ristretto255), no trusted dealer │
├───────────────────────┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Curve │ Ristretto255 (not raw X25519) │ X25519 clamping breaks Shamir; Ristretto255 is cofactor-1 Curve25519 │
├───────────────────────┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Threshold decryption │ Lagrange interpolation in the exponent │ Secret never reconstructed; KEM-layer only │
├───────────────────────┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Consensus integration │ Option B: merge into Prepare phase │ Zero additional round trips │
├───────────────────────┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Key rotation │ Proactive Secret Sharing (PSS) │ MPK unchanged across epochs; zero client UX impact │
├───────────────────────┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Slashing │ DLEQ fraud proofs (wrong-keyshare) + liveness attestation (no-keyshare) │ Cryptographically verifiable; ~40k gas on-chain │
├───────────────────────┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ On-chain commitments │ Merkle root of partial decryptions + BLS co-signature │ Per-task 32-byte commitment; individual shares on EigenDA │
└───────────────────────┴─────────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────┘

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

  1. X25519 clamping is a showstopper — RFC 7748 mandates scalar clamping which is non-linear, breaking Shamir recombination. Ristretto255 (same underlying curve, prime-order
    group, no clamping) is the correct DKG group. Conversion via EdwardsPoint::to_montgomery() preserves HPKE compatibility.
  2. The gateway remains an untrusted combiner — it verifies DLEQ proofs and combines partial DH outputs but never holds shares. Trust level improves from "gateway sees all
    plaintext always" to "gateway sees plaintext only during task processing, verified by operator co-attestation."
  3. Threshold applies only at the KEM layer — HKDF and ChaCha20-Poly1305 run unchanged after threshold combining reconstructs the DH shared secret.
    ─────────────────────────────────────────────────

Architecture: Current → Phase 2

flowchart LR
subgraph PHASE1["Phase 1.5 (Current)"]
C1[Client] -->|HPKE encrypt to gateway key| GW1[Gateway]
GW1 -->|decrypt centrally| P1[Plaintext]
P1 -->|pass via ConsensusCommitRequest| O1[Operators]
end

  subgraph PHASE2["Phase 2 (Target)"]
      C2[Client] -->|HPKE encrypt to threshold MPK| GW2[Gateway]
      GW2 -->|distribute enc to operators| O2[Operators]
      O2 -->|partial DH + DLEQ proof| GW2
      GW2 -->|Lagrange combine >= t shares| P2[Plaintext]
      P2 -->|pass via ConsensusCommitRequest| O2
  end

DKG Protocol Selection: FROST on Ristretto255

Why FROST

┌─────────────────────┬─────────────────────────────────┬───────────────────────────┬──────────────────────────────────┬───────────┐
│ Factor │ FROST │ Pedersen │ Joint-Feldman │ GJKR07 │
├─────────────────────┼─────────────────────────────────┼───────────────────────────┼──────────────────────────────────┼───────────┤
│ Rounds │ 2 │ 3 │ 3 │ 4 │
├─────────────────────┼─────────────────────────────────┼───────────────────────────┼──────────────────────────────────┼───────────┤
│ Rust crate │ frost-ristretto255 (production) │ vsss-rs (primitives only) │ None │ None │
├─────────────────────┼─────────────────────────────────┼───────────────────────────┼──────────────────────────────────┼───────────┤
│ Rushing adversary │ Proof-of-knowledge in R1 │ Not addressed │ Vulnerable (negligible for HPKE) │ Fixed │
├─────────────────────┼─────────────────────────────────┼───────────────────────────┼──────────────────────────────────┼───────────┤
│ Ristretto255 native │ Yes │ Any group │ Any group │ Any group │
└─────────────────────┴─────────────────────────────────┴───────────────────────────┴──────────────────────────────────┴───────────┘

DKG Ceremony Flow

sequenceDiagram
participant GW as Gateway (Coordinator)
participant O1 as Operator 1
participant O2 as Operator 2
participant On as Operator n
participant CH as On-Chain

  Note over GW: Triggered by OperatorAdded/Removed event

  rect rgb(230, 245, 255)
  Note over O1,On: Round 1 — Commitments + Proof-of-Knowledge
  O1->>GW: (C₁, PoK₁)
  O2->>GW: (C₂, PoK₂)
  On->>GW: (Cₙ, PoKₙ)
  GW->>GW: Verify all PoKs, broadcast commitments
  end

  rect rgb(255, 245, 230)
  Note over O1,On: Round 2 — Encrypted Share Distribution
  O1->>O2: E(s₁₂) encrypted to O2's X25519 key
  O1->>On: E(s₁ₙ) encrypted to On's X25519 key
  O2->>O1: E(s₂₁)
  On->>O1: E(sₙ₁)
  Note over O1,On: Each operator verifies shares against commitments
  end

  O1->>CH: BLS-signed MPK commitment
  O2->>CH: BLS-signed MPK commitment
  Note over CH: Store threshold MPK in EpochRegistry

Curve Compatibility

DKG output: (sᵢ, MPK) on Ristretto255
HPKE encrypt: pk_hpke = MPK.to_montgomery() → standard X25519 point
Partial decrypt: Dᵢ = sᵢ * enc_ristretto → Ristretto255 scalar mult
Combine: D = Σ λᵢDᵢ (Lagrange) → shared_secret = D.to_montgomery()
KDF + AEAD: unchanged from Phase 1.5

Rust Crate Stack

┌─────────────────────────┬───────────────────────────────┬─────────────────────────────────────────────┐
│ Component │ Crate │ Notes │
├─────────────────────────┼───────────────────────────────┼─────────────────────────────────────────────┤
│ Ristretto255 arithmetic │ curve25519-dalek v4 │ Already transitive dep │
├─────────────────────────┼───────────────────────────────┼─────────────────────────────────────────────┤
│ Feldman VSS shares │ vsss-rs v4 │ New dep, Ristretto255 support │
├─────────────────────────┼───────────────────────────────┼─────────────────────────────────────────────┤
│ Full DKG ceremony │ frost-ristretto255 v2 │ New dep (ZCash Foundation) │
├─────────────────────────┼───────────────────────────────┼─────────────────────────────────────────────┤
│ DLEQ proofs │ ~50 lines on curve25519-dalek │ Custom, straightforward │
├─────────────────────────┼───────────────────────────────┼─────────────────────────────────────────────┤
│ HPKE after combining │ Existing hpke crate │ Needs open_with_precomputed_dh augmentation │
└─────────────────────────┴───────────────────────────────┴─────────────────────────────────────────────┘


Consensus Integration: Merged Prepare Phase (Option B)

sequenceDiagram
participant C as Client
participant GW as Gateway
participant O as Operators (1..n)
participant CH as On-Chain

  Note over C,GW: UPLOAD (before task)
  C->>GW: SecureEnvelope encrypted to MPK
  GW->>GW: Store in encrypted_data_refs

  rect rgb(230, 255, 230)
  Note over GW,O: PREPARE PHASE (extended)
  GW->>O: newt_fetchPolicyData + enc handles
  O->>O: Fetch policyTaskData (existing)
  O->>O: Compute Dᵢ = sᵢ * enc, DLEQ πᵢ (NEW)
  O->>GW: {policyTaskData, Dᵢ, πᵢ}
  end

  rect rgb(255, 230, 230)
  Note over GW: DECRYPTION + CONSENSUS
  GW->>GW: Verify t DLEQ proofs
  GW->>GW: Lagrange interpolate → plaintext
  GW->>GW: Median consensus on policyTaskData
  end

  rect rgb(230, 230, 255)
  Note over GW,O: COMMIT PHASE (unchanged)
  GW->>O: ConsensusCommitRequest {consensus data + privacy_data}
  O->>O: Evaluate policy + BLS sign
  O->>GW: ConsensusCommitResponse
  end

  GW->>CH: respondToTask (BLS aggregated)

Latency impact: Operator computes Dᵢ (~0.3ms) alongside policyTaskData fetch — no additional round trip. Total crypto overhead: ~2.1ms for t=7, n=10.


Epoch-Based Key Rotation

Recommended Model: Proactive Secret Sharing (PSS)

┌──────────────────┬──────────────────────────┬───────────────────────┬─────────────────────┐
│ Property │ Full Rotation (Model A) │ PSS (Model B) │ Hybrid (Model C) │
├──────────────────┼──────────────────────────┼───────────────────────┼─────────────────────┤
│ Client UX change │ Yes (new MPK each epoch) │ No (MPK constant) │ Per long epoch │
├──────────────────┼──────────────────────────┼───────────────────────┼─────────────────────┤
│ Forward secrecy │ Full │ Mobile adversary only │ Full per long epoch │
├──────────────────┼──────────────────────────┼───────────────────────┼─────────────────────┤
│ DKG overhead │ Full DKG each epoch │ O(n²) refresh msgs │ Mixed │
├──────────────────┼──────────────────────────┼───────────────────────┼─────────────────────┤
│ Recommended for │ Phase 3 │ Phase 2 │ Phase 4 │
└──────────────────┴──────────────────────────┴───────────────────────┴─────────────────────┘

Epoch Lifecycle

EPOCH N EPOCH N+1
┌──────────────────────────────────────────────────┐
│ Share Refresh → Active → Grace Period → Destroy │ → Share Refresh
└──────────────────────────────────────────────────┘
↑ ↑
O(n²) proactive Destruction
share refresh attestation
(MPK unchanged) submitted on-chain

Defaults: Epoch = 7200 blocks (~24h), Overlap = 600 blocks (~2h), Grace = 300 blocks (~1h)

On-Chain: EpochRegistry Contract

contract EpochRegistry {
struct EpochCommitment {
uint256 epoch;
uint32 startBlock;
uint32 endBlock;
bytes32 mpk; // Ristretto255 threshold public key
bytes32 operatorSetFingerprint; // keccak256(sorted operators + weights)
uint16 threshold; // t
uint16 committeeSize; // n
bytes32[] operatorKeyshareCommitments; // g^{sᵢ} per operator
}

  function triggerEpochRotation(EpochCommitment calldata) external;
  function attestKeyshareDestroyed(uint256 epoch, bytes calldata sig) external;
  event EpochRotated(uint256 indexed oldEpoch, uint256 indexed newEpoch);

}


Privacy-Specific Slashing

Violation Categories

┌──────────────────────┬─────────────────────────────────┬───────────────────────────────┬─────────┬───────────────────┐
│ Violation │ Detection │ Proof Type │ Slash % │ Phase │
├──────────────────────┼─────────────────────────────────┼───────────────────────────────┼─────────┼───────────────────┤
│ Wrong-keyshare │ DLEQ verification failure │ Cryptographic (deterministic) │ 10% │ Phase 2 │
├──────────────────────┼─────────────────────────────────┼───────────────────────────────┼─────────┼───────────────────┤
│ No-keyshare │ Operator absent from t shares │ Quorum attestation │ 1-2% │ Phase 2 │
├──────────────────────┼─────────────────────────────────┼───────────────────────────────┼─────────┼───────────────────┤
│ Premature decryption │ Unauthorized task participation │ On-chain authorization check │ 10% │ Phase 2 (partial) │
├──────────────────────┼─────────────────────────────────┼───────────────────────────────┼─────────┼───────────────────┤
│ Keyshare leakage │ Undetectable without TEE │ N/A │ N/A │ Phase 3 (TEE) │
└──────────────────────┴─────────────────────────────────┴───────────────────────────────┴─────────┴───────────────────┘

DLEQ Proof for Wrong-Keyshare Detection

Each partial decryption Dᵢ = sᵢ * enc comes with a DLEQ proof proving log_G(PKᵢ) == log_{enc}(Dᵢ). On-chain verification costs ~40k gas (2 BN254 scalar mults via EIP-196
precompiles).

New Contract: PrivacySlasher

PrivacySlasher.slashForInvalidKeyshare(operator, taskId, Dᵢ, πᵢ) // DLEQ fails → instant slash
PrivacySlasher.slashForLivenessViolation(operator, taskId, epoch, witnessSignatures) // absent → slash

Both call into EigenLayer's InstantSlasher.fulfillSlashingRequest().


On-Chain Commitments for Auditability

Three-Layer Commitment Scheme

flowchart TB
L1["Layer 1: DecryptionAuthorization
taskId + envelopeCommitment + policyClient + committeeId"]
L2["Layer 2: Share Merkle Root
keccak256(D₁,π₁ || D₂,π₂ || ... || Dₜ,πₜ)
Individual shares → EigenDA"]
L3["Layer 3: Reconstructed Commitment
Included in BLS consensus digest
Operators co-attest via BLS signature"]

  L1 --> L2
  L2 --> L3

Gas cost: ~23k per task (1 event emit + 1 mapping write). Individual share data goes to EigenDA, not L1 storage.


Codebase Integration Summary

Files That Change

┌─────────────────────────────────────┬─────────────────────────────────────────────────────────────────┐
│ File │ Change │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/core/src/crypto/hpke.rs │ Add open_with_precomputed_dh() │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/core/src/crypto/envelope.rs │ Add decrypt_envelopes_threshold() │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/core/src/dkg/ (new) │ keyshare.rs, dleq.rs, threshold.rs │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/aggregator/src/rpc_server.rs │ Add partial_decryption + dleq_proof to ConsensusPrepareResponse │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/operator/src/config.rs │ Add DkgConfig │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/operator/src/builder.rs │ Load keyshare, register newt_partialDecrypt │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/operator/src/core.rs │ newt_partialDecrypt handler │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/gateway/src/rpc/api/sync.rs │ Replace decrypt_privacy_data() with threshold path │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/gateway/src/handler/mod.rs │ Threshold MPK instead of ECDSA-derived key │
├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ crates/gateway/src/dkg/ (new) │ coordinator.rs, aggregator.rs │
└─────────────────────────────────────┴─────────────────────────────────────────────────────────────────┘

Files That Do NOT Change

  • crates/aggregator/src/bls.rs — BLS aggregation untouched
  • crates/chain-watcher/ — operator set events already emitted correctly
  • Client API schema — newt_getPrivacyPublicKey, newt_uploadEncryptedData unchanged
  • SecureEnvelope wire format — same HPKE Base mode, different recipient key

Implementation Roadmap

Dependency Graph

flowchart TD
A["NEWT-177
FROST DKG Protocol
(frost-ristretto255)"]
B["NEWT-176
Threshold Decryption
(partial DH + DLEQ + Lagrange)"]
C["NEWT-628
Epoch Registry Contract
+ PSS Rotation"]
D["NEWT-178
Two-Phase Consensus
Integration (Option B)"]
E["NEWT-630
On-Chain Commitments
(Merkle root + BLS co-sig)"]
F["NEWT-629
Privacy Slashing
(DLEQ + liveness)"]

  A --> B
  B --> D
  A --> C
  C --> F
  D --> E
  E --> F

Phased Rollout

┌───────────────┬───────────────────────────────────────────────────────────────────────────┬──────────────────────────────┬───────────┐
│ Sub-Phase │ Scope │ Tickets │ Estimate │
├───────────────┼───────────────────────────────────────────────────────────────────────────┼──────────────────────────────┼───────────┤
│ 2A — MVP │ Trusted dealer (gateway runs Feldman VSS), threshold decrypt, DLEQ proofs │ NEWT-176, NEWT-177 (partial) │ 3-4 weeks │
├───────────────┼───────────────────────────────────────────────────────────────────────────┼──────────────────────────────┼───────────┤
│ 2B — Rotation │ PSS epoch refresh, EpochRegistry contract, destruction attestation │ NEWT-628, NEWT-174 │ 2-3 weeks │
├───────────────┼───────────────────────────────────────────────────────────────────────────┼──────────────────────────────┼───────────┤
│ 2C — Full DKG │ FROST ceremony (no trusted dealer), on-chain commitments │ NEWT-177 (full), NEWT-630 │ 2-3 weeks │
├───────────────┼───────────────────────────────────────────────────────────────────────────┼──────────────────────────────┼───────────┤
│ 2D — Slashing │ PrivacySlasher contract, DLEQ on-chain verification, liveness │ NEWT-629 │ 2 weeks │
└───────────────┴───────────────────────────────────────────────────────────────────────────┴──────────────────────────────┴───────────┘

Prerequisites

  1. hpke crate augmentation — need open_with_precomputed_dh (upstream PR or fork)
  2. frost-ristretto255 + vsss-rs — new Cargo dependencies (security audit required)
  3. Operator keyshare storage — encrypted keystore file (same pattern as BLS/ECDSA keys)

Fairblock Comparison — Final Assessment

┌────────────────────┬──────────────────────────────────┬──────────────────────────────────────────────┐
│ Dimension │ Newton Phase 2 │ Fairblock │
├────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ DKG │ FROST (2-round, off-chain) │ Joint-Feldman (on-chain keyshare submission) │
├────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ Encryption │ Threshold HPKE (Ristretto255) │ IBE (BLS12-381 pairings) │
├────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ Share exchange │ Off-chain (P2P or via gateway) │ On-chain (x/keyshare module) │
├────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ Key rotation │ PSS (constant MPK) │ Per-epoch new IBE master key │
├────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ Slashing │ EigenLayer InstantSlasher + DLEQ │ Validator rewards (no slashing) │
├────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ Decryption latency │ ~2.1ms crypto + network RTT │ ~28.5ms crypto (14x slower) │
├────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ Chain dependency │ Ethereum (EigenLayer) │ FairyRing (Cosmos) + IBC │
└────────────────────┴──────────────────────────────────┴──────────────────────────────────────────────┘

Adopted from Fairblock: epoch-based rotation model, MSK destruction pattern, privacy-specific slashing concept.
Not adopted: separate chain, on-chain keyshare aggregation, IBE, new token.


Known Limitations

  1. Gateway still reconstructs plaintext — threshold removes single-key custody but not single-point-of-reconstruction. Full MPC computation is Phase 3.
  2. X25519 → Ristretto255 KEM migration — DHKEM(Ristretto255) is in CFRG drafts only, not RFC 9180. Client libraries need updating.
  3. NATS infrastructure — the whitepaper specifies NATS for DKG ceremonies and share exchange, but NATS is not yet deployed. Phase 2A can use HTTP/JSON-RPC (existing operator
    communication); NATS needed for Phase 2C.
  4. Keyshare leakage undetectable — without TEE attestation, an operator leaking their keyshare is not provably slashable. Epoch rotation provides forward secrecy as mitigation.
  5. Centralized single-phase consensus — has no Prepare phase to piggyback partial decryptions on. Either needs a dedicated Phase 0 decrypt round or must be deprecated for
    privacy-enabled tasks.
  6. External cryptographic audit required — threshold HPKE with DLEQ proofs is custom cryptography. Independent review mandatory before mainnet deployment.

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

  1. Phase 2A (MVP with trusted dealer) can ship in 3-4 weeks — the gateway already has the infrastructure to run Feldman VSS as a one-shot ceremony. This immediately eliminates
    the "single static key" risk while the full FROST DKG is built.
  2. The client API is unchanged — newt_getPrivacyPublicKey returns the threshold MPK instead of the gateway key. SecureEnvelope format identical. SDK migration is just pointing at
    a different key.
  3. PSS (Proactive Secret Sharing) is the key insight from Fairblock — it gives epoch-based forward secrecy without changing the public key, avoiding a client migration per epoch.
    Newton should adopt this over Fairblock's "new IBE master key each epoch" approach.
    ─────────────────────────────────────────────────

@denniswon
Copy link
Author

Linear vs Research Sync Analysis

Here's what I found by comparing the 7 Linear tickets against docs/THRESHOLD_DKG.md:

Sync Issues Found

┌────────────────┬──────────────────────────────────────────────────────────────────────┬───────────────────────────────────────┐
│ Issue │ Problem │ Action Needed │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ NEWT-177 │ Describes Pedersen DKG (4 phases, ark-ec/blstrs), but research │ Update description to FROST │
│ │ recommends FROST DKG (2 rounds, frost-ristretto255) on Ristretto255 │ │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ NEWT-177 │ References G1Point/BLS12-381 types, not Ristretto255 │ Update types to Ristretto255 │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ NEWT-629 │ Labeled "[Privacy P3]" but belongs in Phase 2 sub-phase 2D │ Rename prefix to "[Privacy P2D]" │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ NEWT-630 │ Labeled "[Privacy P4]" but belongs in Phase 2 sub-phase 2C │ Rename prefix to "[Privacy P2C]" │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ All 4 original │ Due dates set to Feb 2026 (past) │ Clear or reset │
│ tickets │ │ │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ NEWT-628, 629, │ No assignee │ Assign to dennis.won │
│ 630 │ │ │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ Missing │ No ticket for HPKE crate augmentation (open_with_precomputed_dh) │ Create new ticket │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ Missing │ No ticket for DLEQ proof module │ Covered implicitly in NEWT-176 but │
│ │ │ deserves explicit sub-task │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ Missing │ No ticket for EpochRegistry contract │ Create new ticket under NEWT-628 │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ Missing │ No ticket for PrivacySlasher contract │ Create new ticket under NEWT-629 │
├────────────────┼──────────────────────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ NEWT-174 vs │ │ NEWT-174 = Rust implementation; │
│ NEWT-628 │ Significant overlap (both describe PSS, epoch rotation) │ NEWT-628 = full spec including │
│ │ │ contract │
└────────────────┴──────────────────────────────────────────────────────────────────────┴───────────────────────────────────────┘

★ Insight ─────────────────────────────────────
Why FROST over Pedersen: The original Linear tickets were written before the research deep-dive. Pedersen DKG (NEWT-177's current
spec) has 3-4 rounds and requires a separate complaint/dispute phase. FROST DKG (RFC 9591) achieves the same security in 2 rounds
with built-in proof-of-knowledge, and has a production Rust crate (frost-ristretto255) from the ZCash Foundation. The X25519 clamping
issue (which breaks Shamir) makes Ristretto255 mandatory regardless of which DKG protocol we use.
─────────────────────────────────────────────────

Detailed Execution Plan

Sub-Phase 2A: MVP Threshold Decrypt (3-4 weeks)

Goal: Trusted dealer Feldman VSS, threshold decryption with DLEQ proofs, E2E test on local anvil.

Linear tickets: NEWT-177 (partial), NEWT-176

Week 1: Crypto Foundation

┌───────────────────────────────┬────────────────────────────────────┬──────────────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├───────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Add vsss-rs and │ crates/core/Cargo.toml │ Feature-gate behind privacy-threshold │
│ curve25519-dalek v4 deps │ │ │
├───────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Implement Feldman VSS dealer │ crates/core/src/dkg/keyshare.rs │ generate_shares(secret, threshold, n) -> Vec + │
│ │ (new) │ verification vector │
├───────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Implement DLEQ proofs │ crates/core/src/dkg/dleq.rs (new) │ prove(sk_i, enc_point) -> DleqProof, verify(pk_i, │
│ │ │ enc_point, D_i, proof) -> bool │
├───────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Ristretto255 <-> Montgomery │ crates/core/src/dkg/convert.rs │ ristretto_to_montgomery() for HPKE compatibility │
│ conversion │ (new) │ │
├───────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Unit tests for all crypto │ crates/core/src/dkg/mod.rs │ Round-trip VSS, DLEQ verification, conversion │
│ primitives │ │ correctness │
└───────────────────────────────┴────────────────────────────────────┴──────────────────────────────────────────────────────────┘

Week 2: HPKE Threshold Path

┌────────────────────────────────┬────────────────────────────────────┬──────────────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Fork/augment hpke crate │ crates/core/src/crypto/hpke.rs │ Add open_with_precomputed_dh(shared_secret, enc, ct, │
│ │ │ aad) that skips KEM │
├────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Implement │ │ Accepts Vec<(OperatorId, PartialDecryption, DleqProof)>, │
│ decrypt_envelopes_threshold() │ crates/core/src/crypto/envelope.rs │ verifies proofs, Lagrange-interpolates, calls │
│ │ │ open_with_precomputed_dh │
├────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Threshold decryption │ crates/core/src/dkg/tests/ │ Encrypt with MPK.to_montgomery(), decrypt with t-of-n │
│ integration test │ │ partials │
└────────────────────────────────┴────────────────────────────────────┴──────────────────────────────────────────────────────────┘

Week 3: Operator + Gateway Integration

┌───────────────────────────────────────┬─────────────────────────────────────┬──────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├───────────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤
│ DkgConfig on operator │ crates/operator/src/config.rs │ keyshare_path, threshold, epoch_id │
├───────────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Keyshare loading at operator startup │ crates/operator/src/builder.rs │ Load from encrypted keystore file │
├───────────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤
│ newt_partialDecrypt RPC handler │ crates/operator/src/core.rs │ Compute D_i = s_i * enc_ristretto, generate DLEQ │
│ │ │ proof, return PartialDecryptionResponse │
├───────────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤
│ partial_decryption + dleq_proof on │ crates/aggregator/src/rpc_server.rs │ New optional fields │
│ ConsensusPrepareResponse │ │ │
├───────────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Gateway threshold decryption path │ crates/gateway/src/rpc/api/sync.rs │ In Prepare phase: collect partials, verify DLEQ, │
│ │ │ Lagrange-combine, decrypt │
├───────────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Threshold MPK on ChainService │ crates/gateway/src/handler/mod.rs │ Replace single HPKE key with epoch-aware MPK │
└───────────────────────────────────────┴─────────────────────────────────────┴──────────────────────────────────────────────────┘

Week 4: E2E Test + Polish

┌──────────────────────────────┬───────────────────────────────────────┬─────────────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├──────────────────────────────┼───────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Trusted dealer test fixture │ integration-tests/src/threshold.rs │ Generate VSS shares for N operators, distribute via │
│ generator │ (new) │ config files │
├──────────────────────────────┼───────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Threshold privacy E2E test │ integration-tests/tests/e2e.rs │ make e2e scenario=threshold-privacy — encrypt to MPK, 2 │
│ │ │ operators partial-decrypt, gateway combines │
├──────────────────────────────┼───────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ newt_getPrivacyPublicKey │ crates/gateway/src/rpc/api/privacy.rs │ Epoch-aware: returns current epoch's MPK │
│ returns MPK │ │ │
├──────────────────────────────┼───────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Metrics for threshold │ crates/metrics/src/lib.rs │ privacy_threshold_decrypt_duration, │
│ decryption │ │ privacy_dleq_verify_count │
└──────────────────────────────┴───────────────────────────────────────┴─────────────────────────────────────────────────────────┘

Exit criteria: Local anvil E2E passes with t=2, n=3 threshold decrypt using Feldman VSS dealer-generated shares.


Sub-Phase 2B: Epoch Rotation + PSS (2-3 weeks)

Goal: Epoch-based key rotation via PSS, EpochRegistry contract, old share destruction attestation.

Linear tickets: NEWT-628, NEWT-174

Week 5: PSS Protocol

┌───────────────────────────┬─────────────────────────────────────┬─────────────────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├───────────────────────────┼─────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ PSS refresh protocol │ crates/core/src/dkg/pss.rs (new) │ Each operator generates zero-secret polynomial, │
│ │ │ redistributes shares │
├───────────────────────────┼─────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ EpochMetadata types │ crates/core/src/dkg/epoch.rs (new) │ EpochMetadata, `EpochStatus::Active │
├───────────────────────────┼─────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ Epoch-aware keyshare │ crates/operator/src/keystore.rs │ Store multiple epoch shares, zeroize expired shares via │
│ storage │ (new) │ zeroize crate │
├───────────────────────────┼─────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
│ Destruction attestation │ crates/operator/src/core.rs │ Ed25519-sign (epoch_id, "destroyed"), submit to gateway │
└───────────────────────────┴─────────────────────────────────────┴─────────────────────────────────────────────────────────────┘

Week 6: EpochRegistry Contract

┌──────────────────────────┬─────────────────────────────────────────┬───────────────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├──────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ │ contracts/src/core/EpochRegistry.sol │ registerEpoch(epochId, mpk, operatorSetHash, threshold), │
│ EpochRegistry.sol │ (new) │ getCurrentEpoch(), recordDestruction(epochId, │
│ │ │ operatorSig) │
├──────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Forge tests for │ contracts/test/EpochRegistry.t.sol │ Epoch transitions, grace period expiry, destruction │
│ EpochRegistry │ (new) │ recording │
├──────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Rust bindings + make │ crates/core/src/generated/ │ Auto-generated │
│ generate-bindings │ │ │
├──────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ Gateway epoch │ crates/gateway/src/dkg/coordinator.rs │ Trigger rotation at epoch boundary (block-based), │
│ coordinator │ (new) │ orchestrate PSS, publish MPK │
└──────────────────────────┴─────────────────────────────────────────┴───────────────────────────────────────────────────────────┘

Week 7: Integration + E2E

┌──────────────────────────────┬───────────────────────────────────────┬────────────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├──────────────────────────────┼───────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ │ integration-tests/tests/epoch.rs │ Rotate 3 epochs, verify: old shares zeroed, new MPK │
│ Epoch rotation E2E test │ (new) │ works, grace period decryption works, expired epoch │
│ │ │ fails │
├──────────────────────────────┼───────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ newt_getPrivacyPublicKey │ crates/gateway/src/rpc/api/privacy.rs │ Return {mpk, epoch_id, expires_at_block} │
│ epoch-aware │ │ │
├──────────────────────────────┼───────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ Emergency rotation admin │ crates/cli/src/commands/ │ make admin command=rotate_epoch │
│ command │ │ │
└──────────────────────────────┴───────────────────────────────────────┴────────────────────────────────────────────────────────┘

Exit criteria: Epoch rotation works on local anvil. Old epoch shares zeroed. Grace period decryption verified. Forward secrecy
integration test passes.


Sub-Phase 2C: Full FROST DKG + On-Chain Commitments (2-3 weeks)

Goal: Replace trusted dealer with trustless FROST DKG ceremony. Add on-chain decryption commitments.

Linear tickets: NEWT-177 (full), NEWT-630

Week 8-9: FROST DKG Ceremony

┌────────────────────────────────────┬───────────────────────────────────────┬───────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├────────────────────────────────────┼───────────────────────────────────────┼───────────────────────────────────────────────────┤
│ Add frost-ristretto255 dep │ crates/core/Cargo.toml │ Feature-gate behind frost-dkg │
├────────────────────────────────────┼───────────────────────────────────────┼───────────────────────────────────────────────────┤
│ FROST Round 1 (commitments + PoK) │ crates/core/src/dkg/frost.rs (new) │ Wraps frost-ristretto255 API │
├────────────────────────────────────┼───────────────────────────────────────┼───────────────────────────────────────────────────┤
│ FROST Round 2 (share distribution) │ crates/core/src/dkg/frost.rs │ Encrypted share delivery via existing HPKE │
├────────────────────────────────────┼───────────────────────────────────────┼───────────────────────────────────────────────────┤
│ DKG ceremony coordinator service │ crates/gateway/src/dkg/coordinator.rs │ Orchestrate 2-round ceremony across operators via │
│ │ │ HTTP/JSON-RPC │
├────────────────────────────────────┼───────────────────────────────────────┼───────────────────────────────────────────────────┤
│ Operator DKG participation handler │ crates/operator/src/builder.rs │ newt_dkgRound1, newt_dkgRound2 RPC handlers │
├────────────────────────────────────┼───────────────────────────────────────┼───────────────────────────────────────────────────┤
│ DKG ceremony trigger on operator │ crates/gateway/src/handler/mod.rs │ Listen for OperatorAddedToOperatorSet events │
│ set change │ │ │
└────────────────────────────────────┴───────────────────────────────────────┴───────────────────────────────────────────────────┘

Week 9-10: On-Chain Commitments

┌────────────────────────────────────┬────────────────────────────────────────────────┬──────────────────────────────────────────┐
│ Task │ Files │ Details │
├────────────────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ DecryptionCommitment type │ crates/core/src/dkg/commitment.rs (new) │ compute(epoch_id, task_id, partials, │
│ │ │ result) -> H256 │
├────────────────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ Add decryption_commitment to │ contracts/src/core/NewtonProverTaskManager.sol │ New bytes32 field in response struct │
│ TaskResponseMetadata │ │ │
├────────────────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ Gateway emits commitment on-chain │ crates/gateway/src/rpc/api/sync.rs │ Compute commitment alongside BLS │
│ │ │ aggregation │
├────────────────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ Share Merkle root for EigenDA │ crates/core/src/dkg/commitment.rs │ Merkle tree of (operatorId, D_i, pi_i) │
│ │ │ tuples │
├────────────────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ Challenger decryption verification │ crates/challenger/src/lib.rs │ challengeDecryptionAggregation path │
└────────────────────────────────────┴────────────────────────────────────────────────┴──────────────────────────────────────────┘

Exit criteria: FROST DKG ceremony completes with 5 operators on local anvil. On-chain commitment stored and verifiable.


Sub-Phase 2D: Privacy Slashing (2 weeks)

Goal: Implement PrivacySlasher contract and off-chain detection for wrong-keyshare and no-keyshare violations.

Linear tickets: NEWT-629

Week 11: PrivacySlasher Contract

┌─────────────────────────┬───────────────────────────────────────────┬──────────────────────────────────────────────────────────┐
│ Task │ Files │ Details │
├─────────────────────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ │ contracts/src/core/PrivacySlasher.sol │ reportMissedPartialDecryption(operator, epochId, │
│ PrivacySlasher.sol │ (new) │ taskId), reportInvalidPartialDecryption(operator, │
│ │ │ partial, dleqProof) │
├─────────────────────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ DLEQ on-chain │ contracts/src/libraries/DleqVerifier.sol │ Verify DLEQ proofs using EIP-196 precompiles (~40k gas) │
│ verification │ (new) │ │
├─────────────────────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Integration with │ │ │
│ EigenLayer │ contracts/src/core/PrivacySlasher.sol │ fulfillSlashingRequest() call │
│ InstantSlasher │ │ │
├─────────────────────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Forge tests │ contracts/test/PrivacySlasher.t.sol (new) │ Invalid partial -> slash, missed deadline -> slash, │
│ │ │ duplicate report -> revert │
└─────────────────────────┴───────────────────────────────────────────┴──────────────────────────────────────────────────────────┘

Week 12: Off-Chain Detection + E2E

┌─────────────────────────────────┬────────────────────────────────────────────────┬─────────────────────────────────────────────┐
│ Task │ Files │ Details │
├─────────────────────────────────┼────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ PartialDecryptionTracker │ crates/gateway/src/dkg/tracker.rs (new) │ Track requested vs received partials, │
│ │ │ detect timeouts │
├─────────────────────────────────┼────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Gateway reports missed/invalid │ crates/chainio/src/avs/writer.rs │ report_missed_partial(), │
│ partials │ │ report_invalid_partial() │
├─────────────────────────────────┼────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Slashing E2E test │ integration-tests/tests/privacy_slashing.rs │ Simulate operator submitting invalid DLEQ │
│ │ (new) │ proof, verify slash │
├─────────────────────────────────┼────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Dispute window integration │ contracts/src/core/PrivacySlasher.sol │ 100-block window consistent with existing │
│ │ │ challenger │
└─────────────────────────────────┴────────────────────────────────────────────────┴─────────────────────────────────────────────┘

Exit criteria: Invalid DLEQ proof triggers on-chain slash on local anvil. Missed deadline detection works.


Missing Linear Tickets

Based on the analysis, we need these new tickets:

┌────────────────────────┬──────────┬───────────┬────────────────────────────────────────────────────────────────────────────────┐
│ New Ticket │ Parent │ Sub-Phase │ Description │
├────────────────────────┼──────────┼───────────┼────────────────────────────────────────────────────────────────────────────────┤
│ HPKE crate │ NEWT-176 │ 2A │ Fork/augment hpke crate with open_with_precomputed_dh for threshold path │
│ augmentation │ │ │ │
├────────────────────────┼──────────┼───────────┼────────────────────────────────────────────────────────────────────────────────┤
│ EpochRegistry contract │ NEWT-628 │ 2B │ Solidity contract for epoch lifecycle, MPK publication, destruction │
│ │ │ │ attestation │
├────────────────────────┼──────────┼───────────┼────────────────────────────────────────────────────────────────────────────────┤
│ PrivacySlasher │ NEWT-629 │ 2D │ Solidity contract for privacy-specific slashing via EigenLayer │
│ contract │ │ │ │
├────────────────────────┼──────────┼───────────┼────────────────────────────────────────────────────────────────────────────────┤
│ DleqVerifier library │ NEWT-629 │ 2D │ Solidity library for on-chain DLEQ proof verification via EIP-196 │
└────────────────────────┴──────────┴───────────┴────────────────────────────────────────────────────────────────────────────────┘

Tickets That Need Updates

┌─────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Ticket │ Update Needed │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-177 │ Update description: Pedersen DKG -> FROST DKG (RFC 9591), ark-ec -> frost-ristretto255, G1Point -> │
│ │ RistrettoPoint, 4 phases -> 2 rounds │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-177 │ Clear stale due date (was Feb 7) │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-176 │ Clear stale due date (was Feb 10) │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-178 │ Clear stale due date (was Feb 13), update to reference FROST not Pedersen │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-174 │ Clear stale due date (was Feb 13) │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-629 │ Rename "[Privacy P3]" -> "[Privacy P2D]" to match sub-phase numbering │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-630 │ Rename "[Privacy P4]" -> "[Privacy P2C]" to match sub-phase numbering │
├─────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ NEWT-628, 629, │ Assign to dennis.won │
│ 630 │ │
└─────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


★ Insight ─────────────────────────────────────
Sub-phase dependencies matter: 2A (MVP) is the critical path — everything else builds on it. 2B (rotation) and 2C (full DKG) can
partially overlap because they modify different subsystems: 2B is operator keystore + EpochRegistry contract, while 2C is the DKG
ceremony coordinator + on-chain commitments. 2D (slashing) must wait for both because it verifies DLEQ proofs (from 2A) in an epoch
context (from 2B).

The hpke crate augmentation is the single biggest technical risk — the standard HPKE API doesn't expose a open_with_precomputed_dh
entry point. We either need an upstream PR to the hpke crate (uncertain timeline) or a minimal fork. This should be spiked in week 1.
─────────────────────────────────────────────────

@denniswon
Copy link
Author

⏺ Memory file updated. Here's what changed:

Phase 2 section completely rewritten to reflect the Linear sync:

  • 11 tickets across 4 sub-phases (was 7 flat tickets with no structure)
  • Architecture decisions summary (FROST over Pedersen, Ristretto255, merged Prepare phase)
  • Per-sub-phase tables with due dates, blocking relationships, and exit criteria
  • Dependency graph showing the critical path: NEWT-638 → NEWT-176 → NEWT-178 → NEWT-630 → NEWT-629
  • 4 new tickets captured: NEWT-638 (HPKE augmentation), NEWT-639 (EpochRegistry), NEWT-640 (PrivacySlasher),
    NEWT-641 (DleqVerifier)
  • Key files that change section (from THRESHOLD_DKG.md)
  • Technical risk items section highlighting the HPKE crate augmentation as highest risk

Other updates:

  • Phase summary row updated: "Pedersen DKG" → "FROST DKG", "0/7" → "0/11"
  • PR #424 added to open PRs
  • Date bumped to 2026-03-08
  • Removed the separate "Linear Issue Status" table (redundant with per-phase tables)

★ Insight ─────────────────────────────────────
The memory file now serves as a single source of truth linking Linear tickets to the docs/THRESHOLD_DKG.md
research synthesis. The dependency graph makes the critical path clear — NEWT-638 (HPKE crate augmentation) is the
gating item for the entire Phase 2A MVP, since threshold decryption can't work without open_with_precomputed_dh.
This is why it was created as a separate ticket rather than bundled into NEWT-176.
─────────────────────────────────────────────────

✻ Crunched for 5m 40s

❯ /claude-supermemory:super-save phase 2 plan of newton privacy layer for threshold dkg research and implementation

⏺ Let me save this Phase 2 Threshold DKG plan to Supermemory.

⏺ Bash(node "/Users/denniswon/.claude/plugins/cache/supermemory-plugins/claude-supermemory/0.0.1/scripts/save-projec
t-memory.cjs" "[SAVE:denniswon:2026-03-09]…)
⎿  Project knowledge saved: newton-prover-avs
ID: 9cHSozFhnpr4V62M8z1tGQ

⏺ Saved to Supermemory (ID: 9cHSozFhnpr4V62M8z1tGQ). The Phase 2 Threshold DKG execution plan — architecture
decisions, 4 sub-phase timeline, all 11 Linear tickets, dependency graph, and technical risks — is now persisted
across sessions.

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

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