Skip to content

test(rs-platform-wallet): e2e suite, Found-025 fix + triage pins#3549

Draft
Claudius-Maginificent wants to merge 295 commits into
fix/rs-platform-wallet-auto-select-inputsfrom
feat/rs-platform-wallet-e2e
Draft

test(rs-platform-wallet): e2e suite, Found-025 fix + triage pins#3549
Claudius-Maginificent wants to merge 295 commits into
fix/rs-platform-wallet-auto-select-inputsfrom
feat/rs-platform-wallet-e2e

Conversation

@Claudius-Maginificent
Copy link
Copy Markdown
Collaborator

@Claudius-Maginificent Claudius-Maginificent commented Apr 27, 2026

Issue being fixed or feature implemented

End-to-end test suite for rs-platform-wallet, now carrying — as FIXED, landed code (not pinned, not deferred) — the Found-026 platform-receive-address reserve-on-hand-out defect and the Found-017 register-wallet persist-error fail-closed defect, on top of the Found-025 rs-sdk address-sync fix and the #516 dead-accessor removal. The duplicate-address race that previously masked PA-001/002/003 and id_002/id_005 is gone; those cases are now green. The branch is at landed HEAD 76baafda3f.

What was done?

Chain atop the prior #516 base (9e24f1f343):

  1. 9e24f1f343fix(rs-platform-wallet/e2e) (chore(release): bump version to v0.22.15 and update changelog #516, QA-516-001/002): prior base. Deletes the dead SpvRuntime::event_manager() accessor, replaces PlatformEventManager's mutable ArcSwap+add_handler with an immutable construction-fixed Arc<[handler]> set; treats broadcast RecvError::Lagged as the mn-list stall signal, fast-fails instead of silently dropping. Also carries the Found-025 rs-sdk address-sync fix (separately PR'd as fix(sdk): address-sync no longer silently discards balance changes for post-snapshot addresses (Found-025) #3650 against v3.1-dev) and the earlier fix(dashmate): docker undefined error #480/test(dapi): fix froken subscribeToNewBlockHeaders test #508 funding-gate triage.
  2. 0618e6eba8fix(rs-platform-wallet) (Found-026, FIXED): reserve the platform receive address on hand-out so next_unused_address() cannot return a duplicate under concurrency. Production fix in src/wallet/platform_addresses/wallet.rs (+150/-2) plus two guard tests.
  3. cba5125a29fix(rs-platform-wallet/e2e) (QA-001, post-condition): align slot-0 consumption with the native reserve-on-hand-out semantics (wallet_factory.rs, gap_limit.rs, pa_001b_change_address_branch.rs).
  4. 00437dbeedfix(rs-platform-wallet/e2e) (chain-confirmed-gate vs stale-local-cache, post-test(dapi): fix froken subscribeToNewBlockHeaders test #508): sync wallet cache after the chain-confirmed funding gate before consume, across 5 cases (pa_001, pa_002, pa_003, id_002, id_005sync_balances insertion). This corrects the test-sequencing defect that the reclass below re-attributes those cases to.
  5. 0e547b02fefix(rs-platform-wallet) (Found-017, FIXED): fail-closed on registration persist error in src/manager/wallet_lifecycle.rs (+14); adds the found_017_register_wallet_store_ok_persists regression case (+119) and registers it in cases/mod.rs.
  6. 76baafda3fdocs(rs-platform-wallet/e2e) (gated reclass, HEAD): reclassify Found-026/Found-017 as FIXED in TEST_SPEC; re-spec PA-005 Invariant-1 assert_eq!assert_ne! per architecture adjudication (now correct, passes); RE-ATTRIBUTE pa_001/002/003/id_002/id_005 from the Found-026-family race to the now-fixed chain-confirmed-gate test-sequencing defect (now green); convert the found_017 pin from red-by-design to passing-as-regression.

v3.1-dev backports are tracked as separate sibling PRs — Found-026 and Found-017 require semantic re-apply because the target regions diverged at v3.1-dev; they are not clean cherry-picks and are not part of this PR.

How Has This Been Tested?

CI posture — READ THIS BEFORE THE e2e LINE (honest pre-statement):

The expected-red set SHRANK with this chain. PA-001/002/003, id_002/id_005, QA-501/QA-502 (the Found-026-family race) and found_017 are no longer expected red — they are FIXED and green (found_017 now passes as a regression test). The rs-platform-wallet e2e job may still be non-green, but the remaining non-green set is now strictly:

The dash_sdk … (Found-025) WARN seam is still EXPECTED-present — it is the Found-025 fix's observability working as designed (address-sync surfacing the balance-change reconciliation it previously discarded silently), NOT a regression.

Validation status (stated precisely — no green-paint):

Found-026 and Found-017 were proven via a focused 3/3 14-thread re-run on the affected sites plus a fully-clean full-suite R2 and an architecture adjudication of the PA-005 Invariant-1 re-spec. No flake-masking, no green-paint — the previously-masked PA-001/002/003 + id_002/id_005 went genuinely green once the duplicate-address race was eliminated at the production layer. The Found-025 fix remains AUTHENTIC under the prior 14-thread v-run (#501; 1845 upstream WARNs still emit). The integration onto feat was Adams-verified scope-clean (byte-faithful fast-forward 9e24f1f343..76baafda3f, no stray production files, no injection). This PR remains DRAFT pending the operator's final disposition.

Remaining non-green is INTENTIONAL (the failure IS the proof), NOT failures to fix in this PR: found_006/found_021/found_022 (rust-dashcore #762/#763/#764, RED-by-design), pa_3040 (#3040), al_001 (Found-008/#3641). PA-005 is green post-re-spec.

Breaking Changes

None. The Found-026 fix (0618e6eba8) and Found-017 fix (0e547b02fe) are correctness fixes in rs-platform-wallet with no external API-surface change. The #516 refactor (carried in the 9e24f1f343 base) changes PlatformWalletManager::new to take a Vec<app_handlers> and deletes the unused SpvRuntime::event_manager() accessor — an internal-handler-wiring change with no external API consumer; all in-tree call sites and examples are updated in the same commit. The Found-025 rs-sdk fix is separately PR'd as #3650 against v3.1-dev.

Related Issues & PRs

All entries verified read-only via gh against dashpay/platform and dashpay/rust-dashcore. State as of refresh.

Carve-out / backport PRs (v3.1-dev)

Upstream pins (rust-dashcore — RED-by-design)

Tracked platform issues

Superseded / closed

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Co-authored by Claudius the Magnificent AI Agent

@github-actions github-actions Bot added this to the v3.1.0 milestone Apr 27, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9bf65d8c-7bfd-43d5-9ea9-1cde7ec21321

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

An end-to-end testing framework for rs-platform-wallet is added with shared process context, bank wallet funding, persistent wallet registry, cleanup/orphan sweeping, and complete test infrastructure including configuration management, SDK setup, event notification, and example test cases demonstrating transfers between platform addresses. Related SDK modules are also exposed for external use, and seed-based signer constructors are introduced.

Changes

Cohort / File(s) Summary
E2E Framework Infrastructure
packages/rs-platform-wallet/tests/e2e/framework/config.rs, workdir.rs, registry.rs
Environment variable loading with .env file support, workdir slot reservation with exclusive file locking, and persistent JSON-backed wallet seed registry with atomic writes and corruption recovery.
E2E Harness & Context
packages/rs-platform-wallet/tests/e2e/framework/harness.rs, mod.rs
Process-shared E2eContext initialization via OnceCell, SDK/wallet manager/bank/registry/event-hub injection, setup utilities for seed generation and test wallet creation with registry entry insertion.
Bank Wallet & Funding
packages/rs-platform-wallet/tests/e2e/framework/bank.rs
BIP-39 mnemonic-backed bank wallet with minimum-credit validation, DIP-17 address derivation, and concurrent-safe fund transfers using global async mutex to prevent nonce races.
Cleanup & Lifecycle
packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs, wallet_factory.rs
Orphan wallet sweeping at startup with fund drainage and registry status updates, per-test teardown with unregistration, plus test wallet factory with address selection, balance sync, and cleanup guards.
SDK & Event Infrastructure
packages/rs-platform-wallet/tests/e2e/framework/sdk.rs, wait_hub.rs, context_provider.rs
SDK construction with TrustedHttpContextProvider, testnet DAPI endpoint defaults, WaitEventHub for event-driven async test waits, and SpvContextProvider implementing platform SDK context bridge.
Wait & Polling Utilities
packages/rs-platform-wallet/tests/e2e/framework/wait.rs, spv.rs
Generic polling loop with timeout, event-driven balance-change waiter with sync/notification integration, SPV client startup and masternode-list sync readiness checker with progress logging.
E2E Test Cases & Entry Points
packages/rs-platform-wallet/tests/e2e.rs, cases/mod.rs, cases/transfer.rs, README.md, .env.example
Integration test root module, test case organization, fund-and-transfer example test verifying fee deduction, framework documentation with operator setup requirements and architecture overview, and environment configuration template.
Cargo Dependencies
packages/rs-platform-wallet/Cargo.toml
Dev-dependency additions: tokio-shared-rt, tempfile, dotenv, bip39, fs2, parking_lot, simple-signer, SDK context provider, plus tokio-util rt feature.
SDK Public API Exposure
packages/rs-sdk/src/platform/transition.rs, transition/address_inputs.rs
Module visibility change from crate-restricted to public; functions fetch_inputs_with_nonce and nonce_inc exposed for external callers.
Signer Feature & Constructors
packages/simple-signer/Cargo.toml, signer.rs
New derive feature pulling key-wallet and thiserror dependencies; two seed-based constructors for platform-address and identity signers with BIP-32 derivation and error mapping.

Sequence Diagram(s)

sequenceDiagram
    participant Test as E2E Test
    participant Harness as E2eContext Harness
    participant Registry as Wallet Registry
    participant Bank as BankWallet
    participant TWallet as TestWallet
    participant Manager as PlatformWalletManager
    participant SDK as SDK/PlatformWallet
    participant Cleanup as Cleanup

    Test->>Harness: init() first call
    Harness->>Registry: open(test_wallets.json)
    Harness->>Cleanup: sweep_orphans()
    Cleanup->>Registry: list_orphans()
    Cleanup->>Manager: create from orphan seed
    Cleanup->>SDK: sync & drain to bank
    Cleanup->>Registry: remove_orphan_entry
    Harness->>Bank: load from mnemonic
    Harness->>Bank: sync_balances()
    Harness->>Bank: fund_address(test_addr1, credits)
    Harness->>SDK: transfer via bank wallet
    Test->>Test: setup() generates seed
    Test->>Manager: create TestWallet
    Test->>TWallet: create(seed)
    Test->>TWallet: next_unused_address() → addr2
    Test->>Bank: fund_address(addr2, TRANSFER_CREDITS)
    Test->>SDK: transfer via bank
    Test->>TWallet: wait_for_balance(addr2, expected)
    TWallet->>SDK: sync_balances()
    Test->>SDK: transfer(addr2 → addr1, TRANSFER_CREDITS)
    SDK->>SDK: execute, compute fee
    Test->>TWallet: verify balances & fee
    Test->>Test: teardown()
    Test->>Cleanup: teardown_one(test_wallet)
    Cleanup->>TWallet: drain all addresses to bank
    Cleanup->>Registry: remove_entry
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes


🐰 Hopping through the test warren,
New E2E frames make wallets soar-in',
Bank funds and registry keep,
While cleanup sweeps run deep,
SDK paths now public—hooray, no more hide-n'! 🌙✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title references the main e2e test framework addition and identifies Found-025 as a specific fix, but obscures the primary production bug fix (auto_select_inputs trimming) that should be the headline given the PR objectives. Consider revising the title to highlight the production fix more clearly, e.g., 'fix(auto_select_inputs): trim consumed amount + e2e suite' or similar, since the bug fix is the only breaking change and a critical production correction.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rs-platform-wallet-e2e

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lklimek lklimek changed the title feat(rs-platform-wallet): integration test framework + first transfer test test(platform-wallet): integration test framework + first transfer test Apr 27, 2026
@lklimek lklimek requested a review from Copilot April 27, 2026 14:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an end-to-end (wallet → SDK → broadcast) integration test harness to rs-platform-wallet and introduces the first live test case (address-funds transfer), alongside a production fix to InputSelection::Auto input selection so generated transitions satisfy protocol structure rules.

Changes:

  • Added a reusable E2E framework under packages/rs-platform-wallet/tests/e2e/ (workdir slot locking, bank wallet, persistent registry, cleanup/sweep, wait hub, signer, SDK wiring).
  • Added the first E2E test case: transferring credits between two platform-payment addresses in a test wallet (ignored by default).
  • Fixed auto_select_inputs in production code to avoid selecting full balances as “input credits”, and added unit tests for the selection logic.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/rs-platform-wallet/src/wallet/platform_addresses/transfer.rs Fixes auto input selection; adds pure helper + unit tests for selection behavior.
packages/rs-platform-wallet/tests/e2e.rs Adds the integration test crate root and module wiring for the e2e suite.
packages/rs-platform-wallet/tests/e2e/README.md Operator/setup documentation for running live e2e tests.
packages/rs-platform-wallet/tests/e2e/cases/mod.rs Declares e2e test modules.
packages/rs-platform-wallet/tests/e2e/cases/transfer.rs First e2e test exercising funding + self-transfer + teardown.
packages/rs-platform-wallet/tests/e2e/framework/mod.rs Framework public surface (setup, errors, prelude) and module layout.
packages/rs-platform-wallet/tests/e2e/framework/harness.rs E2eContext singleton init: config, workdir locking, SDK, manager, bank, registry, startup sweep.
packages/rs-platform-wallet/tests/e2e/framework/config.rs Env/.env configuration loader for the harness.
packages/rs-platform-wallet/tests/e2e/framework/sdk.rs Constructs dash_sdk::Sdk with TrustedHttpContextProvider and DAPI address resolution.
packages/rs-platform-wallet/tests/e2e/framework/workdir.rs Cross-process workdir slot selection via flock.
packages/rs-platform-wallet/tests/e2e/framework/panic_hook.rs Installs panic hook to cancel background work on panic.
packages/rs-platform-wallet/tests/e2e/framework/wait_hub.rs Notify-based hub bridging wallet/SPV/platform events to async waiters.
packages/rs-platform-wallet/tests/e2e/framework/wait.rs Async waiting helpers (event-driven balance wait + generic polling).
packages/rs-platform-wallet/tests/e2e/framework/signer.rs Seed-backed Signer<PlatformAddress> with eager DIP-17 key cache.
packages/rs-platform-wallet/tests/e2e/framework/wallet_factory.rs Test wallet factory + SetupGuard (panic-safe registry-backed lifecycle).
packages/rs-platform-wallet/tests/e2e/framework/registry.rs JSON-backed persistent registry for panic-safe orphan recovery.
packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs Startup sweep + per-test teardown draining funds back to bank.
packages/rs-platform-wallet/tests/e2e/framework/bank.rs Loads and syncs a pre-funded bank wallet; serialized funding API.
packages/rs-platform-wallet/tests/e2e/framework/context_provider.rs Retained (disabled) SPV-backed SDK context provider module for future re-enable.
packages/rs-platform-wallet/tests/e2e/framework/spv.rs Retained (disabled) SPV startup/readiness helpers for future re-enable.
packages/rs-platform-wallet/Cargo.toml Adds dev-dependencies needed by the e2e harness.
Cargo.lock Locks new/updated dependencies for the added test tooling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
Comment thread packages/rs-platform-wallet/tests/e2e.rs Outdated
Comment thread packages/rs-platform-wallet/src/wallet/platform_addresses/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/registry.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/registry.rs Outdated
@lklimek lklimek changed the base branch from v3.1-dev to fix/rs-platform-wallet-auto-select-inputs April 28, 2026 07:17
Comment thread packages/rs-platform-wallet/tests/e2e/cases/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/cases/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/cases/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/cases/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/bank.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/panic_hook.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/sdk.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/wait.rs
Comment thread packages/rs-platform-wallet/tests/e2e/framework/wallet_factory.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/wallet_factory.rs
Comment thread packages/rs-platform-wallet/tests/e2e/framework/signer.rs Outdated
@lklimek
Copy link
Copy Markdown
Contributor

lklimek commented Apr 30, 2026

@coderabbitai review all

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

@lklimek Sure! I'll kick off a full review of all the changes in this PR.


🧠 Learnings used
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 3564
File: packages/rs-platform-wallet/src/manager/wallet_lifecycle.rs:300-317
Timestamp: 2026-04-29T20:49:09.968Z
Learning: In `packages/rs-platform-wallet/src/manager/wallet_lifecycle.rs`, the `platform_wallet.identity().sync().await` call inside `register_wallet` is intentionally blocking (not detached to a background task). This is a UX design choice: for recovery flows, identity data must be hydrated before the wallet detail view renders to avoid showing an empty identity list that fills in seconds later. The sync is bounded by Platform query timeouts (~10s worst case) and failures are non-fatal (caught + logged with `tracing::warn`). Do not flag this as a blocking-call issue.

Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 3564
File: packages/rs-platform-wallet/src/changeset/core_bridge.rs:105-109
Timestamp: 2026-04-29T20:49:08.152Z
Learning: In `packages/rs-platform-wallet/src/changeset/core_bridge.rs`, the `RecvError::Lagged` arm in `spawn_wallet_event_adapter` intentionally only logs a warning and continues. A full wallet reconciliation/resync cannot be triggered here because the upstream `key_wallet_manager`/`dashcore` crate does not yet expose a rescan entry point. This is known technical debt to revisit when rust-dashcore adds a rescan API.
✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

♻️ Duplicate comments (1)
packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs (1)

57-75: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep sub-threshold wallets recoverable.

If 0 < total <= SWEEP_DUST_THRESHOLD, both cleanup paths skip sweep_platform_addresses and still delete the registry entry. That permanently abandons the remaining credits and will slowly drain the shared bank across repeated runs. Either sweep every positive balance with ReduceOutput(0) or only remove the entry once the wallet is actually empty.

Also applies to: 109-121, 145-163

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs` around lines 57 -
75, The cleanup currently deletes registry entries even when 0 < total <=
SWEEP_DUST_THRESHOLD, abandoning recoverable credits; update the logic in the
sweep_one match branches (the block that calls registry.remove and
registry.set_status) to: if the wallet balance is > 0 but <=
SWEEP_DUST_THRESHOLD, call sweep_platform_addresses with ReduceOutput(0) (or
otherwise perform a full sweep for any positive balance) and only call
registry.remove when the wallet is actually empty; ensure failed-path still sets
EntryStatus::Failed when sweep fails and that successful-path only increments
swept and removes the registry entry when the post-sweep balance is zero
(reference symbols: sweep_one, sweep_platform_addresses, SWEEP_DUST_THRESHOLD,
ReduceOutput(0), registry.remove, registry.set_status, EntryStatus::Failed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/rs-platform-wallet/tests/e2e/cases/transfer.rs`:
- Line 31: Rename the test function transfer_between_two_platform_addresses to
follow the convention by renaming it to
should_transfer_between_two_platform_addresses; update the async fn declaration
(and any internal references or usages of
transfer_between_two_platform_addresses) to the new name so the test name begins
with "should" while keeping the function body and attributes unchanged.
- Around line 51-79: This test performs real network calls via
s.ctx.bank().fund_address and s.test_wallet.transfer / wait_for_balance; change
it to comply with the "no network in unit/integration tests" rule by either (A)
moving this file/case to an e2e-only suite (so it runs under an e2e test runner)
or (B) refactoring to inject mocked implementations for the bank client and
wallet observer used by wait_for_balance and transfer (replace s.ctx.bank() and
any network-dependent wait_for_balance calls with test doubles that simulate
funding/transfer and observable balance updates); update references to
next_unused_address, transfer, and wait_for_balance to use the mocks or the
e2e-only harness accordingly.

In `@packages/rs-platform-wallet/tests/e2e/framework/config.rs`:
- Around line 34-50: Config currently derives Debug and will print sensitive
bank_mnemonic; replace the automatic derive with a manual impl Debug for Config
that omits or redacts bank_mnemonic (e.g., display "REDACTED" or hide its value)
and prints the other fields normally; implement Debug in the same module
referencing the struct name Config and its fields (bank_mnemonic, network,
dapi_addresses, min_bank_credits, workdir_base, trusted_context_url) so future
secret fields can also be redacted consistently.

In `@packages/rs-platform-wallet/tests/e2e/framework/registry.rs`:
- Around line 225-259: Rename the three test functions to follow the "should …"
naming convention: change missing_file_opens_empty to a descriptive name like
should_open_empty_if_file_missing, change insert_remove_round_trip_persists to
should_persist_insert_remove_round_trip, and change
corrupt_file_falls_back_to_empty to should_fall_back_to_empty_on_corrupt_file;
update the fn identifiers in
packages/rs-platform-wallet/tests/e2e/framework/registry.rs (the tests currently
named missing_file_opens_empty, insert_remove_round_trip_persists,
corrupt_file_falls_back_to_empty) and run cargo test to ensure no references
break.

In `@packages/rs-platform-wallet/tests/e2e/framework/wallet_factory.rs`:
- Around line 291-293: Rename the test function
default_spec_matches_pinned_constants to follow the repository "should …"
convention (e.g., should_default_spec_match_pinned_constants or
should_match_pinned_constants_by_default) so the test name starts with "should";
update the function declaration fn default_spec_matches_pinned_constants() to
the new name and keep the body (including PlatformPaymentAccountSpec::default())
unchanged so references and assertions remain valid.

In `@packages/rs-platform-wallet/tests/e2e/framework/workdir.rs`:
- Line 92: Rename the test function
first_call_takes_slot_zero_second_falls_through to follow the required "should
..." convention (for example
should_first_call_take_slot_zero_and_second_fall_through); update the function
identifier wherever referenced (the test declaration itself and any uses in
attributes or calls) so the Rust test name begins with "should_" and keep the
original behavior and test annotation (e.g., #[test]) unchanged.
- Around line 50-61: The current error handling in the lock acquisition loop
treats every Err(err) as a busy slot; update the branch in the function that
opens/locks `lock_file` (the block that logs "workdir slot busy, trying next")
to inspect the IO error kind: if the error indicates contention (e.g.,
would-block / ErrorKind::WouldBlock or the platform-specific WouldBlock
equivalent), keep the existing tracing::debug and continue; for any other errors
(permission, other IO), log an error and propagate/return the error instead of
retrying so real failures aren’t swallowed.

In `@packages/rs-platform-wallet/tests/e2e/README.md`:
- Around line 99-106: The fenced code blocks in the e2e README (the blocks
starting with the "Bank wallet under-funded." message and the "SetupGuard
dropped without explicit teardown — wallet <id>" message) lack language tags,
causing MD040 lint failures; update those fenced blocks to include a language
specifier (e.g., change ``` to ```text) for both occurrences (the block
containing "Bank wallet under-funded." and the later block containing
"SetupGuard dropped without explicit teardown") so the markdown linter accepts
them.
- Around line 233-235: Update the stale troubleshooting example to match the
current error shape emitted by the pick_available_workdir routine: replace the
quoted `No available workdir slots (tried 0..10)` text with the actual error
text produced by pick_available_workdir (copy exact current message/format), and
note that this occurs when all 10 workdir slots are locked so operators search
logs for the correct string; reference pick_available_workdir in the note so
maintainers can locate the implementation for future changes.

---

Duplicate comments:
In `@packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs`:
- Around line 57-75: The cleanup currently deletes registry entries even when 0
< total <= SWEEP_DUST_THRESHOLD, abandoning recoverable credits; update the
logic in the sweep_one match branches (the block that calls registry.remove and
registry.set_status) to: if the wallet balance is > 0 but <=
SWEEP_DUST_THRESHOLD, call sweep_platform_addresses with ReduceOutput(0) (or
otherwise perform a full sweep for any positive balance) and only call
registry.remove when the wallet is actually empty; ensure failed-path still sets
EntryStatus::Failed when sweep fails and that successful-path only increments
swept and removes the registry entry when the post-sweep balance is zero
(reference symbols: sweep_one, sweep_platform_addresses, SWEEP_DUST_THRESHOLD,
ReduceOutput(0), registry.remove, registry.set_status, EntryStatus::Failed).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0379415c-b6af-4b82-b05c-635af13cb042

📥 Commits

Reviewing files that changed from the base of the PR and between 74b81d1 and ae98ccf.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • packages/rs-platform-wallet/Cargo.toml
  • packages/rs-platform-wallet/tests/.env.example
  • packages/rs-platform-wallet/tests/e2e.rs
  • packages/rs-platform-wallet/tests/e2e/README.md
  • packages/rs-platform-wallet/tests/e2e/cases/mod.rs
  • packages/rs-platform-wallet/tests/e2e/cases/transfer.rs
  • packages/rs-platform-wallet/tests/e2e/framework/bank.rs
  • packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs
  • packages/rs-platform-wallet/tests/e2e/framework/config.rs
  • packages/rs-platform-wallet/tests/e2e/framework/context_provider.rs
  • packages/rs-platform-wallet/tests/e2e/framework/harness.rs
  • packages/rs-platform-wallet/tests/e2e/framework/mod.rs
  • packages/rs-platform-wallet/tests/e2e/framework/registry.rs
  • packages/rs-platform-wallet/tests/e2e/framework/sdk.rs
  • packages/rs-platform-wallet/tests/e2e/framework/spv.rs
  • packages/rs-platform-wallet/tests/e2e/framework/wait.rs
  • packages/rs-platform-wallet/tests/e2e/framework/wait_hub.rs
  • packages/rs-platform-wallet/tests/e2e/framework/wallet_factory.rs
  • packages/rs-platform-wallet/tests/e2e/framework/workdir.rs
  • packages/rs-sdk/src/platform/transition.rs
  • packages/rs-sdk/src/platform/transition/address_inputs.rs
  • packages/simple-signer/Cargo.toml
  • packages/simple-signer/src/signer.rs

Comment thread packages/rs-platform-wallet/tests/e2e/cases/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/cases/pa_002_partial_fund.rs
Comment thread packages/rs-platform-wallet/tests/e2e/framework/config.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/registry.rs
Comment thread packages/rs-platform-wallet/tests/e2e/framework/wallet_factory.rs
Comment thread packages/rs-platform-wallet/tests/e2e/framework/workdir.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/workdir.rs
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
@lklimek lklimek requested a review from Copilot April 30, 2026 06:19
@lklimek lklimek marked this pull request as ready for review April 30, 2026 06:19
@thepastaclaw
Copy link
Copy Markdown
Collaborator

thepastaclaw commented Apr 30, 2026

✅ Review complete (commit 921833f)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated 9 comments.

Comments suppressed due to low confidence (2)

packages/rs-sdk/src/platform/transition/address_inputs.rs:39

  • Now that this helper is public, nonce + 1 can overflow when nonce == u32::MAX, which will panic in debug builds and wrap in release builds. Consider using checked_add(1) and returning an error (or otherwise handling the overflow) so callers can't accidentally produce an invalid/wrapping nonce.
pub fn nonce_inc(
    data: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
) -> BTreeMap<PlatformAddress, (AddressNonce, Credits)> {
    data.into_iter()
        .map(|(address, (nonce, credits))| (address, (nonce + 1, credits)))
        .collect()

packages/rs-sdk/src/platform/transition/address_inputs.rs:18

  • fetch_inputs_with_nonce is now public but has no doc comment explaining (1) that it performs existence/balance checks and (2) that callers typically need to apply nonce_inc before building a transfer (as transfer_address_funds does). Please document the intended call pattern (or provide a single public helper that returns the incremented nonces) to reduce misuse from external callers.
pub async fn fetch_inputs_with_nonce(
    sdk: &Sdk,
    amounts: &BTreeMap<PlatformAddress, Credits>,
) -> Result<BTreeMap<PlatformAddress, (AddressNonce, Credits)>, Error> {
    if amounts.is_empty() {
        return Err(Error::from(TransitionNoInputsError::new()));
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/rs-platform-wallet/tests/e2e/cases/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/registry.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/cases/transfer.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/framework/cleanup.rs Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
Comment thread packages/rs-platform-wallet/tests/e2e/README.md Outdated
@lklimek lklimek added the ready for final review Ready for the final review. If AI was involved in producing this PR, it has already had a reviewer. label Apr 30, 2026
lklimek and others added 2 commits April 30, 2026 09:20
…rant transfer fixture

* `transfer.rs` — funding/transfer fixtures handle `[ReduceOutput(0)]`
  fee deduction via post-fee floors and split-fee assertions
  (bank_fee + transfer_fee), so the test no longer asserts the gross
  amount lands intact. Module doc points at the actual error path
  (`FrameworkError::Bank` for missing mnemonic, panic for under-funded
  bank) per Copilot's `transfer.rs:7` note.
* `config.rs` — replace `derive(Debug)` with a manual impl that
  redacts `bank_mnemonic` so a stray `{config:?}` log or panic
  backtrace can't leak the shared funding seed (CodeRabbit `config.rs:50`).
* `workdir.rs` — match `ErrorKind::WouldBlock` as slot-busy and
  propagate every other IO error as `FrameworkError::Io`, instead of
  swallowing them all as "slot busy" (CodeRabbit `workdir.rs:50`).
* `registry.rs` — drop the never-set `EntryStatus::Sweeping` variant +
  doc references; the per-slot workdir lock already serialises the
  only writer, so no transient cross-process state is required
  (Copilot `registry.rs:35`, `cleanup.rs:75`).
* `cleanup.rs` — replace the hardcoded `SWEEP_DUST_THRESHOLD` constant
  with the protocol's `min_input_amount` from `PlatformVersion`, so
  the sweep gate stays in lock-step with whatever `address_funds`
  validation requires.
* `wait_hub.rs` — fix stale `platform_address_sync` import path; the
  module moved to `manager::platform_address_sync` in PR #3564 and
  is re-exported at the crate root.
* `README.md` — fenced-code-block language tags (MD040), corrected
  workdir-exhausted error string, first-run timing reflects
  `TrustedHttpContextProvider` default (no SPV in critical path),
  troubleshooting note rescoped, teardown step list no longer claims
  to wait for the bank to observe credits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…network-seeds

Replaces the hardcoded `TESTNET_DAPI_ADDRESSES` list in `framework/sdk.rs`
with a `default_address_list_for_network` helper that mirrors PR #3533's
upstream `default_address_list_for_network` byte-for-byte: pulls
`dash_network_seeds::evo_seeds(network)`, filters seeds with a
`platform_http_port`, and constructs DAPI URLs from the seed IPs.

Once PR #3533 (`feat(sdk): source mainnet/testnet bootstrap from
dash-network-seeds`) lands in `v3.1-dev` and exposes
`SdkBuilder::new_testnet()` properly (currently `unimplemented!()` on
this branch's base), the helper collapses into a single
`SdkBuilder::new_testnet()` call with no behavioural delta.

`framework/spv.rs::seed_p2p_peers` follows suit: testnet peer IPs come
from `dash_network_seeds::evo_seeds(Testnet)` when the operator
hasn't supplied an explicit DAPI list. Also drops the dead
`TESTNET_DAPI_ADDRESSES` re-import.

Adds `dash-network-seeds` as a dev-dependency, pinned to the same
rust-dashcore rev as the workspace `dashcore` to keep all sibling
crates in lock-step.

Resolves the `sdk.rs:41` review thread.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
lklimek and others added 2 commits April 30, 2026 10:09
…3040

Bumps `FUNDING_CREDITS` 50M -> 100M and `TRANSFER_CREDITS` 10M -> 50M
(plus matching floors) so `output[0]` comfortably exceeds Drive's
chain-time fee. Issue #3040 (`calculate_min_required_fee` is too low)
causes `[ReduceOutput(0)]` selections with small `output[0]` to fail
at chain time despite passing the static-fee check. Picking output
amounts well above the empirical chain-time ceiling sidesteps the
bug until the dpp-layer fix lands.

Bumps `DEFAULT_MIN_BANK_CREDITS` 100M -> 500M to keep the bank
covering several runs at the larger per-run cost (also follows DET's
5x safety-factor pattern from dash-evo-tool#513).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
lklimek and others added 2 commits May 15, 2026 11:55
… Found-025 sync-discard race

TK-001 and TK-014 failed in the v53 14-thread run, both timing out in the
SETUP FUNDING gate before any token logic ran (tk_001_token_transfer.rs:67
setup_with_token_and_two_identities; tk_014_token_group_action.rs:109
setup_with_per_identity_funding). In both, bank.fund_address chain-confirmed
the funding (nonce streak 2/2) before the wait, then the rs-sdk address-sync
silently discarded the fetched balance update because the target address was
not yet in pending_addresses — Found-025, amplified by 14-thread concurrency.
Not production defects: transfer/group-action/co-sign code never executed and
siblings (TK-001b/c, TK-009/010/012) were green in the same run.

Root cause in the shared chokepoint framework/mod.rs::setup_with_per_identity_funding:
it gated on wait_for_balance, whose proof-verified hand-off only runs AFTER
the Found-025-poisoned local sync map (balances().get(addr)) first reaches
target — so under Found-025 the proof gate was never reached and the budget
expired in the local-view branch (60-62 polls, no chain-confirmed line).

Fix: observe funding directly via the proof-verified AddressInfo::fetch path
(wait_for_address_balance_chain_confirmed_n, CHAIN_CONFIRMED_CONSECUTIVE_SUCCESSES)
— the same chain-state read the validator walks and the family PA-009c
adopted — bypassing the poisoned map entirely. The existing strong
wait_for_address_known_to_platform gate is unchanged. Only the
funding-observation mechanism changed: no funding amounts, identity counts,
contract publish, propose/co-sign, or token/identity assertions altered.

Deterministic and concurrency-independent, so it hardens the whole
setup-helper blast radius (all 22 TK-*/ID-*/CR-003/DPNS-001 cases routing
through setup_with_per_identity_funding). No new Found-NNN pin and no
upstream issue (Found-025 already owns the root cause). A TK-wave
serialization / worker-pool cap remains a documented fallback only — not
implemented, since the proof-verified read-back structurally bypasses the
poisoned map.

TEST_SPEC.md: TK-001 (quick-index + body) and TK-014 (quick-index + body)
reclassified green -> red-real-fail mirroring TK-007 wording, cross-linked
to Found-025; one changelog entry added. All three references per test are
mutually consistent (no stale green/PASS-in-v47 drift).

Live e2e requires a bank-funded node (yarn start) unavailable in this
environment; verified by inspection + cargo build --tests + cargo clippy
(both clean). Live re-validation deferred to the combined v54 run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ound-008 env-mask

FIX 1 (#475): the e2e README documented the opt-in invocation with
`--ignored`, which runs ONLY the `#[ignore]`-attributed subset (~40/108)
and silently skips owned-fix cases. Corrected to `--include-ignored` so the
full suite runs, with a one-line note explaining why `--ignored` alone is wrong.

FIX 2 (#474, CLAUDE.md infra-blocker rule): added a `// TODO(env):` marker in
al_001 near the Core-funded setup gate and a brief note in TEST_SPEC.md's
AL-001/Found-008 entry recording that the Found-008 pin is env-masked when the
e2e testnet Core L1 bank is depleted (al_001 dies at the setup gate, not at the
designed FinalityTimeout; same depletion also fails cr_003 + id_002b). Funding
address phrased generically — the specific address is being verified separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…of-band)

User will top up the e2e Core L1 bank wallet directly, so the env-mask
documentation is unnecessary. Removes the FIX 2 content added in the previous
commit: the `// TODO(env):` marker in al_001 near the Core-funded setup gate
and the AL-001/Found-008 "pin coverage degraded under Core-bank depletion"
bullet in TEST_SPEC.md. Net effect of this commit pair is README-only — the
`--ignored` -> `--include-ignored` run-flag correction (#475).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Claudius-Maginificent Claudius-Maginificent changed the title test(platform-wallet): e2e framework + 60-test suite, 5 upstream bug pins test(platform-wallet): e2e framework + scenario suite, upstream bug pins + setup-gate hardening (v54: 98 PASS/10 FAIL) May 15, 2026
…ter store-error wallet loss)

register_wallet logs and swallows the registration-round persister
`store` error (manager/wallet_lifecycle.rs:276-282) then inserts the
wallet into self.wallets unconditionally (wallet_lifecycle.rs:347-349).
A successful-looking import leaves no persisted record and vanishes on
the next launch — HIGH-severity silent data loss. Note the asymmetry:
the load_persisted / initialize_from_persisted failure paths in the same
function already roll back and return Err; the registration store does
not.

Deterministic pin (no live network, no concurrency): injects a
StoreFailsPersister whose `store` returns Err while `load`/`flush`
succeed (so the already-correct load_persisted rollback path does not
mask the defect), drives create_wallet_from_seed_bytes through a
mock-SDK manager, and asserts the correct atomic-failure contract — the
call returns Err AND the wallet is absent from wallet_ids(). Fails today
for the real reason; flips green once the registration store is treated
as load-bearing. #[ignore]d — live run deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lklimek and others added 6 commits May 15, 2026 15:24
… for post-snapshot addresses (Found-025)

`incremental_catch_up` built its `key_to_tag` lookup once from a single
pre-RPC `provider.pending_addresses()` snapshot and passed it by
immutable reference into both apply loops. The `if let Some(..) =
address_lookup.get(..)` predicate had no `else`, so any balance change
the platform returned for an address derived *after* the snapshot was
dropped with no log, metric, or error — `result.found` never got it and
`on_address_found` was never called. Under concurrent multi-identity
funding the derive-fund-sync interleave is routine, which is why e2e
gates TK-001/007/013/014 and id_005 flaked here.

Extract the two inline apply loops into a pure `pub(crate)
apply_address_changes` seam (no Sdk, no network, no async) that returns
applied updates plus the addresses absent from the snapshot. The new
`apply_block_changes` re-polls `pending_addresses()` when an unknown
address appears (mirroring the tree-scan refresh) and replays only the
previously-unknown subset, so a fresh receive address is recovered and
known-address `AddToCredits` deltas are never double-counted. An address
still unknown after the refresh is logged at `warn` — observable, never
silently dropped. Known-address behavior is byte-for-byte identical.

Adds three deterministic `#[cfg(test)]` regression guards on the pure
seam (no proof/Sdk needed): unknown-address surfacing, post-snapshot
recovery through the refresh, and delta double-count safety. All three
fail on the pre-fix silent-discard logic and pass post-fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit 35925f8)
(cherry picked from commit 5c590341f65c89221e043ef89720fa948250e187)
…ex 0

`primary_core_receive_address` routed through
`CoreWallet::next_receive_address_for_account(0)`, whose pool
advances its "next unused" cursor off index 0 as soon as a UTXO
lands there — so the operator-funded Core top-up address drifted
run-to-run, stranding duffs on stale empty addresses.

Mirror the existing `derive_platform_address_at_index` pattern with
`derive_core_receive_address_at_index`: derive a deterministic
`m/44'/coin'/0'/0/0` address via the live wallet's
`derive_public_key`, reconstructing the P2PKH address exactly as
key-wallet's own address pool does. The under-funded preflight now
reports this same stable address; `CORE_TX_FEE_RESERVE` semantics
and the under-funded arithmetic are unchanged.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

(cherry picked from commit 07f160c1d09eebfed320dbd0408195ee7bd5ea19)
…down with preflight

The Core-bank auto-refill defaults were 3-4 orders of magnitude too low to
keep the bank funded. A FULL e2e pass burns ~13 tDASH ≈ 1.3e9 duffs
(1 DASH = 1e8 duffs); the old defaults were 100_000 / 1_000_000 duffs
(0.001 / 0.01 DASH) — not even 0.1% of one pass.

Re-anchor sizing on the measured per-pass burn (CORE_BURN_PER_FULL_PASS_DUFF
= 1_300_000_000):
- threshold: 2_000_000_000 duffs (~20 tDASH) — one full pass + ~0.5-pass
  margin so the bank is topped up before it can starve mid-pass.
- target: 5_000_000_000 duffs (~50 tDASH ≈ 3.8 passes) — one slow
  Platform→Core withdrawal buys several passes of runway.

Run the refill on BOTH ends of the lifecycle:
- setup: existing call (harness.rs, unchanged) fires first.
- teardown: new call in SetupGuard::teardown after the sweep returns this
  test's funds to the bank — the cheapest point to refill for the next
  pass. Below-threshold-guarded inside the helper, so it's a no-op when
  already funded; best-effort, never fails a teardown.

Add a setup preflight (assert_core_funded_for_one_pass): after the refill
attempt, if confirmed Core is still below one full pass
(CORE_REFILL_OPERATIONAL_MIN_DUFF), fail fast with a FrameworkError::Bank
naming the fixed index-0 top-up address and the exact shortfall — mirrors
the existing send_core_to under-funded error shape — instead of silently
entering a doomed run.

Unit test pins the defaults to the measured burn (threshold ≥1 pass,
target ≥3 passes, preflight floor == 1 pass).

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

(cherry picked from commit 6662725c4c95a8866eb62014fc070ca23ca38310)
…nfirmed (Found-025)

The funding-gate `wait_for_balance` in the identity/address state-transition
tests checks the wallet's local sync map before handing off to the
proof-verified chain gate. Under multi-thread churn the rs-sdk address-sync
silently drops a fetched balance update when the address isn't yet in
`pending_addresses` (Found-025), poisoning that local map so the precondition
never reaches target and the proof-verified hand-off never runs — the gate
times out before the immediately-following broadcast.

Swap the 9 funding-then-broadcast gates (register_identity_from_addresses /
top_up_from_addresses inputs) to `wait_for_address_balance_chain_confirmed_n`
with `CHAIN_CONFIRMED_CONSECUTIVE_SUCCESSES`, mirroring the
`setup_with_per_identity_funding` precedent exactly. Post-broadcast
`wait_for_balance` sites whose assertion subject IS the wallet's local
`.balances()` view (id_005 dest readback, all PA self-transfer tests) are
left untouched — swapping them would un-sync the local map those assertions
depend on.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

(cherry picked from commit bf4779e288ed2b22f52680b22543f57fc8690848)
…unmask

QA-501/502 (id_005:127, id_002:117): Found-025 fix unmasked a downstream
Found-026-family production cursor race — next_unused_address() returns a
DUPLICATE under 14-thread churn. RED-by-design pins + TEST_SPEC reclass
(green → red-by-design concurrency-only). No production fix; assertions
stay genuinely RED for the real reason. Same root component + concurrency
trigger as Found-026 (PA-008b); distinct observable mechanism
(duplicate-derivation vs enqueue-miss) — linked to Found-026 family, no
new Found-NNN (#496 holds filing; Found-026 still suspected).

QA-503 (id_sweep:167): HARNESS test-defect, minimal fix. The secondary
bank-identity post<=pre invariant is structurally unobservable under
concurrent harness bank_rebalance core-refill (which by design tops up
the bank identity; growth delta exactly matches topup_credits). Sweep
correctness already pinned by the race-immune swept_identity_credits
assertion — same flaw class QA-V39-001 fixed for the primary check.
Removed the unobservable invariant (not green-paint: no real check
weakened, no production source touched).

QA-504 (pa_006b:83): Found-025-family known-fail under documented
multi-thread conditions; un-swapped wait_for_balance reads the poisoned
local map (#480 intentional non-swap). TEST_SPEC status corrected
(green → red-real-fail multi-thread only). Flagged a swap-scope
recommendation (swap ONLY the funding gate) — NOT applied (out of code
scope).

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

(cherry picked from commit e53d543a8f24e3ff3dbb917fd122f71322d52e67)
… chain-confirmed (QA-504)

Corrects a #480 mis-scoping: pa_006b:81 is a funding PRECONDITION
gate, not a post-broadcast `.balances()` assertion, so the #480
local-map rationale never applied. Swapped ONLY :81 to
`wait_for_address_balance_chain_confirmed_n` (same pattern / arg-order
/ CHAIN_CONFIRMED_CONSECUTIVE_SUCCESSES as the #480 funding-gate
swaps) — resolves the v-run's documented deterministic 14-thread
funding-gate timeout. Post-broadcast `wait_for_balance(&addr_dst)` at
:170 stays correctly un-swapped per #480.

TEST_SPEC PA-006b reclassified `red-real-fail` → `partially-fixed
(QA-504)`: the documented failure is fixed, but a clean multi-thread
pass is NOT claimed — :170 retains residual Found-025-family
exposure and no live re-run was possible (no bank-funded node).
Honest, not green-washed.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

(cherry picked from commit 92881fde6cce24ccc47123ef7982e5b961682184)
@lklimek lklimek changed the title test(platform-wallet): e2e framework + scenario suite, upstream bug pins + setup-gate hardening (v54: 98 PASS/10 FAIL) test(rs-platform-wallet): e2e suite, Found-025 fix + triage pins May 15, 2026
lklimek and others added 3 commits May 15, 2026 17:15
…-scoping across PA-* (QA-001/#508)

#480 swapped 9 identity funding gates to wait_for_address_balance_chain_confirmed_n
but excluded ALL PA-* tests wholesale. That was wrong for funding-PRECONDITION gates:
a wait_for_balance that gates a funding/source address BEFORE a broadcast that
consumes it is the Found-025 poisoned-local-map hazard (stale 0 → 60s timeout).
pa_006b:81 was instance #1 (already fixed). #506 found pa_001b:66 as instance #2
(PASS→FAIL regression). This pass systematically classifies EVERY wait_for_balance
site in cases/ and swaps the 11 remaining funding-precondition gates:

  pa_001:103, pa_001b:64, pa_001b:139, pa_001c:57, pa_002:111, pa_002b:64,
  pa_003:138, pa_004:96, pa_004b:125, pa_006:75, pa_009:171

Post-broadcast / local-view waits whose balance IS the assertion subject, and
RED-by-design pins (pa_3040, found_*, id_005, PA-005/007/008* concurrency-
observability), stay correctly un-swapped per the legitimate #480 KEEP rationale.
Every timeout / expected / expect-message preserved; no assertion weakened.

Also fixes the PA-001b TEST_SPEC internal inconsistency (index row said `blocked`
while the detail body claimed `PASS`): both now consistently and honestly state
`precondition-fixed` — subcase_a no longer Found-025-times-out, but the
deliberately-kept post-broadcast waits at :95/:223 retain residual Found-025-
family multi-thread exposure (same posture as PA-006b:170); not an unproven
clean multi-thread pass.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

(cherry picked from commit 2abd21182a4d154f247f007a406f4a138688a595)
…e refs (PROJ-001)

Independent verify found the PA-001b index row (~:181) and detail body
(~:488) cited drifted source line numbers. Corrected against the actual
current pa_001b_change_address_branch.rs after the #508 swap-block
insertion shifted lines down:

  subcase_a funding-precondition gate (now `_n`): :64  -> :70
  subcase_b funding-precondition gate (now `_n`): :139 -> :154
  subcase_a kept post-broadcast wait (addr_2):    :95  -> :107
  subcase_b kept post-broadcast wait (change_addr)::223 -> :244

`:95` had drifted onto the `transfer_with_change_address(` call; `:223`
had likewise drifted off the kept change_addr wait (true line :244).
Line-number refs only — every semantic claim is verbatim unchanged.
Doc-only; no code touched.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

(cherry picked from commit 54ec95baf3271ac7d7543a7926dd018a67512727)
…ound-026-family RED (proven 14-thread v-run)

Index + body Status for PA-001/PA-002/PA-003 said green/IMPLEMENTED-passing
while a funded canonical 14-thread v-run at e83a43c shows them panicking
deterministically on assert_ne! duplicate-address — the same Found-026-family
next_unused_address pool-cursor race already pinned by ID-002/ID-005. Mirrors
the existing ID-002/ID-005 red-by-design (concurrency-only) classification,
adds the trio to the Found-026 detail family-members list, and removes the
index/body inconsistency the V28-303 concession already conceded. Doc-only;
no cases/*.rs, assertions, or test behaviour touched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lklimek
Copy link
Copy Markdown
Contributor

lklimek commented May 18, 2026

E2E test-results record — consolidated (authoritative)

Branch: feat/rs-platform-wallet-e2e @ landed HEAD 76baafda3f (origin, PR #3549)
Command: cargo test --test e2e -- --include-ignored --nocapture --test-threads=14
Post-landing reality: Found-026 + Found-017 FIXED in landed code; the previously-masked sites are now green.

This is the single authoritative test-results record. Earlier per-version run comments on this PR are superseded and point here. Do not create new results comments — update this one.

CI reading guidance

The expected-red set SHRANK with the Found-026/QA-001/gate-fix/Found-017/reclass chain (9e24f1f343..76baafda3f). PA-001/002/003, id_002/id_005, QA-501/QA-502 and found_017 are no longer red — they are FIXED and green. Any remaining non-green maps strictly to an upstream RED-by-design pin or a tracked ticket. Zero unexplained reds, zero new regressions.

Now GREEN (was red/masked, fixed by this chain)

Test(s) Was Now Fixed by
pa_001, pa_002, pa_003 Found-026-family duplicate-address race (RED-by-design pin) GREEN — focused 3/3 14-thread re-run + R2-clean 0618e6eba8 Found-026 + cba5125a29 QA-001 + 00437dbeed gate-fix
id_002, id_005 masked by same race + stale-local-cache gate GREEN — chain-confirmed-gate + sync_balances 00437dbeed gate-fix
found_017 RED-by-design pin (red-by-design) GREEN by correctness — passes as regression 0e547b02fe Found-017 fail-closed
pa_005 Invariant-1 assert_eq! (wrong) GREEN — re-spec'd to assert_ne! per adjudication 76baafda3f reclass

Remaining expected non-green — upstream / tracked only

Test(s) Class Why (tracked)
found_006, found_021, found_022 RED-by-design upstream pins rust-dashcore #762 / #763 / #764 — the failure IS the proof
pa_3040 Bug pin platform #3040 still present (by design)
al_001 Bug pin Found-008 / tracked as #3641

Found-025 WARN seam — expected, not a regression

The dash_sdk::platform::address_sync … (Found-025) WARN lines remain the Found-025 fix's observability seam working as designed — silent-discard replaced with WARN + re-poll. Intentional diagnostic signal, not a regression, expected-present.

Campaign fix-set — landed on feat HEAD 76baafda3f

Item Commit State
#516 dead event_manager() removal + Lagged-stall (+ carried Found-025, #480/#508) 9e24f1f343 prior base
Found-026 reserve platform receive address on hand-out 0618e6eba8 FIXED
QA-001 align slot-0 consumption w/ native reserve-on-hand-out cba5125a29 FIXED
chain-confirmed-gate vs stale-local-cache (5 cases sync_balances) 00437dbeed FIXED
Found-017 fail-closed register-wallet persist error 0e547b02fe FIXED
reclass: Found-026/017 FIXED, PA-005 Inv-1 assert_ne!, pa_001/002/003/id_002/id_005 re-attributed, found_017 pin→regression 76baafda3f FIXED (HEAD)

Validation evidence (no green-paint)

Found-026 + Found-017 proven via a focused 3/3 14-thread re-run on the affected sites + a fully-clean full-suite R2 + architecture adjudication of the PA-005 Invariant-1 re-spec. The masked PA-001/002/003 + id_002/id_005 went genuinely green once the duplicate-address race was eliminated at the production layer (0618e6eba8). Integration was Adams-verified scope-clean (byte-faithful ff 9e24f1f343..76baafda3f, no stray production files).

Conclusion

Found-026 and Found-017 are FIXED in landed code, not pinned. PA-001/002/003 + id_002/id_005 + found_017 + pa_005 are green. Remaining expected non-green = found_006/found_021/found_022 (rust-dashcore #762/#763/#764), pa_3040 (#3040), al_001 (#3641) — all upstream/tracked. Found-025 WARN seam expected-present. Zero unexplained reds, zero new regressions; campaign fix-set landed. v3.1-dev backports tracked as separate sibling PRs (semantic re-apply — regions diverged).


🤖 Generated with Claude Code

lklimek and others added 7 commits May 18, 2026 10:23
…structor-inject handler set

Replace PlatformEventManager's ArcSwap+add_handler with an immutable
Arc<[handler]> set fixed at construction, matching dash-spv's own
immutable event_handlers; PlatformWalletManager::new now takes a
Vec<app_handlers>. The e2e mn-list ManagerError observer is built up
front and threaded in via that vec (broadcast-subscribed per wait)
instead of late-registered through the deleted accessor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…signal (QA-516-001/002)

dash-spv emits one Masternode ManagerError per failed inbound message,
so a persistent stall bursts past the 16-slot ring; the recv match now
returns the stall FrameworkError on RecvError::Lagged instead of
silently dropping it, restoring the O(ms) fast-fail. RecvError::Closed
is handled explicitly. Comments corrected to the per-failed-message
emission model.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (Found-026)

key-wallet AddressPool::next_unused returns the first address whose `used == false`; for platform-payment addresses `used` only flips on a positive synced balance, so under thread churn a concurrent next_unused_receive_address re-handed the same unsynced index (duplicate address, collision, balance stuck 0).
Now select-and-reserve atomically inside the already-held wallet_manager.write() guard: next_unused_with_info + mark_index_used(info.index); a later real sync hit on the reserved index is an idempotent no-op so highest_used/gap-limit accounting isn't double-counted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erve-on-hand-out (QA-001)

The reserve-on-hand-out contract is now native in next_unused_receive_address.
Harness manual mark retired; slot-0 + pa_001b distinct-address assumers updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…funding gate before consume (post-#508)

#508's chain-only funding gate is sdk-only and never warms the wallet's
local balance map; add sync_balances() in 6 sites mirroring pa_001b. Test-only, no production change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ound-017)

register_wallet swallowed the load-bearing store() Err then committed+returned Ok
-> silent wallet loss on restart; now rolls back the in-memory insert and propagates
the error, mirroring the load_persisted rollback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…A-005 Invariant-1 + correct chain-gate attribution

Found-026/QA-001/gate-fix/Found-017 proven (3/3 focused + R2-clean + Nagatha-adjudicated): the reserve-on-hand-out fix and fail-closed registration are genuinely correct.
PA-005 Invariant-1 re-specified assert_eq->assert_ne per adjudication (A) — back-to-back next_unused_address now reserves on hand-out and returns pairwise-distinct addresses; pa_001/002/003/id_002/id_005 re-attributed from the now-fixed Found-026 next_unused race to the chain-confirmed-gate-vs-stale-local-map test-sequencing defect fixed by the gate-fix.
found_017 pin reclassed red-by-design -> passing-as-regression (pin file byte-untouched; positive companion guards the success path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ccess evidence

wait_for_core_balance / bank Core funding gate 300/900->180s (worst cold
success ~100s, ~1.8x margin); over-sized ceilings only padded the FAIL
path. mn-list cold floor, tk_013, tk_002, al_001 deliberately untouched
(env / protocol-epoch / RED-by-design); FUNDING_TX_CONFIRMATION_TIMEOUT
left at 120s — in-source 75s contention evidence exceeds a 60s cap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants