Created
April 9, 2026 13:51
-
-
Save denniswon/28aa29640e8e9eb6677a6e175a013834 to your computer and use it in GitHub Desktop.
Newton Privacy Layer — Known Limitations & Next Steps
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Here's the full picture: | |
| --- | |
| Newton Privacy Layer — Known Limitations & Next Steps | |
| ★ Insight ───────────────────────────────────── | |
| The limitations fall into three buckets: (1) security gaps where the system doesn't enforce what it should, (2) trust assumptions | |
| where centralization remains, and (3) missing infrastructure that blocks future phases. Each has a clear resolution path — none are | |
| architectural dead ends. | |
| ───────────────────────────────────────────────── | |
| Known Limitations | |
| 1. Challenger Cannot Verify Privacy Tasks (NEWT-664) — SECURITY GAP | |
| Problem: evaluate_and_prove() in crates/challenger/src/lib.rs:171 replays task responses to detect misbehavior, but it has no access | |
| to decrypted privacy data. The Rego evaluation runs without data.identity.*, data.privacy.*, or data.ephemeral.* — so the challenger's | |
| result will differ from the operator's result even when the operator was honest. | |
| Impact: Operators can return arbitrary results for privacy-enabled tasks without risk of slashing. This is the single largest security | |
| gap in the shipped system. | |
| Solution (two-stage): | |
| - Phase 1.5 (short-term): Detect privacy-enabled tasks and skip challenge evaluation + emit challenger_privacy_tasks_skipped metric at | |
| WARN level. Prevents false-positive slashing. | |
| - Phase 2 (long-term): Challenger holds a FROST key share from the DKG ceremony, requests partial decryptions from operators, combines | |
| t-of-n shares, and replays with full privacy context. | |
| Linear: NEWT-664, High priority, Backlog. | |
| --- | |
| 2. Gateway Reconstructs Plaintext (Trust Assumption) | |
| Problem: Even with threshold DKG, the gateway is the entity that combines the partial DH outputs via Lagrange interpolation. It sees | |
| the reconstructed shared secret and decrypts the HPKE ciphertext. This is true for both centralized and threshold modes — the | |
| difference is whether the gateway holds the full key (centralized) or reconstructs it per-task from operator contributions | |
| (threshold). | |
| Impact: A compromised gateway can read all ephemeral privacy data. Identity and confidential data are safer — operators decrypt those | |
| locally. | |
| Solution: Phase 3 MPC — operators evaluate the Rego policy directly on encrypted data without any party seeing plaintext. This | |
| requires: | |
| - NEWT-173 (privacy-preserving policy evaluation with MPC/ZK) | |
| - NEWT-168 (MPC framework — canceled, needs rescoping) | |
| - Possibly FHE or garbled circuits depending on benchmarking (NEWT-631) | |
| Timeline: Phase 3, no concrete date. | |
| --- | |
| 3. No Epoch-Based Key Rotation (Forward Secrecy Gap) | |
| Problem: The FROST DKG ceremony produces a single set of key shares that remain valid indefinitely. If an attacker compromises t | |
| shares across any time window, they can decrypt all data ever encrypted to that key. | |
| Impact: No forward secrecy. A single key compromise has unlimited blast radius. | |
| Solution: PSS (Proactive Secret Sharing) with epoch rotation: | |
| - NEWT-628 (canonical spec, due 2026-05-30): epoch-based rotation, MSK destruction, grace periods | |
| - NEWT-639: EpochRegistry Solidity contract for on-chain epoch lifecycle | |
| - Old shares zeroed via zeroize crate after 2-epoch grace period | |
| - MPK stays constant across epochs (no client re-encryption needed) | |
| Timeline: Phase 2C, target Q2 2026. | |
| --- | |
| 4. No Privacy-Specific Slashing | |
| Problem: Operators who leak plaintext, submit invalid partial decryptions, or miss DKG ceremony rounds face no economic penalty beyond | |
| the general task misbehavior slashing. | |
| Impact: No economic deterrent for privacy-specific misbehavior. | |
| Solution: | |
| - NEWT-629 (slashing conditions, blocked by NEWT-628 epoch context) | |
| - NEWT-640 (PrivacySlasher contract) | |
| - NEWT-641 (DleqVerifier Solidity library for on-chain DLEQ proof verification) | |
| - Slashing conditions: missed partial decryption, invalid DLEQ proof, plaintext leakage attestation | |
| Timeline: Phase 2D, after epoch rotation ships. | |
| --- | |
| 5. No On-Chain Auditability for Threshold Operations | |
| Problem: Threshold decryption happens entirely off-chain. There's no on-chain record of which operators participated, which DLEQ | |
| proofs were submitted, or which epoch's key was used. | |
| Impact: No verifiable audit trail for privacy operations. Disputes rely on off-chain logs. | |
| Solution: NEWT-630 (on-chain aggregation commitments) — post a commitment hash of the threshold operation metadata on-chain after each | |
| decryption. | |
| Timeline: Phase 2C, alongside epoch rotation. | |
| --- | |
| 6. Plaintext in Gateway Memory Not Wrapped with secrecy Crate | |
| Problem: Decrypted ephemeral privacy data exists as plain Vec<u8> / String in gateway memory during the task lifecycle. If the process | |
| dumps core or a debugging tool attaches, plaintext is readable. | |
| Impact: Low — requires host-level access to exploit. But defense-in-depth says wrap it. | |
| Solution: Wrap with secrecy::Secret<Vec<u8>> and zeroize-on-drop. The keystore already uses zeroize::Zeroizing (confirmed in | |
| crates/core/src/dkg/keystore.rs:28). Extend this pattern to ephemeral decrypted data in inline_privacy.rs and sync.rs. | |
| Timeline: Quick win, can be done anytime. | |
| --- | |
| 7. Private Data Storage Still on PostgreSQL | |
| Problem: Operators currently access encrypted data blobs from the gateway's PostgreSQL database. External operators (not co-located | |
| with the gateway) need direct DB access — a security and operational risk. | |
| Impact: Limits operator decentralization. External operators can't participate in identity/confidential data flows without DB | |
| credentials. | |
| Solution: Private Data Storage Layer (decided 2026-03-19 team meeting): | |
| - Phase 1 (done): Postgres stopgap with operator DB access | |
| - Phase 2: HPKE migration (done) | |
| - Phase 3: redb (pure Rust) + Jellyfish Merkle Tree, NATS JetStream sync | |
| - Linear: NEWT-799 through NEWT-813 | |
| Timeline: Separate project track, Q3 2026. | |
| --- | |
| Prioritized Next Steps | |
| ┌──────────┬─────────────────────────────────────────────┬──────────────┬──────────┬────────────────────────────────────────────┐ | |
| │ Priority │ What │ Linear │ Effort │ Why First │ | |
| ├──────────┼─────────────────────────────────────────────┼──────────────┼──────────┼────────────────────────────────────────────┤ | |
| │ 1 │ Challenger privacy skip (NEWT-664 Phase │ NEWT-664 │ 1-2 days │ Security gap — false-positive slashing │ | |
| │ │ 1.5) │ │ │ risk │ | |
| ├──────────┼─────────────────────────────────────────────┼──────────────┼──────────┼────────────────────────────────────────────┤ | |
| │ 2 │ secrecy crate for ephemeral plaintext │ (no ticket) │ 1 day │ Quick defense-in-depth win │ | |
| ├──────────┼─────────────────────────────────────────────┼──────────────┼──────────┼────────────────────────────────────────────┤ | |
| │ 3 │ PSS epoch rotation (NEWT-628) │ NEWT-628 │ 3-4 │ Forward secrecy — longest-pole Phase 2 │ | |
| │ │ │ │ weeks │ item │ | |
| ├──────────┼─────────────────────────────────────────────┼──────────────┼──────────┼────────────────────────────────────────────┤ | |
| │ 4 │ EpochRegistry contract (NEWT-639) │ NEWT-639 │ 1-2 │ On-chain epoch lifecycle, blocks slashing │ | |
| │ │ │ │ weeks │ │ | |
| ├──────────┼─────────────────────────────────────────────┼──────────────┼──────────┼────────────────────────────────────────────┤ | |
| │ 5 │ On-chain aggregation commitments (NEWT-630) │ NEWT-630 │ 1 week │ Auditability for threshold ops │ | |
| ├──────────┼─────────────────────────────────────────────┼──────────────┼──────────┼────────────────────────────────────────────┤ | |
| │ 6 │ PrivacySlasher + DleqVerifier │ NEWT-640, │ 2 weeks │ Economic security for privacy misbehavior │ | |
| │ │ (NEWT-640/641) │ 641 │ │ │ | |
| ├──────────┼─────────────────────────────────────────────┼──────────────┼──────────┼────────────────────────────────────────────┤ | |
| │ 7 │ Privacy slashing conditions (NEWT-629) │ NEWT-629 │ 1 week │ Ties slasher to specific violations │ | |
| └──────────┴─────────────────────────────────────────────┴──────────────┴──────────┴────────────────────────────────────────────┘ | |
| Items 1-2 are quick wins. Items 3-7 form the Phase 2C/2D roadmap and should be executed in order (628 → 639 → 630 → 640/641 → 629). | |
| --- | |
| ★ Insight ───────────────────────────────────── | |
| The system is in a solid position for a pre-mainnet AVS — all three privacy data paths work end-to-end, threshold DKG is operational, | |
| and KMS is fully removed. The remaining gaps are about hardening (forward secrecy, slashing, auditability) rather than fundamental | |
| capability. NEWT-664 is the only gap that affects correctness of the current system — everything else is about reducing trust | |
| assumptions or adding economic security. The Phase 2C/2D work is pure Solidity + coordination protocol — the Rust-side crypto is done. | |
| ───────────────────────────────────────────────── | |
| Want me to create a Linear ticket for the secrecy crate wrapping, or start working on NEWT-664 (challenger privacy skip)? |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
looks right
⏺ Question 4 of N: Where does the challenger get the raw DLEQ proofs (private inputs)?
The on-chain commitment (NEWT-630) stores only a hash. The challenger needs the actual per-operator data to feed into the SP1 circuit.
Three options:
A) EigenDA: Gateway posts the raw DLEQ data to EigenDA alongside the on-chain commitment hash. EigenDA provides data availability
guarantees — the data is retrievable by anyone for the dispute window. Cost: ~$0.01 per blob. This is how EigenLayer-native systems
typically handle DA.
B) Gateway API endpoint: Gateway exposes a newt_getDecryptionProofs(taskId) RPC endpoint that returns the raw data. Simpler but relies on
the gateway being online and honest about serving the data. A compromised gateway could refuse to serve proofs for tasks where it
colluded.
C) Operator-direct: Challenger contacts each operator directly (via their socket address) to re-request their partial decryption + DLEQ
proof. Operators are incentivized to respond (if they're honest, they want the bad operator slashed). No single point of failure.
My recommendation: Option A (EigenDA). Reasons:
A, B, or C?
❯ Agreed. EigenDA
⏺ Question 5 of N: Should the SP1 DLEQ circuit be a new circuit or extend the existing sp1-rego circuit?
Currently we have one SP1 circuit at circuits/sp1-rego/ that verifies Rego policy evaluation for the existing challenger. Two options:
A) New separate circuit (circuits/sp1-dleq/): Independent binary, independent proof key, independent verifier on-chain. Clean separation
— DLEQ verification has nothing to do with Rego policy evaluation.
B) Extend sp1-rego: Add a "mode" flag — the circuit either verifies Rego policy OR verifies DLEQ proofs depending on the input. Shares
the same SP1 verifier contract on-chain.
My recommendation: Option A (separate circuit). The two proofs verify completely different things — Rego policy correctness vs DLEQ
cryptographic validity. Combining them into one circuit adds complexity for no benefit. The SP1 verifier contract on-chain can verify
proofs from any circuit (it's parameterized by vkey), so there's no deployment overhead from having two circuits.
A or B?
❯ a
⏺ Question 6 of N: How does the challenger know something is wrong in the first place?
The challenger needs a trigger to investigate a task's DLEQ proofs. It won't speculatively verify every task's proofs via SP1 (too
expensive). Three detection models:
A) Verify all — optimistic: The challenger fetches DLEQ data from EigenDA for every privacy-enabled task and runs off-chain DLEQ
verification (cheap Rust code, no SP1). Only when it detects an invalid proof does it generate the expensive SP1 proof for on-chain
submission. This is the same pattern as the existing policy challenger — it re-evaluates every task, only challenges when results differ.
B) Selective — commitment-based: The challenger only investigates tasks where the gateway's on-chain commitment includes an operator that
was previously flagged as suspicious (reputation system). Reduces work but could miss first-time attackers.
C) Reactive — external report: Anyone can tip the challenger (via an RPC endpoint or on-chain bounty) that a specific task has bad
proofs. The challenger then investigates that specific task.
My recommendation: Option A (verify all, optimistic). Reasons:
The challenger flow becomes:
A, B, or C?
❯ option A
⏺ Question 7 of N: Timing — when does the commitment get posted?
The DLEQ proofs happen during the Prepare phase, but the on-chain transaction happens at respondToTask (end of Commit phase). The
commitment needs to be included in the on-chain submission. Two options:
A) Embed in respondToTask: Add decryptionCommitment and operatorBitmap as new fields in TaskResponseMetadata. The commitment travels with
the existing BLS-aggregated response — one transaction, no extra on-chain call. The gateway computes the commitment during the Prepare
phase and carries it through to submission.
B) Separate transaction: Gateway submits registerDecryptionCommitment(taskId, commitment, bitmap) as a separate call after respondToTask.
Two transactions per privacy task. More gas, but doesn't require modifying the TaskManager interface.
My recommendation: Option A (embed in respondToTask). Modifying TaskResponseMetadata is a struct change that affects the ABI selector
(per lessons.md — "struct changes silently break all callers"), but NEWT-630 is a planned breaking change anyway. Embedding avoids the
extra transaction cost (~21k gas saved per task) and ensures the commitment is atomically bound to the task response — no window where a
response exists without a commitment.
The trade-off: changing TaskResponseMetadata means all clients (gateway, challenger) must update their ABI. But this is a coordinated
deploy — same as any contract upgrade.
A or B?
❯ option A
⏺ Question 8 of N: What happens for non-privacy tasks?
Not every task uses threshold decryption. For tasks without privacy data, there are no DLEQ proofs to commit. The decryptionCommitment
field in TaskResponseMetadata needs to handle this.
A) Zero-value sentinel: decryptionCommitment = bytes32(0) and operatorBitmap = 0 for non-privacy tasks. The PrivacySlasher rejects
challenges against tasks with zero commitment. Simple, no branching in the contract.
B) Boolean flag: Add bool hasDecryptionCommitment to TaskResponseMetadata. Explicit but uses an extra storage slot.
C) Omit from non-privacy tasks: Use a separate respondToTaskWithPrivacy() function that includes the commitment fields. Non-privacy tasks
use the existing respondToTask(). Two code paths on both the gateway and contract side.
My recommendation: Option A (zero-value sentinel). Reasons:
A, B, or C?
❯ A
⏺ Question 9 of N: Challenge window — same 100 blocks as policy challenges, or different?
The existing ChallengeVerifier uses taskChallengeWindowBlock (configurable, time-normalized to ~360s across chains). Privacy slashing
could share the same window or have its own.
A) Same window: Both policy and privacy challenges must be submitted within taskChallengeWindowBlock of respondToTask. One window to
reason about. After it expires, the task is finalized — no privacy or policy challenges.
B) Separate window: Privacy challenges have their own privacyChallengeWindowBlock, potentially longer (DLEQ data needs to be fetched from
EigenDA + SP1 proof generated, which takes longer than a ZK policy re-evaluation). But adds configuration complexity.
My recommendation: Option A (same window). The SP1 proof generation for DLEQ (~10-30s) is comparable to or faster than the SP1 Rego
proof. The challenger runs off-chain DLEQ verification immediately (microseconds), only generates the SP1 proof when a violation is
found. The 360s window is more than enough for: detect violation (~1s) + generate SP1 proof (~30s) + submit transaction (~12s). Sharing
the window simplifies the mental model — after the window closes, the task is fully finalized.
A or B?
❯ A
⏺ I believe we've covered all the major design branches. Let me summarize.
DleqVerifier / Privacy Slashing Design Summary (9 decisions)
┌─────┬────────────────┬────────────────────────────────────────────────┬────────────────────────────────────────────────────────────┐
│ # │ Decision │ Choice │ Reasoning │
├─────┼────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ 1 │ Verification │ SP1 ZK circuit │ Least execution risk — no DKG/PSS changes, no new operator │
│ │ approach │ │ dependencies, bug in circuit doesn't corrupt key material │
├─────┼────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ │ │ │ Verifies all operator DLEQ proofs for a task, outputs │
│ 2 │ Circuit scope │ Batch verification │ invalid bitmap. Matches ChallengeLib.slashSigningOperators │
│ │ │ │ pattern │
│ │ │ │ ChallengeLib.slashSigningOperators pattern │
├─────┼────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ │ │ Public: commitmentHash + epochId + taskId. │ │
│ 3 │ Circuit I/O │ Output: invalidOperatorBitmap + re-computed │ Commitment binding prevents fabricated inputs │
│ │ │ commitmentHash for binding │ │
├─────┼────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ │ Data │ │ Gateway posts raw DLEQ data alongside on-chain │
│ 4 │ availability │ EigenDA │ commitment hash. Trustless retrieval, natural for │
│ │ │ │ EigenLayer AVS │
├─────┼────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ 5 │ Circuit │ New circuits/sp1-dleq/ │ Separate from sp1-rego — different proof types, clean │
│ │ organization │ │ separation │
├─────┼────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ │ Detection │ │ Challenger runs off-chain DLEQ verify (~microseconds) on │
│ 6 │ model │ Verify all (optimistic) │ every privacy task, only generates SP1 proof when │
│ │ │ │ violation found │
├─────┼────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ 7 │ Commitment │ Embed in respondToTask │ New fields in TaskResponseMetadata — atomic, one │
│ │ timing │ │ transaction, no commitment gap │
├─────┼────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ 8 │ Non-privacy │ Zero-value sentinel │ decryptionCommitment = bytes32(0), PrivacySlasher │
│ │ tasks │ │ rejects challenges on zero commitment │
├─────┼────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ 9 │ Challenge │ Same as policy (taskChallengeWindowBlock) │ ~360s is sufficient for detect + SP1 prove + submit. One │
│ │ window │ │ window simplifies finalization │
└─────┴────────────────┴──────────────────────────────────────────────────┴──────────────────────────────────────────────────────────┘
Implementation order refined:
Want me to save this as a plan and update the Linear tickets with the SP1 approach?