Summary
Suppose we send a DashPay contact request and the contact never sends one back. That contact never gets a DashpayReceivingFunds account in the SDK wallet. Our receiving chain for that contact is never watched, and any payment the contact sends us on it is invisible at every rescan depth.
DIP-15 puts our receiving xpub inside the contact request we send. A contact who has our request can therefore pay us whether or not they ever reciprocate. platform-wallet assumes the opposite in two places.
Where
- Account building.
collect_account_build_candidates (packages/rs-platform-wallet/src/wallet/identity/network/contact_requests.rs) walks established_contacts() only. A contact that exists only in sent_contact_requests() is never a candidate, so RegisterReceiving is never queued for it.
- Rescan.
reconcile_dashpay_rescan (.../network/payments.rs) skips every receival account whose contact is not in established_contacts(). Even an account built some other way would get no backfill for the blocks before it was registered.
The live send path (send_contact_request_with_external_signer) registers the receiving account itself, so a wallet that sends the request in the current session is fine. The gap appears when the sent request is learned from Platform by the contact sweep (restore from seed, a second device, a migrated wallet), or when the live registration failed after the request had been saved.
A contact who only sent us a request is not affected: they never received our xpub, so there is no chain of ours for them to pay.
Example (testnet, topple wallet 7dc06ad3…)
- Tx
e5169bfc4989585abd4b0476188611b981e3c750539da5b8a39fe135e3bbb957, height 1,475,820: 0.001 DASH to yMNNc2UZz62V9N7SZfQnsk79G5okCP7eSN. That is our receiving chain 15'/0'/(us)/(4193bbe6…)/0 for contact 5QzA7GnST….
- The SDK store holds one contact request for that contact: ours, at core height 1,475,801, 19 blocks before the payment. It has no incoming request and no DashPay account rows for the contact.
- The transaction is missing from every archived SDK store of this wallet (about 40 builds, int6 through int27). dashj's wallet dump has it and watches the same chain.
- Its later spend,
6ac8356c…, is stored with netAmount = +99734 instead of minus the fee, because the funding transaction is unknown.
- The balance is correct today only because that coin has since been spent. An unspent payment on such a chain would be missing money.
- Two more contacts on the same wallet (
c7037829…, ce3cff2f…) are in the same one-way state.
The host's dark-contact diagnostic cannot see this case either: it lists established contacts that lack receival accounts, and these contacts are not established.
Expected
For every contact we have sent a request to, the wallet should register our DashpayReceivingFunds account (the external account still needs their request) and rescan from the height of our request.
Related
Summary
Suppose we send a DashPay contact request and the contact never sends one back. That contact never gets a
DashpayReceivingFundsaccount in the SDK wallet. Our receiving chain for that contact is never watched, and any payment the contact sends us on it is invisible at every rescan depth.DIP-15 puts our receiving xpub inside the contact request we send. A contact who has our request can therefore pay us whether or not they ever reciprocate. platform-wallet assumes the opposite in two places.
Where
collect_account_build_candidates(packages/rs-platform-wallet/src/wallet/identity/network/contact_requests.rs) walksestablished_contacts()only. A contact that exists only insent_contact_requests()is never a candidate, soRegisterReceivingis never queued for it.reconcile_dashpay_rescan(.../network/payments.rs) skips every receival account whose contact is not inestablished_contacts(). Even an account built some other way would get no backfill for the blocks before it was registered.The live send path (
send_contact_request_with_external_signer) registers the receiving account itself, so a wallet that sends the request in the current session is fine. The gap appears when the sent request is learned from Platform by the contact sweep (restore from seed, a second device, a migrated wallet), or when the live registration failed after the request had been saved.A contact who only sent us a request is not affected: they never received our xpub, so there is no chain of ours for them to pay.
Example (testnet, topple wallet
7dc06ad3…)e5169bfc4989585abd4b0476188611b981e3c750539da5b8a39fe135e3bbb957, height 1,475,820: 0.001 DASH toyMNNc2UZz62V9N7SZfQnsk79G5okCP7eSN. That is our receiving chain15'/0'/(us)/(4193bbe6…)/0for contact5QzA7GnST….6ac8356c…, is stored withnetAmount = +99734instead of minus the fee, because the funding transaction is unknown.c7037829…,ce3cff2f…) are in the same one-way state.The host's dark-contact diagnostic cannot see this case either: it lists established contacts that lack receival accounts, and these contacts are not established.
Expected
For every contact we have sent a request to, the wallet should register our
DashpayReceivingFundsaccount (the external account still needs their request) and rescan from the height of our request.Related