Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
47bf919
fix(rs-platform-wallet/e2e): bank-identity bootstrap recovers from on…
lklimek May 6, 2026
66ed769
fix(rs-platform-wallet/e2e): use HIGH key for token contract deploy (…
lklimek May 6, 2026
e235766
fix(rs-platform-wallet/e2e): tradeMode in pre-programmed and group-ga…
lklimek May 6, 2026
9d89d91
fix(rs-platform-wallet/e2e): wait for chain-confirmed balance before …
lklimek May 6, 2026
8f06378
fix(rs-platform-wallet/e2e): wait for on-chain identity balance to ma…
lklimek May 6, 2026
c379265
fix(rs-platform-wallet/e2e): require N consecutive chain-confirmed ob…
lklimek May 6, 2026
89fc25b
fix(rs-platform-wallet/e2e): size DPNS-001 funding to cover dynamic-f…
lklimek May 6, 2026
2c8ebc8
fix(rs-platform-wallet/e2e): bump token-test identity funding to clea…
lklimek May 6, 2026
27245cb
fix(rs-platform-wallet/e2e): bump TK-013/14 case-local FUNDING to cle…
lklimek May 6, 2026
b79ea20
feat(rs-platform-wallet/e2e): add wait_for_data_contract_visible help…
lklimek May 6, 2026
6857c8d
fix(rs-platform-wallet/e2e): bump TK-013/14 distribution time offset …
lklimek May 6, 2026
45380d9
fix(rs-platform-wallet/e2e): add CRITICAL key to test identities and …
lklimek May 6, 2026
74ee243
fix(rs-platform-wallet/e2e): enable dpns-contract feature on context-…
lklimek May 6, 2026
262e94a
feat(rs-platform-wallet/e2e): add Platform-view wait helpers for addr…
lklimek May 6, 2026
ae966c6
fix(rs-platform-wallet/e2e): restore QA-803 dpns-contract feature on …
lklimek May 6, 2026
e2a5d7e
fix(rs-platform-wallet/e2e): wire Platform-view waits into setup_with…
lklimek May 6, 2026
a5d0138
fix(rs-platform-wallet/e2e): rewire wait_for_data_contract_visible + …
lklimek May 6, 2026
f1347c8
fix(rs-platform-wallet/e2e): id_001 expects 4 keys after CRITICAL add…
lklimek May 6, 2026
8509a63
fix(rs-platform-wallet/e2e): wait for wallet sync after transfer in i…
lklimek May 6, 2026
a26c2f7
fix(rs-platform-wallet/e2e): bump id_sweep setup funding to clear tea…
lklimek May 6, 2026
f023ecb
fix(rs-platform-wallet/e2e): register dynamically-deployed contracts …
lklimek May 6, 2026
94a8149
fix(rs-platform-wallet/e2e): wait for identity visibility on platform…
lklimek May 6, 2026
6c3e5da
feat(rs-platform-wallet/e2e): pre-flight bank balance check with acti…
lklimek May 6, 2026
307b66b
fix(rs-platform-wallet/e2e): relax MIN_BANK_CREDITS to 500M; warn at …
lklimek May 6, 2026
d1d81a3
feat(rs-platform-wallet/e2e): PLATFORM_WALLET_E2E_DISABLE_SPV env var…
lklimek May 6, 2026
11c5b4d
docs(rs-platform-wallet/e2e): INTENTIONAL comments on PA-* hard panic…
lklimek May 7, 2026
ffbcf12
feat(rs-platform-wallet/e2e): print_bank_address also prints Core fal…
lklimek May 7, 2026
5a3c1ca
fix(rs-platform-wallet/e2e): TK-013/14 sign with CRITICAL key for tok…
lklimek May 7, 2026
38f12a3
fix(rs-platform-wallet/e2e): TK-011 align with mint-on-purchase seman…
lklimek May 7, 2026
ce2181d
fix(rs-platform-wallet/e2e): bump id_001 FUNDING_CREDITS to clear tea…
lklimek May 7, 2026
fdcce11
fix(rs-platform-wallet/e2e): make sweep_platform_addresses best-effor…
lklimek May 7, 2026
7bda838
docs(rs-platform-wallet/e2e): INTENTIONAL comments on TK-001c + TK-00…
lklimek May 7, 2026
84749df
feat(rs-platform-wallet/e2e): perpetual-distribution token contract t…
lklimek May 7, 2026
337d937
test(rs-platform-wallet/e2e): wire TK-002 using setup_with_token_perp…
lklimek May 7, 2026
1d74047
feat(rs-platform-wallet/e2e): add SeedBackedIdentitySigner::inject_id…
lklimek May 7, 2026
8a09641
test(rs-platform-wallet/e2e): wire TK-001c using rotate_identity_auth…
lklimek May 7, 2026
4dc6c39
feat(rs-platform-wallet): next_unused_receive_addresses(count) access…
lklimek May 7, 2026
fd9cce7
feat(rs-platform-wallet): output_change_address override on platform-…
lklimek May 7, 2026
376794f
docs(rs-platform-wallet/e2e): add CR-004 spec — legacy BIP32 account …
lklimek May 7, 2026
e334c0f
feat(rs-platform-wallet): CR-004 — legacy BIP32 UTXO update after spe…
lklimek May 7, 2026
6ce5bae
Revert "docs(rs-platform-wallet/e2e): add CR-004 spec — legacy BIP32 …
lklimek May 7, 2026
9e8881e
fix(rs-platform-wallet/e2e): re-apply 4 fixes silently reverted by 7b…
lklimek May 7, 2026
46fb04e
fix(rs-platform-wallet/e2e): make Core-sweep teardown best-effort to …
lklimek May 7, 2026
fafa5b4
feat(rs-platform-wallet/e2e): PLATFORM_WALLET_E2E_RUN_FAILING_BY_DESI…
lklimek May 7, 2026
9da773a
fix(rs-platform-wallet/e2e): TK-013 wait for pre-programmed epoch to …
lklimek May 7, 2026
fd44ea1
fix(rs-platform-wallet/e2e): TK-002 longer wait + tolerate typed `NoC…
lklimek May 7, 2026
67e7d66
feat(rs-platform-wallet/e2e): document parallelism contract + paralle…
lklimek May 7, 2026
066f11e
fix(rs-platform-wallet/e2e): QA-V19-001 — TK-013 polls platform block…
lklimek May 7, 2026
3bcebb5
fix(rs-platform-wallet/e2e): QA-V19-003 — drop PA-005b precondition
lklimek May 7, 2026
5d896d8
fix(rs-platform-wallet/e2e): QA-V19-002 — PA-001b declare only consum…
lklimek May 7, 2026
9c7d1a7
fix(rs-platform-wallet/e2e): bank.fund_address waits for chain confir…
lklimek May 7, 2026
6c2b8f9
merge: bring parallelism contract + parallel-safe assertions into fix…
lklimek May 7, 2026
6b65c7c
merge: bring feat/...e2e (post-test-collapse) into fix/...e2e-qa-fixe…
lklimek May 7, 2026
8b042ce
merge: feat/...e2e (post-v3.1-dev sync) into fix/...e2e-qa-fixes-v1
lklimek May 7, 2026
0d2a011
Merge branch 'feat/rs-platform-wallet-e2e' into fix/rs-platform-walle…
lklimek May 7, 2026
a447a72
fix(rs-platform-wallet/e2e): TK-013 accepts InvalidTokenClaimNoCurren…
lklimek May 7, 2026
29252dc
fix(rs-platform-wallet/e2e): PA-006b balanced sums for concurrent bro…
lklimek May 7, 2026
020f7b7
fix(rs-platform-wallet/e2e): PA-001b derive (dest, change_addr) via b…
lklimek May 7, 2026
813ce11
fix(rs-platform-wallet/e2e): PA-006b assert on-chain outcome, not bro…
lklimek May 8, 2026
093f2f4
feat(rs-platform-wallet/e2e): bank-floor hard precondition gate for T…
lklimek May 8, 2026
5a80162
fix(rs-platform-wallet/e2e): cleanup actually sweeps funded wallets b…
lklimek May 8, 2026
88309a6
feat(rs-platform-wallet/e2e): bank balance cross-check post-SPV-sync …
lklimek May 8, 2026
66014ac
fix(rs-platform-wallet/e2e): sweep_orphans runs BEFORE bank-floor pan…
lklimek May 8, 2026
571a4ce
test(rs-platform-wallet): enable dash-spv keep-finalized-transaction …
lklimek May 8, 2026
69b6e18
fix(rs-platform-wallet/e2e): cross-check fires on every init, not jus…
lklimek May 8, 2026
de12237
fix(rs-platform-wallet/e2e): TK-013 match by typed variant not displa…
lklimek May 8, 2026
e147bca
feat(rs-platform-wallet/e2e): info! on bank-floor and cross-check suc…
lklimek May 8, 2026
62bc788
fix(rs-platform-wallet/e2e): PA-001b survives threads=8 (QA-V27-006)
lklimek May 8, 2026
c4653e2
docs(rs-platform-wallet/e2e): mark PA-004b/PA-009 ignored, document V…
lklimek May 8, 2026
c6ab79c
fix(rs-platform-wallet/e2e): walk up to parent-repo .env when running…
lklimek May 8, 2026
e59adda
feat(rs-platform-wallet/e2e): per-test Drop sweep + counter-driven en…
lklimek May 8, 2026
a85ec6a
fix(rs-platform-wallet/e2e): PA-003 funding accounts for setup-fee ov…
lklimek May 8, 2026
0bbfa80
docs(rs-platform-wallet/e2e): clarify PA-004b/009/010 #[ignore] cohor…
lklimek May 8, 2026
5a1e249
fix(rs-platform-wallet/e2e): promote .env load success log to info (Q…
lklimek May 8, 2026
263861a
fix(rs-platform-wallet/e2e): tolerate sub-tDASH drift on bank cross-c…
lklimek May 8, 2026
e1dfaed
fix(rs-platform-wallet/e2e): cap SetupGuard::Drop sweep with 20s time…
lklimek May 8, 2026
d1adafd
fix(rs-platform-wallet/e2e): TK-005 funding step survives threads=8 (…
lklimek May 8, 2026
56db3ad
fix(rs-platform-wallet/e2e): TK-010 gates pause/resume on chain propa…
lklimek May 8, 2026
4f39a1c
fix(rs-platform-wallet/e2e): TK-011 gates post-mint balance read on c…
lklimek May 8, 2026
e3d29c8
Merge remote-tracking branch 'origin/feat/rs-platform-wallet-e2e' int…
lklimek May 8, 2026
e7695d2
docs(rs-platform-wallet/e2e): TODO on ID-005 cross-replica gate (PR #…
lklimek May 8, 2026
d7c9f4c
test(rs-platform-wallet/e2e): split PA-001b into per-branch sub-cases…
lklimek May 8, 2026
5ac921f
refactor(rs-platform-wallet): move test-only batch-fresh accessor to …
lklimek May 8, 2026
0b1f062
test(rs-platform-wallet/e2e): split PA-005b into per-count sub-cases …
lklimek May 8, 2026
af2c147
test(rs-platform-wallet/e2e): split PA-009 into per-property sub-case…
lklimek May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/rs-platform-wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,12 @@ tokio-util = { version = "0.7", features = ["rt"] }
# and `framework/context_provider.rs` and is currently disabled
# (see harness.rs) — re-enable when SPV cold-start is stable
# (Task #15).
rs-sdk-trusted-context-provider = { path = "../rs-sdk-trusted-context-provider" }

rs-sdk-trusted-context-provider = { path = "../rs-sdk-trusted-context-provider", features = ["dpns-contract"] }
# In-memory test runs (NoPlatformPersistence) need finalized txs retained in RAM.
# Re-declaring here enables the feature for the test target only; production
# builds pay no memory overhead. Per upstream rust-dashcore maintainer guidance.
key-wallet = { workspace = true, features = ["keep-finalized-transactions"] }
key-wallet-manager = { workspace = true, features = ["keep-finalized-transactions"] }

[features]
default = ["bls", "eddsa"]
Expand Down
14 changes: 14 additions & 0 deletions packages/rs-platform-wallet/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ pub enum PlatformWalletError {
#[error("Address operation failed: {0}")]
AddressOperation(String),

#[error(
"gap-limit exceeded: requested {requested} fresh unused addresses but only \
{available} are derivable past the current gap-limit boundary \
(highest_used={highest_used:?}, highest_generated={highest_generated:?}, \
gap_limit={gap_limit})"
)]
GapLimitExceeded {
requested: usize,
available: u32,
highest_used: Option<u32>,
highest_generated: Option<u32>,
gap_limit: u32,
},

#[error("{}", format_no_selectable_inputs(funded_outputs, *sub_min_count, *sub_min_aggregate, *min_input_amount))]
NoSelectableInputs {
/// Funded addresses dropped by the input-equals-output filter.
Expand Down
10 changes: 10 additions & 0 deletions packages/rs-platform-wallet/src/wallet/core/broadcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@ impl<B: TransactionBroadcaster + ?Sized> CoreWallet<B> {
"Wallet not found in wallet manager".to_string(),
)
})?;
tracing::debug!(
target: "platform_wallet::core::broadcast",
txid = %tx.txid(),
account_type = ?account_type,
account_index,
inputs = tx.input.len(),
outputs = tx.output.len(),
"post-broadcast: dispatching check_core_transaction(Mempool) — \
must mark consumed UTXOs spent on the matching account collection"
);
info.check_core_transaction(&tx, TransactionContext::Mempool, wallet, true, true)
.await;
}
Expand Down
1 change: 1 addition & 0 deletions packages/rs-platform-wallet/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! harness; `cases/` hosts `#[tokio_shared_rt::test(shared)]` entries.

#![allow(dead_code, unused_imports)]
#![allow(clippy::result_large_err)]

// `tests/e2e.rs` is the integration-test crate root; explicit
// `#[path]` keeps the on-disk layout grouped under `tests/e2e/`.
Expand Down
38 changes: 32 additions & 6 deletions packages/rs-platform-wallet/tests/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,42 @@ Tracing output (SPV sync events, balance polls, sweep results) is written to std

---

## Multi-process safety
## Parallelism

Multiple `cargo test` invocations running concurrently — for example, parallel CI jobs
on different branches — must not share the same bank wallet or working directory, or
they will conflict on nonces.
The harness supports running cases in parallel within a single `cargo test`
invocation (`--test-threads=N`, N > 1) AND across multiple concurrent invocations
on the same machine.

The framework handles this at two levels:
### In-process (`--test-threads=N`)

All tests share one `E2eContext` (singleton via `tokio::sync::OnceCell`), one bank
wallet, one SPV runtime, and one workdir slot. Per-test isolation comes from:

- **Fresh per-test wallets** — every `setup()` mints a fresh OS-random 64-byte seed,
so two parallel tests have disjoint wallet ids, addresses, identities, and nonces.
- **Serialised bank funding** — `bank.fund_address` and `bank.send_core_to` lock a
process-global `FUNDING_MUTEX` so concurrent callers don't race UTXO selection or
nonce assignment. Tests waiting on `wait_for_balance` do NOT hold the mutex —
bank serialisation only covers the actual broadcast critical section.
- **Compile-time `Send + Sync`** — `E2eContext` and `SetupGuard` are statically
asserted thread-safe (`framework/mod.rs`). A future field addition that breaks
thread-safety fails to compile.

Two cases need a note under parallel execution:

- **PA-008c** observes the process-global `FUNDING_MUTEX_HISTORY` ring buffer to
prove the mutex serialises. Asserts a lower bound on entry count (`>= 3`) and
the pairwise non-overlap property — both hold regardless of sibling traffic.
- **PA-010** is `#[ignore]`'d pending a per-test bank instance API; bank is
process-shared by design.

### Cross-process (concurrent `cargo test` invocations)

Multiple `cargo test` invocations on the same machine — for example, parallel CI
jobs or developer worktrees — must NOT share the same bank wallet or workdir slot.

**Workdir slots** — each process tries to acquire an exclusive `flock` on the base
working directory. If that lock is already held it tries up to 10 numbered slot
working directory. If that lock is already held it walks up to 10 numbered slot
directories (`<workdir>-1`, `<workdir>-2`, ...). A slot holds the SPV block cache,
the SDK config, and the test-wallet registry independently from every other slot.

Expand Down
107 changes: 107 additions & 0 deletions packages/rs-platform-wallet/tests/e2e/TEST_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -2311,4 +2311,111 @@ Each question's answer changes the spec; numbered for reference.

---

## 7. Known Issues

Tracked production bugs and harness gaps that affect test outcomes. Tests are
`#[ignore]`d in these cases — but **`#[ignore]` does NOT mean "never runs"**:

- `cargo test` (default): ignored tests are **skipped**.
- `cargo test -- --ignored`: runs **only** ignored tests. PA-004b, PA-009, and PA-010 execute under this flag and fail by design. Any failure mode other than the one documented per-entry below is a regression.

Do not modify production code in this section — these are documentation entries only.

### V27-007 — `PlatformAddressWallet::transfer` ledger pollution (production bug)

**Status**: tracked, fix deferred. Tests `pa_004b_sweep_below_dust_gate_no_broadcast`
and `pa_009_cleanup_gate_tracks_platform_version_min_input_amount` are `#[ignore]`'d
with reason `"FAILING — production bug in PlatformAddressWallet::transfer pollutes local ledger with non-owned addresses. See TEST_SPEC.md (V27-007) and TODO comment below."` — they run under `cargo test -- --ignored` and fail by design until the production fix lands.

**Expected failure mode** (PA-004b and PA-009): the `assert_eq!(addr_1_residual, TARGET_RESIDUAL, ...)` assertion panics because `total_credits()` returns the bank's full balance (~40.8 tDASH) instead of the wallet's actual residual (`TARGET_RESIDUAL = 1_000`). Any failure at a different assertion or with a different value is a regression.

**PA-010 — harness gap** (`pa_010_bank_starvation_typed_error`): this test is also `#[ignore]`'d (`"BLOCKED — needs harness refactor: per-test bank instance (Bank::with_test_balance) OR injectable balance override on the singleton, plus a typed BankError::Underfunded variant. See spec status."`) and fails under `cargo test -- --ignored` by design — it always panics with:

```
PA-010 is BLOCKED on a harness refactor. The bank is a process-shared singleton (E2eContext.bank, OnceCell-backed); building a `with_test_balance(5_000_000)` underfunded instance for ONE test conflicts with that lifecycle. The current under-funded fail mode is also a generic AddressOperation error, not a typed BankError::Underfunded. See TEST_SPEC.md → PA-010 → **Status**.
```

This is a harness gap (not a production bug); fix path is tracked in the harness roadmap (Wave 4 / `Bank::with_test_balance` constructor). Any panic message other than the one above, or a failure that propagates past the `panic!` call, is a regression.

**Bug**: `PlatformAddressWallet::transfer` at
`packages/rs-platform-wallet/src/wallet/platform_addresses/transfer.rs:160` calls
`account.set_address_credit_balance(p2pkh, funds.balance, key_source.as_ref())`
for every address in the transition (inputs ∪ outputs), with no ownership check.
When a wallet transfers to an externally-owned address (e.g., bank's primary
receive address), the externally-owned post-balance gets staged into the source
wallet's local `address_balances` ledger.

**Symptom**: `wallet.total_credits()` after a transfer-to-external returns the
external address's balance summed in. PA-004b/PA-009 see the bank's full
~40.8 tDASH on what should be a dust-residual wallet → assertions panic.

**Same unguarded primitive** also exists at:
- `packages/rs-platform-wallet/src/wallet/platform_addresses/withdrawal.rs:141`
- `packages/rs-platform-wallet/src/wallet/platform_addresses/fund_from_asset_lock.rs:129`

Currently safe by caller behavior (those iterate only-owned addresses), but
identical shape; defense-in-depth fix should apply there too.

**Severity**:
- **Tests**: HIGH — every `total_credits()` post-transfer-to-external is a false read.
- **SDK consumers**: HIGH — anyone following `transfer → read total_credits` sees
inflated balances and could make wrong spend decisions.
- **Production sweep path**: MEDIUM-LOW — sweep would build inputs against the
external address, but the source wallet can't sign for it; Drive rejects the
transition; error swallowed → no on-chain leak.

**Fix sketch** (~6 LOC, do not apply in this PR):
Filter the loop in `transfer.rs:145-160` so `set_address_credit_balance` is
called only for addresses the source account owns:

```rust
for (addr, maybe_info) in address_infos.iter() {
let PlatformAddress::P2pkh(hash) = addr else { continue };
let p2pkh = PlatformP2PKHAddress::new(*hash);
// Skip addresses the source account doesn't own; address_infos covers
// inputs ∪ outputs and outputs we don't own must not pollute the local
// credit ledger.
if !account.address_balances.contains_key(&p2pkh)
&& account.addresses.address_info_by_p2pkh(&p2pkh).is_none()
{
continue;
}
// ... existing set_address_credit_balance + changeset push
}
```

Defense-in-depth: apply same filter at `withdrawal.rs:141` and
`fund_from_asset_lock.rs:129`. Optionally make `set_address_credit_balance`
itself reject addresses not in the pool (wider change in `key-wallet`).

**Confirmation audit**:
- Search for any aggregate that sums `total_credits()` across multiple wallets in the manager (production code, dashboards, telemetry) — would double-count.
- Run e2e suite with the fix in place, verify PA-004b/PA-009 pass.
- Add debug assertion in `set_address_credit_balance` that the address is in the pool — every callsite that violates would surface.

**Investigated**: Bilby read-only audit, 2026-05-08, agent ID `a2d81349f872a0c6a`.

---

### V28-303 — PA-003 partial fix: deficit closed, contention timeout remains

**Status**: partial. PA-003 (`pa_003_fee_scaling`) is NOT `#[ignore]`'d — it runs in the default `cargo test` cohort. However, it is not reliably green under concurrency.

**What V28-303 did**: bumped `FUNDING_CREDITS` from 400M to 500M and `FUNDING_FLOOR` from 350M to 450M (`cases/pa_003_fee_scaling.rs`). This closed the "available 240,524,980 credits, required 250,000,000" deficit that caused a deterministic failure on the 5-output transfer leg: with 400M pre-fund, `addr_src` retained only ~200M after the 1-out transfer and five marker transfers, giving ~235M of reachable candidate balance against a 250M requirement. With 500M pre-fund, `addr_src` retains ≥300M post-setup and the auto-selector has comfortable headroom.

**What V28-303 did NOT fix**: at `threads=8` (standard CI concurrency), the `wait_for_balance` call on funding confirmation hits the 60s deadline before the balance settles. Current observed failure mode:

```
wait_for_balance timed out after 60s — addr_src balance never reached FUNDING_FLOOR (450_000_000)
```

This is a contention symptom: eight concurrent tests competing for DAPI bandwidth and bank-wallet nonce slots delay the funding broadcast confirmation beyond the per-step `STEP_TIMEOUT = Duration::from_secs(60)`.

**Claiming "V28-303 fixes PA-003" or "PA-003 first time passing" is wrong.** V28-303 narrows the failure surface (one deterministic failure mode removed) but does not green-light PA-003 in standard CI.

**Real fix path**: QA-V28-403 — raise `STEP_TIMEOUT` per step (or use a dynamic deadline tied to observed DAPI latency under load). Until that lands, PA-003 may pass in low-concurrency or low-load runs and fail under the standard 8-thread CI tier.

---


<sub>Catalogued by Marvin (QA), with the resigned competence of someone who has read every line of this code twice. Edge-case expansion by Trillian, who knows that the difference between "tested" and "tested at the boundary" is the difference between "ships" and "ships back".</sub>
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use platform_wallet::wallet::identity::types::funding::IdentityFundingMethod;

use crate::framework::prelude::*;
use crate::framework::signer::{derive_identity_key, SeedBackedIdentitySigner};
use crate::framework::wait::wait_for_identity_balance;
use crate::framework::wait::{wait_for_identity_balance, wait_for_identity_visible_to_platform};

/// DIP-9 identity index used for the asset-lock registration. Slot 0
/// is canonical for "first identity on this wallet" — same convention
Expand Down Expand Up @@ -217,9 +217,22 @@ async fn cr_003_asset_lock_funded_registration() {
asset-lock output value (fees are subtracted, not added)."
);

// Step 5: round-trip the identity via the SDK to assert the
// returned shape matches the on-chain shape — same MASTER key id,
// same balance, same revision = 0 baseline.
// Step 5: wait for the identity to be visible across enough DAPI
// replicas before the round-trip fetch. The asset-lock-funded path
// has different proof convergence than the address-funded path —
// `wait_for_identity_balance` above confirms credits landed, but
// a subsequent `Identity::fetch` on a still-lagging replica returns
// `Ok(None)`. Two consecutive successes bias toward distinct nodes
// having replicated the identity (QA-911).
wait_for_identity_visible_to_platform(
s.test_wallet.platform_wallet().sdk(),
identity_id,
IDENTITY_VISIBILITY_TIMEOUT,
2,
)
.await
.expect("identity propagation gate cleared before round-trip fetch (QA-911)");

let fetched = Identity::fetch(s.test_wallet.platform_wallet().sdk(), identity_id)
.await
.expect("Identity::fetch round-trip after registration")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,33 @@ use rand::RngCore;
use crate::framework::prelude::*;
use crate::framework::wait::wait_for_dpns_name_visible;

/// Bank → funding-address gross. Sized to cover the registration
/// transition (`REGISTRATION_FUNDING`) plus the chain-time
/// `IdentityCreateFromAddresses` dynamic fee paid from the address
/// residual (~96M observed at ID-001 calibration), with comfortable
/// headroom for DPNS-register-side fees that come out of the
/// identity's credit balance afterwards.
const FUNDING_CREDITS: u64 = 200_000_000;

/// Pre-fee credits committed to the new identity by
/// `IdentityCreateFromAddresses`. The identity arrives on chain with
/// exactly this balance — DPNS register fees draw against it.
const REGISTRATION_FUNDING: u64 = 130_000_000;

/// Headroom carried on the funding address residual so the chain-time
/// `IdentityCreateFromAddresses` dynamic fee (~110.86M observed on
/// testnet — `validate_fees_of_event_v0 PaidFromAddressInputs`
/// baseline plus the slot-2 TRANSFER key's storage cost) clears with
/// buffer for protocol-version drift. Mirrors the
/// `setup_with_n_identities` `REGISTRATION_HEADROOM` constant in
/// `framework/mod.rs` — the residual must absorb the dynamic fee
/// after registration consumes `REGISTRATION_FUNDING`, otherwise the
/// chain returns
/// `AddressesNotEnoughFundsError(required=110_862_220)` (QA-701-B).
const REGISTRATION_HEADROOM: u64 = 150_000_000;

/// Bank → funding-address gross. Funds the registration transition
/// (`REGISTRATION_FUNDING`) plus the dynamic-fee residual headroom
/// (`REGISTRATION_HEADROOM`). Earlier sizings (~200M) left only ~70M
/// after the registration consumed `REGISTRATION_FUNDING`, which fell
/// short of the ~110.86M dynamic fee — DPNS-001 then panicked with
/// "Insufficient combined address balances: total available is less
/// than required 110862220". Reuses the same arithmetic as
/// `setup_with_n_identities`'s funding policy.
const FUNDING_CREDITS: u64 = REGISTRATION_FUNDING + REGISTRATION_HEADROOM;

/// Floor `wait_for_balance` keys on before registration runs. Under
/// Option C (DeductFromInput) the address receives exactly
/// `FUNDING_CREDITS`, so the floor equals the funded amount.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ use crate::framework::prelude::*;

/// Funds the bank submits to the funding address. Option C
/// (DeductFromInput) delivers exactly this amount to the address.
/// Sized so that after the 50M registration, the residual (130M)
/// Sized so that after the 50M registration, the residual (160M)
/// covers the chain-time IdentityCreateFromAddresses dynamic fee
/// (~110.86M, from validate_fees_of_event_v0 PaidFromAddressInputs;
/// grew from ~96M after the slot-2 TRANSFER key was added in
/// `173b2e15ce`, +~550 bytes × 27_000 credits/byte ≈ +14.85M) with
/// ~19M buffer.
const FUNDING_CREDITS: u64 = 180_000_000;
/// (~125.71M, from validate_fees_of_event_v0 PaidFromAddressInputs;
/// grew from ~110.86M after QA-800 added the CRITICAL key in slot 4,
/// +~550 bytes × 27_000 credits/byte ≈ +14.85M) with ~30M buffer for
/// the teardown sweep fee.
const FUNDING_CREDITS: u64 = 210_000_000;

/// Floor the wait_for_balance keys on before registration runs.
/// Under Option C the address receives exactly FUNDING_CREDITS, so
/// the floor equals the funded amount.
const FUNDING_FLOOR: u64 = 180_000_000;
const FUNDING_FLOOR: u64 = 210_000_000;

/// Credits committed to the new identity in the registration
/// transition. The address loses this exact amount minus the bank's
Expand Down Expand Up @@ -104,8 +104,8 @@ async fn id_001_register_identity_from_addresses() {
);
assert_eq!(
on_chain.public_keys().len(),
3,
"registered identity must carry exactly three keys (MASTER + HIGH + TRANSFER)"
4,
"registered identity must carry exactly four keys (MASTER + HIGH + TRANSFER + CRITICAL)"
);
assert!(
on_chain.balance() >= IDENTITY_BALANCE_FLOOR,
Expand All @@ -125,7 +125,7 @@ async fn id_001_register_identity_from_addresses() {

// Address residual: register_from_addresses consumed
// REGISTRATION_FUNDING from the address AND the chain-time
// dynamic fee (~96M observed). After both, residual <
// dynamic fee (~125.71M observed). After both, residual <
// FUNDING_CREDITS - REGISTRATION_FUNDING (the headroom).
s.test_wallet
.sync_balances()
Expand Down
Loading
Loading