From fb0ee0886e5e8d955c959ec2493fa89c8fd67309 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:06:29 +0000 Subject: [PATCH 1/3] fix(platform-wallet): derive the shield-capacity regression fixture from the reserve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `regression_reports_max_from_usable_suffix_not_total_account_balance` failed on `--all-features`, on this branch and on its base. The failing line was the test's own shape-precondition guard, not a production assertion, and its message said what to do: re-seed the balances when the versioned reserve drops below the hardcoded leading balance. That is exactly what happened. `reserve()` is `2 x compute_minimum_shielded_fee(2 actions)`. Under the v9 event constants (proof verification 100_000_000, storage 344 bytes/action) it was 325_702_400; under v10 (40_000_000 and 550 bytes/action) it is 228_280_000. The fixture's 297_264_780 sits between the two, so the leading address stopped being sub-reserve dust and the "usable suffix" shape the test claims to build no longer existed. The planner is right either way: with a lower reserve that address genuinely is a viable input 0, so the whole balance genuinely is usable. Maximum spendable balance was never miscomputed. Derive the leading balance as `reserve() - 1` instead — the largest balance that must still be rejected by the strict `> reserve` viability test, so a tighter boundary than the magic number was, and one no future fee re-balance can invalidate. The guard goes with it: the precondition is now true by construction. CI never caught this. The wallet job filtered nextest with `not test(~shield)`, a substring match on the full test path, so the `shield_input_selection_tests` module was excluded as collateral by a filter aimed at the shielded-wallet suite. It took 47 pure-logic tests across the three wallet crates with it — input selection, FFI error codes and memo encoding, SQLite viewing-key rows — for a measured 0.089 s of runtime. Exclude by module path (`wallet::shielded::`) so the step skips what it means to skip, and pin `--no-tests fail` so a filter that stops selecting anything fails the step instead of passing green. Co-Authored-By: Claude Opus 5 --- .github/workflows/tests-rs-wallet.yml | 16 +++++++++--- .../src/wallet/platform_wallet.rs | 25 +++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.github/workflows/tests-rs-wallet.yml b/.github/workflows/tests-rs-wallet.yml index 64cc36997d9..e6637303f60 100644 --- a/.github/workflows/tests-rs-wallet.yml +++ b/.github/workflows/tests-rs-wallet.yml @@ -201,9 +201,16 @@ jobs: --locked \ -- --no-deps -D warnings - # Mirrors the workspace job's non-shielded step for the wallet crates: - # same package subset, same `not test(~shield)` filter (shielded wallet - # tests are not run there either). + # The shielded-wallet suite (Orchard proving, viewing-key binds, note + # scans) is excluded by MODULE PATH, not by the word "shield". A + # `test(~shield)` substring match also swept up ~47 pure-logic tests + # across the three crates — input selection, FFI error codes, memo + # encoding, SQLite viewing-key rows — that cost a tenth of a second and + # have no compensating job anywhere. + # + # `--no-tests fail` is the zero-match guard: a filter that stops + # selecting anything must fail the step, not pass green. Pinned rather + # than left to nextest's default, which is a default and not a promise. - name: Run wallet tests (non-shielded) run: | cargo nextest run \ @@ -212,7 +219,8 @@ jobs: --package platform-wallet-ffi \ --all-features \ --locked \ - -E 'not test(~shield)' + --no-tests fail \ + -E 'not test(~wallet::shielded::)' env: RUST_MIN_STACK: 4194304 CARGO_PROFILE_DEV_DEBUG: "0" diff --git a/packages/rs-platform-wallet/src/wallet/platform_wallet.rs b/packages/rs-platform-wallet/src/wallet/platform_wallet.rs index c1ae06ba180..2b316334032 100644 --- a/packages/rs-platform-wallet/src/wallet/platform_wallet.rs +++ b/packages/rs-platform-wallet/src/wallet/platform_wallet.rs @@ -2303,23 +2303,24 @@ mod shield_input_selection_tests { #[test] fn regression_reports_max_from_usable_suffix_not_total_account_balance() { - // Real account snapshot: the leading address is below the reserve, so + // Account snapshot whose leading address cannot pay the fee, so // capacity must come from the usable suffix, not the account total. - assert!( - 297_264_780 <= reserve(), - "regression shape requires the leading address to stay below the reserve; \ - re-seed the balances if the versioned reserve drops under 297_264_780" - ); + // The leading balance is derived from the reserve — one credit below + // the strict `> reserve` viability threshold, the largest balance that + // must still be rejected as input 0 — so the shape holds whatever the + // versioned fee schedule does next. + let dust = reserve() - 1; + let usable = 3_623_849_220; let candidates = vec![ - (addr(1), 297_264_780), + (addr(1), dust), (addr(2), 2_000_000_000), (addr(3), 1_623_849_220), ]; let plan = plan(candidates).unwrap(); - let expected_max = 3_623_849_220 - reserve(); + let expected_max = usable - reserve(); - assert_eq!(plan.preflight.account_balance_credits, 3_921_114_000); - assert_eq!(plan.preflight.usable_balance_credits, 3_623_849_220); + assert_eq!(plan.preflight.account_balance_credits, dust + usable); + assert_eq!(plan.preflight.usable_balance_credits, usable); assert_eq!(plan.preflight.fee_reserve_credits, reserve()); assert_eq!(plan.preflight.max_shieldable_credits, expected_max); assert!(plan.preflight.can_shield); @@ -2329,11 +2330,13 @@ mod shield_input_selection_tests { assert!(!chosen.contains_key(&addr(1))); assert_eq!(chosen.values().sum::(), expected_max); + // `available` reports the usable suffix, never the account total — + // the whole point of the regression. let err = plan.select_inputs(expected_max + 1).unwrap_err(); assert!(matches!( err, PlatformWalletError::PlatformShieldCapacityExceeded { available, required } - if available == 3_623_849_220 && required == 3_623_849_221 + if available == usable && required == usable + 1 )); } From 0eee25537639e40219f9a5b738bf664d354ce4a4 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:20:18 +0000 Subject: [PATCH 2/3] fix(ci): stop the workspace shield filter sweeping up the wallet packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `-E 'not test(~shield)'` is a substring match on the full test path, so it selects "anything with the word shield in its name", not "the shielded suites". For dpp, drive, drive-abci and dash-sdk that is survivable: the "Run shielded tests" phase compensates, and those packages genuinely need it — they are Orchard proving tests, and that phase runs them under `cargo test` in one shared process so a single verifying key is built instead of one per test. The three wallet packages have neither the compensator nor the need. They are absent from the shielded phase's package list, so everything the substring dropped fell into a gap no job covered: 145 `wallet::shielded::` tests, plus ~47 more whose only sin is the word — input selection, FFI error codes, memo encoding, SQLite viewing-key rows. Measured, they cost ~6 s wall under nextest's process-per-test model, so the VK-reuse argument that justifies the exclusion elsewhere does not apply to them. Carve the wallet packages out of the exclusion by package rather than retarget the pattern by module path: a module-path filter would also stop excluding dpp/drive/drive-abci/dash-sdk, pulling their proving tests into the nextest phase where each would rebuild its own verifying key. The strategy-simulation phase keeps its `~shield` exclusion for the same reason — drive-abci IS in the compensating phase. Both nextest invocations pin `--no-tests fail` so an expression that stops selecting anything fails the step instead of passing green. Co-Authored-By: Claude Opus 5 --- .github/workflows/tests-rs-workspace.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests-rs-workspace.yml b/.github/workflows/tests-rs-workspace.yml index 1c159c2c706..f9891e528bd 100644 --- a/.github/workflows/tests-rs-workspace.yml +++ b/.github/workflows/tests-rs-workspace.yml @@ -324,6 +324,20 @@ jobs: # caught minutes after merge — the same safety-net pattern as the # shielded phase. Keeping this phase's filter identical across events # keeps the `rust` codecov flag comparable between PR and push runs. + # + # `test(~shield)` is a substring match on the FULL test path, so it does + # not mean "the shielded suites" — it means "anything with the word in + # its name". The shielded phase below compensates for dpp / drive / + # drive-abci / dash-sdk, which genuinely need it: they are Orchard + # proving tests, and that phase runs them under `cargo test` in a shared + # process so one verifying key is built instead of one per test. + # + # The three wallet packages have no such compensator and no such need — + # their whole `wallet::shielded::` suite is 145 tests in ~6 s under + # nextest's process-per-test model — so excluding them dropped 145 + # shielded tests plus ~47 more that merely contain the word (input + # selection, FFI error codes, memo encoding, SQLite viewing-key rows) + # into a gap no job covered. They are carved out of the exclusion here. - name: Run non-shielded tests with nextest (parallel, compiles all packages) if: steps.coverage-cache.outputs.reuse == 'false' run: | @@ -352,7 +366,8 @@ jobs: --package keyword-search-contract \ --all-features \ --locked \ - -E 'not test(~shield) and (not binary_id(=drive-abci::strategy_tests) or test(~comprehensive_mixed_operations))' + --no-tests fail \ + -E '(not test(~shield) or package(platform-wallet) or package(platform-wallet-storage) or package(platform-wallet-ffi)) and (not binary_id(=drive-abci::strategy_tests) or test(~comprehensive_mixed_operations))' env: RUST_MIN_STACK: 4194304 CARGO_PROFILE_DEV_DEBUG: "0" @@ -370,6 +385,9 @@ jobs: # The chain simulations excluded from the phase above. PR runs skip # them (and upload no rust-strategy coverage — codecov carries the base # commit's forward); push, nightly, and dispatch runs execute them all. + # The `~shield` exclusion here IS compensated — drive-abci is in the + # shielded phase below — so it stays; `--no-tests fail` is the guard + # against the whole expression silently selecting nothing. - name: Run full chain-simulation suite (push and nightly only) id: strategy-tests if: >- @@ -380,6 +398,7 @@ jobs: --package drive-abci \ --all-features \ --locked \ + --no-tests fail \ -E 'binary_id(=drive-abci::strategy_tests) and not test(~comprehensive_mixed_operations) and not test(~shield)' env: RUST_MIN_STACK: 4194304 From 5377ad627393102f616cccec85f4fbd7c1995c88 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:22:29 +0000 Subject: [PATCH 3/3] fix(ci): execute the shielded wallet suite in the wallet fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 145 `wallet::shielded::` tests ran in no CI job at all. Both workflows excluded them, and tests-rs-workspace.yml's compensating "Run shielded tests" phase covers dpp / drive / drive-abci / dash-sdk only — the three wallet packages are absent from its package list. The sibling commit fixes the workspace path; this fixes the fast path a wallet-scoped PR actually takes, and both are needed because the two workflows are mutually exclusive for any given PR. Drop the test filter entirely rather than narrow it again. Nothing in these three packages is worth excluding: the shielded suite is 145 tests in ~6 s wall (~97 s CPU) under nextest's process-per-test model, and `--all-features` compiles those binaries whether or not they are selected — so the exclusion was saving execution time on artifacts already built and discarded. Removing the expression also removes the whole class of substring-filter bug from this workflow rather than moving it. Measured on this base: 1804 tests, 1804 passed, 25.6 s for the step. Co-Authored-By: Claude Opus 5 --- .github/workflows/tests-rs-wallet.yml | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests-rs-wallet.yml b/.github/workflows/tests-rs-wallet.yml index e6637303f60..ed8bc622717 100644 --- a/.github/workflows/tests-rs-wallet.yml +++ b/.github/workflows/tests-rs-wallet.yml @@ -201,17 +201,22 @@ jobs: --locked \ -- --no-deps -D warnings - # The shielded-wallet suite (Orchard proving, viewing-key binds, note - # scans) is excluded by MODULE PATH, not by the word "shield". A - # `test(~shield)` substring match also swept up ~47 pure-logic tests - # across the three crates — input selection, FFI error codes, memo - # encoding, SQLite viewing-key rows — that cost a tenth of a second and - # have no compensating job anywhere. + # No test filter: this job runs everything in the three wallet packages, + # shielded suite included. There is nothing here worth excluding — + # `wallet::shielded::` is 145 tests in ~6 s wall (~97 s of CPU) under + # nextest, and `--all-features` compiles those binaries whether or not + # they are selected, so skipping them saved execution time on artifacts + # already paid for and thrown away. # - # `--no-tests fail` is the zero-match guard: a filter that stops - # selecting anything must fail the step, not pass green. Pinned rather - # than left to nextest's default, which is a default and not a promise. - - name: Run wallet tests (non-shielded) + # This job and tests-rs-workspace.yml are mutually exclusive paths for a + # given PR, so the shielded suite has to run in BOTH or a wallet-scoped + # PR silently loses it. The workspace job carves the wallet packages out + # of its own shield exclusion for the same reason. + # + # `--no-tests fail` is the zero-match guard: a selection that matches + # nothing must fail the step, not pass green. Pinned rather than left to + # nextest's default, which is a default and not a promise. + - name: Run wallet tests run: | cargo nextest run \ --package platform-wallet \ @@ -219,8 +224,7 @@ jobs: --package platform-wallet-ffi \ --all-features \ --locked \ - --no-tests fail \ - -E 'not test(~wallet::shielded::)' + --no-tests fail env: RUST_MIN_STACK: 4194304 CARGO_PROFILE_DEV_DEBUG: "0"