You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(dash-spv): track network acceptance of broadcast transactions (#913)
* feat(dash-spv): track network acceptance of broadcast transactions
Broadcasts are now sent to a subset of connected peers (default: half,
per BroadcastHoldout::Half) while being withheld from the rest. Since
peers never re-announce a transaction to whoever sent it to them, an inv
for our txid from a withheld peer proves the transaction relayed through
the network — the broadcast is then reported Accepted. InstantSend
locks, block confirmations, and already-have rejects also count as
acceptance; p2p reject messages (best-effort on modern dashd) report
Rejected, distinguishing txn-mempool-conflict (rejection) from
txn-already-in-mempool (acceptance) under the shared Duplicate code; a
configurable timeout reports Uncertain, upgradeable by a late echo.
Outcomes surface as SyncEvent::TransactionBroadcastResult, an awaitable
DashSpvClient::broadcast_transaction_and_wait API, and matching FFI
callbacks, config setters, and a blocking FFI wait function.
Rebroadcast keeps respecting the holdout while pending, re-picks
holdouts from never-sent peers on disconnect, skips rejected entries,
and sends deferred broadcasts as soon as a peer connects. Broadcast
state survives disconnects.
Verified with a new two-node dashd integration test: the transaction is
sent to one node and the withheld node's inv echo confirms acceptance;
a double-spend leg documents that dashd 23 sends no BIP61 rejects and
correctly resolves Uncertain.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(dash-spv-ffi): regenerate FFI_API.md for broadcast acceptance functions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(dash-spv): drop BIP61 reject handling from broadcast acceptance
Modern Dash Core removed the BIP61 reject message entirely (no
enablebip61, NetMsgType::REJECT, or reject codes remain in dashpay/dash),
and the two-node integration test confirmed dashd 23.1.0 sends nothing
for a refused transaction. The reject path was therefore dead code:
remove the Reject message subscription and handler, the
BroadcastResult::Rejected and BroadcastStatus::Rejected variants, and
the FFI reject fields (FFIBroadcastStatus is now Accepted=0/Uncertain=1,
the callback and FFIBroadcastResult lose reject_code/reject_reason, and
the result struct no longer owns strings so its destroy function is
gone).
A transaction the network refuses now uniformly surfaces as Uncertain:
no echo arrives within the acceptance timeout. The integration test's
double-spend leg asserts exactly that.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(dash-spv): address review feedback on broadcast await and FFI tests
- broadcast_transaction_and_wait no longer resolves on the manager's
interim Uncertain event: a late echo can still upgrade the outcome to
Accepted, so callers with a timeout longer than the configured
acceptance timeout keep waiting until their deadline. Uncertain is now
only returned on deadline expiry (or bus close).
- Add FFI unit tests for the broadcast config setters: zero
threshold/timeout are rejected with InvalidArgument without modifying
the config; valid values map onto the ClientConfig fields; holdout
half/count setters round-trip.
- Drop the obsolete "or rejection signal" wording from the timeout
setter doc (BIP61 reject handling was removed) and regenerate
FFI_API.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sets how many distinct non-recipient peers must announce a broadcast txid back before it is reported as accepted. Must be > 0. # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
268
+
269
+
**Safety:**
270
+
-`config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
Sets the timeout (in seconds) after which a pending broadcast with no acceptance signal is reported as uncertain. Must be > 0. # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
284
+
285
+
**Safety:**
286
+
- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
Withholds broadcast transactions from a fixed number of peers (clamped so that at least one peer always receives the transaction). # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
300
+
301
+
**Safety:**
302
+
-`config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
Withholds broadcast transactions from half of the connected peers (the default policy). The withheld peers are the source of the `inv` echo that proves a broadcast propagated through the network. # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
316
+
317
+
**Safety:**
318
+
- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call
319
+
320
+
**Module:** `config`
321
+
322
+
---
323
+
255
324
#### `dash_spv_ffi_config_set_data_dir`
256
325
257
326
```c
@@ -458,6 +527,22 @@ Broadcasts a transaction to the Dash network via connected peers. # Safety - `
Broadcasts a transaction and waits for its network-level outcome. Blocks until the network accepts the transaction (non-recipient peers announce it back, it is InstantSend-locked, or confirmed) or the timeout elapses (outcome `Uncertain`). `timeout_secs == 0` uses the configured acceptance timeout plus a small grace period. Requires mempool tracking to be enabled in the client config. # Safety - `client` must be a valid, non-null pointer to an initialized FFIDashSpvClient - `tx_bytes` must be a valid, non-null pointer to the transaction data - `length` must be the length of the transaction data in bytes - `out_result` must be a valid, non-null pointer to an FFIBroadcastResult
538
+
539
+
**Safety:**
540
+
- `client` must be a valid, non-null pointer to an initialized FFIDashSpvClient - `tx_bytes` must be a valid, non-null pointer to the transaction data - `length` must be the length of the transaction data in bytes - `out_result` must be a valid, non-null pointer to an FFIBroadcastResult
0 commit comments