From a7b95ddb4b61d06c02c7f583f79044a895027ef9 Mon Sep 17 00:00:00 2001 From: Marvin Jones Date: Wed, 15 Jul 2026 14:19:25 -0400 Subject: [PATCH] docs(privacy): reattribute clock bug's deeper cause to logos-execution-zone The spel-framework dispatcher filter that drops the clock account is real, but ValidatedStateDiff::from_public_transaction never checks that a program's output accounts match the caller-declared message.account_ids, unlike the privacy circuit's own account_identities.len() == states_iter.len() check. That absence is why the drop went unnoticed by every pre-existing public AMM test. Update findings.md's conclusion and add a dated correction to privacy-test-matrix.md attributing this second, independent gap to logos-execution-zone. --- docs/findings.md | 4 ++-- docs/privacy-test-matrix.md | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/findings.md b/docs/findings.md index 042d433..bf6adff 100644 --- a/docs/findings.md +++ b/docs/findings.md @@ -74,7 +74,7 @@ In this task, we extend testing for LEZ programs to cover privacy features: # LEZ programs -## AMM program (unusual issues) +## AMM program | Function tested | Test name | Category | Description of objective | Result | |---|---|---|---|---| @@ -162,4 +162,4 @@ Privacy coverage for LEZ program tests is greatly improved from the added tests. - Privacy transactions have issues with chain calls in which multiple calls affect the same private account. This issue can be mitigated by adopting account diff paradigm instead of the current "account state replacement" that we currently use. Additional observation: -AMM's chained-call privacy tests were blocked by the clock account being `DEFAULT_PROGRAM_ID`-owned in the test fixture, which trips a `spel-framework` dispatcher bug (upstream in `logos-co/spel`, confirmed present through v0.6.0) that silently drops any default-owned, non-default, unclaimed account from a program's output. Fixed by giving the fixture's clock account a non-default owner; see the AMM section. The dispatcher bug itself remains open upstream. \ No newline at end of file +AMM's chained-call privacy tests were blocked by the clock account being `DEFAULT_PROGRAM_ID`-owned in the test fixture, which trips a `spel-framework` dispatcher filter (upstream in `logos-co/spel`) that silently drops any default-owned, non-default, unclaimed account from a program's output. Fixed by giving the fixture's clock account a non-default owner; see the AMM section. But the deeper bug is in `logos-execution-zone` itself: `ValidatedStateDiff::from_public_transaction` never checks that the accounts touched in a program's output match the caller-declared `message.account_ids` — no count, no membership check, nothing like the privacy circuit's own `account_identities.len() == states_iter.len()` assertion. That's why the `spel-framework` drop went unnoticed by every pre-existing public AMM test: the public path has no validation capable of catching a silently-dropped account at all. Both remain open upstream. \ No newline at end of file diff --git a/docs/privacy-test-matrix.md b/docs/privacy-test-matrix.md index e4d535f..dfd7fda 100644 --- a/docs/privacy-test-matrix.md +++ b/docs/privacy-test-matrix.md @@ -551,6 +551,26 @@ merge point from `logos-co/release/v0.5.0` (commit `73fc462`) shows the filter a there too, before any of the fork's own commits. **This is an upstream `logos-co/spel` bug**, inherited unchanged by the pinned fork — report it against the former, not the latter. +**Correction — there's a second, independent bug in `logos-execution-zone` itself (2026-07-15)**: +the "not a `lez_core`/circuit bug" claim above is too narrow. `ValidatedStateDiff:: +from_public_transaction` (`lee/state_machine/src/validated_state_diff.rs`) never checks that the +accounts touched in a program's output match the caller-declared `message.account_ids` — no count +check, no membership check, nothing analogous to the privacy circuit's own `account_identities. +len() == states_iter.len()` assertion in `compute_circuit_output`. The reconciliation loop just +does `state_diff.insert(pre.account_id, post.account().clone())` for whatever pairs the program's +output happens to contain, however many that is, and returns `Ok(...)` regardless. + +That absence is why the `spel-framework` drop went unnoticed by every one of this repo's ~34 +pre-existing public AMM tests: the public path has no validation capable of catching a +silently-dropped account at all, so a bug with zero privacy dimension to it hid behind passing +public tests until the privacy circuit's stricter, positional bookkeeping happened to expose it. +Two independent upstream defects, not one: `spel-framework`'s over-broad filter (which creates the +drop) and `logos-execution-zone`'s missing account-accounting check on the public path (which lets +any such drop — from this or any future bug — go completely undetected). The latter is arguably +the more consequential of the two, since it's a general soundness gap independent of clock, AMM, +or privacy entirely. Both should be reported upstream; report the `lee` gap against +`logos-co/logos-execution-zone`, not `logos-co/spel`. + ### ✅ Fixed for these tests (2026-07-15) — test-fixture clock ownership, not a circuit workaround The immediate blocker for all five `CHAIN`-dimension AMM privacy tests below was that