From 6aca58be7854309a0e4992420acbc9fdb219abf7 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 5 May 2026 10:37:57 +0200 Subject: [PATCH] test(rs-platform-wallet/e2e): DPNS-001 register and resolve .dash name First DPNS-tier test on the e2e harness. Sets up an identity via Wave A signer + register_identity_from_addresses, registers a uniquely labelled .dash name, asserts resolver visibility within STEP_TIMEOUT. Standard #[ignore]-gated; relies on PLATFORM_WALLET_E2E_BANK_MNEMONIC + live testnet. Per TEST_SPEC.md DPNS-001 (P0). Co-Authored-By: Claude Opus 4.6 --- .../rs-platform-wallet/tests/e2e/TEST_SPEC.md | 2 +- .../tests/e2e/cases/dpns_001_register_name.rs | 136 ++++++++++++++++++ .../rs-platform-wallet/tests/e2e/cases/mod.rs | 1 + 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 packages/rs-platform-wallet/tests/e2e/cases/dpns_001_register_name.rs diff --git a/packages/rs-platform-wallet/tests/e2e/TEST_SPEC.md b/packages/rs-platform-wallet/tests/e2e/TEST_SPEC.md index 5ea2ed791e..703279fda0 100644 --- a/packages/rs-platform-wallet/tests/e2e/TEST_SPEC.md +++ b/packages/rs-platform-wallet/tests/e2e/TEST_SPEC.md @@ -1071,7 +1071,7 @@ so that when SPV lands, the test bodies can be written without further design. #### DPNS-001 — Register and resolve a `.dash` name - **Priority**: P0 -- **Status**: STUB — placeholder for follow-up PR (Wave A + DPNS helpers). +- **Status**: STUB — implemented in `cases/dpns_001_register_name.rs`; `#[ignore]`-gated, run with `cargo test -- --ignored`. - **Wallet feature exercised**: `wallet/identity/network/dpns.rs:176` (`register_name_with_external_signer`); `dpns.rs:281` (`resolve_name`). - **DET parallel**: `dash-evo-tool/tests/backend-e2e/register_dpns.rs:14` (`test_register_dpns_name`). - **Preconditions**: ID-001 helper; identity has `≥ 100_000_000` credits (DPNS register fee + headroom). diff --git a/packages/rs-platform-wallet/tests/e2e/cases/dpns_001_register_name.rs b/packages/rs-platform-wallet/tests/e2e/cases/dpns_001_register_name.rs new file mode 100644 index 0000000000..d14738e424 --- /dev/null +++ b/packages/rs-platform-wallet/tests/e2e/cases/dpns_001_register_name.rs @@ -0,0 +1,136 @@ +//! DPNS-001 — Register and resolve a `.dash` name. +//! Spec: `tests/e2e/TEST_SPEC.md` §"DPNS" → DPNS-001. +//! Priority: P0. +//! +//! Bank funds a fresh platform address, the wallet registers an +//! identity at DIP-9 slot 0 via the Wave A +//! [`TestWallet::register_identity_from_addresses`] helper, then a +//! uniquely-labelled `e2e-<8 hex>.dash` name is registered against that +//! identity through +//! [`IdentityWallet::register_name_with_external_signer`]. The +//! assertion side waits on +//! [`wait_for_dpns_name_visible`](crate::framework::wait::wait_for_dpns_name_visible) +//! so we observe end-to-end resolver propagation, not just the +//! state-transition broadcast acknowledgement. +//! +//! `#[ignore]`-gated like the rest of the live-testnet harness; pair +//! with `PLATFORM_WALLET_E2E_BANK_MNEMONIC` and run via +//! `cargo test -- --ignored`. + +use std::time::Duration; + +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 = 100_000_000; + +/// 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. +const FUNDING_FLOOR: u64 = FUNDING_CREDITS; + +/// Per-step deadline: bank funding observation, identity visibility, +/// DPNS resolver visibility. +const STEP_TIMEOUT: Duration = Duration::from_secs(60); + +#[ignore = "requires PLATFORM_WALLET_E2E_BANK_MNEMONIC and live testnet access; \ + run with `cargo test -- --ignored`"] +#[tokio_shared_rt::test(shared, flavor = "multi_thread", worker_threads = 12)] +async fn dpns_001_register_and_resolve_name() { + let _ = tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "info,platform_wallet=debug".into()), + ) + .with_test_writer() + .try_init(); + + let s = setup().await.expect("e2e setup failed"); + + // 1. Bank-fund a fresh address. + let funding_addr = s + .test_wallet + .next_unused_address() + .await + .expect("derive funding address"); + s.ctx + .bank() + .fund_address(&funding_addr, FUNDING_CREDITS) + .await + .expect("bank.fund_address"); + wait_for_balance(&s.test_wallet, &funding_addr, FUNDING_FLOOR, STEP_TIMEOUT) + .await + .expect("funding never observed"); + + // 2. Register identity at DIP-9 slot 0 (Wave A helper does the + // placeholder identity + key derivation + on-chain wait). + let identity = s + .test_wallet + .register_identity_from_addresses(funding_addr, REGISTRATION_FUNDING, 0) + .await + .expect("register_identity_from_addresses"); + + // 3. Generate a unique `.dash` label per run. 8 hex chars = + // 32 bits of entropy — collision-safe for a CI worker fleet + // while keeping the label well inside DPNS's 3..=63 char + // range. + let label = format!("e2e-{}", random_hex_label(8)); + let name = format!("{label}.dash"); + + // 4. Register the DPNS name. The wallet's external-signer path + // looks the identity up by id and signs the document state + // transition with the supplied identity signer (Wave A's + // `SeedBackedIdentitySigner`, pre-derived for slot 0). The + // label parameter is the prefix only — DPP appends ".dash" and + // returns the full domain name. + let full_name = s + .test_wallet + .platform_wallet() + .identity() + .register_name_with_external_signer(&identity.id, &label, identity.signer.as_ref()) + .await + .expect("register_name_with_external_signer"); + assert_eq!( + full_name, name, + "register_name_with_external_signer must return the full domain name" + ); + + // 5. Wait for resolver visibility — observable propagation, not + // just the broadcast ack. + let resolved = wait_for_dpns_name_visible(s.ctx.sdk(), &name, STEP_TIMEOUT) + .await + .expect("DPNS name never resolved"); + + // 6. Resolution must return the registering identity. + assert_eq!( + resolved, identity.id, + "DPNS resolver must return the registering identity's id" + ); + + s.teardown().await.expect("teardown"); +} + +/// Generate a lower-case hex string of length `n` from +/// [`rand::rngs::OsRng`]. Used to pin a unique DPNS label per run so +/// concurrent CI workers don't contest each other and a re-run never +/// collides with a prior round's already-registered name. +fn random_hex_label(n: usize) -> String { + let mut bytes = vec![0u8; n.div_ceil(2)]; + rand::rngs::OsRng.fill_bytes(&mut bytes); + let mut s = hex::encode(&bytes); + s.truncate(n); + s +} diff --git a/packages/rs-platform-wallet/tests/e2e/cases/mod.rs b/packages/rs-platform-wallet/tests/e2e/cases/mod.rs index 24cf6a87e0..a1e7fec93f 100644 --- a/packages/rs-platform-wallet/tests/e2e/cases/mod.rs +++ b/packages/rs-platform-wallet/tests/e2e/cases/mod.rs @@ -2,6 +2,7 @@ //! `#[tokio_shared_rt::test(shared)]` entries that share the //! process-wide [`super::framework::E2eContext`]. +pub mod dpns_001_register_name; pub mod id_001_register_identity_from_addresses; pub mod id_002_top_up_identity; pub mod id_003_identity_to_identity_transfer;