Replies: 4 comments
|
Update: The proposal now includes a detailed local reputation system for Paymasters. Wallets track provider success rates, attributable failures, response latency, and temporary cooldowns locally. Neutral network or user-side failures do not affect a provider’s score, and no payment details or reputation data are shared publicly. Security and liquidity checks remain mandatory, while reputation is used only for local filtering and as a tie-breaker between equally priced offers. |
|
Update 2: The proposal now clearly defines automatic paymaster selection for headless workflows while keeping manual selection as the default. It also specifies backward-compatible RPC options, fee caps, idempotent payment sessions, safe fallback handling, Qt/CLI parity, and crash recovery. Feedback is welcome! |
|
Update 3: Improved Paymaster Privacy and Capacity Verification |
|
WiP: |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Concept Proposal: DigiDollar Paymaster Network - Send and Receive DigiDollar Without Holding DGB
Decentralized Fee Sponsorship with Verifiable Liquidity and No Consensus Changes
Status: Technical concept draft for discussion and a regtest prototype
Codebase: DigiByte Core
develop, reviewed against v9.26.5Consensus change: not proposed
1. Summary
The DigiDollar Paymaster Network enables a wallet to send DigiDollar even
though it holds no DGB for the network fee.
A payment remains a normal
DD_TX_TRANSFERtransaction:confirmed DD carrier input.
in full, and signs only its own inputs.
Paymasters are not privileged network participants. Participation is
permissionless through signed, short-lived announcements on the normal
DigiByte P2P network. The Core wallet initially displays only providers whose
identity and multiple dedicated admission slots have been verified locally.
For privacy, fresh reachability and endpoint authentication and the operational
payment-slot proof are performed sequentially only after a candidate has been
selected.
The same technical protocol supports three economic variants: public
fee-free community sponsorship, sponsorship restricted to a merchant or
application, and a public user-paid offer with an operator-selected percentage
price in DD. The wallet explicitly separates this funding source from the
technical Paymaster role. It calculates every user fee deterministically and
sorts only providers authorized for the specific request by the user's total
cost.
An extended, idempotent
senddigidollarcall can automatically select thelowest-cost usable provider within a hard DD fee cap—either always or only when
a complete funding preflight determines that the direct DGB fee path cannot be
funded. Qt and headless applications use the same persistent Core session for
this purpose.
The network is not inherently anonymous. As a co-signer, the selected provider
must know the complete transaction, and the final transaction remains publicly
analyzable. An optional high-privacy profile therefore minimizes network and
storage metadata without claiming a technically unattainable anonymity
guarantee.
2. Goals and Non-Goals
Goals
control over the user's DD.
Paymasters; pure zero-fee sponsors do not need an unused DD carrier pool.
user fee is offered, matching carrier UTXOs.
sponsorship, and transparent operator-selected percentage fees in DD.
transaction validity.
unavoidable for its signature; public discovery, logs, and persistent
history contain no additional payment profile.
isolation and no silent clearnet fallback.
Non-Goals
chainparams.DD output.
Paymaster.
descriptive sponsor labels do not establish legal status.
blockchain.
the existing onion/proxy and P2P v2 paths.
3. Technical Foundation in the Existing Code
The existing
TxBuilderTransferParamsalready separates DD inputs from DGBfee inputs:
See
src/digidollar/txbuilder.h.BuildTransferTransaction()adds the DD inputs first and the DGB inputsafterward (
src/digidollar/txbuilder.cpp).Consensus validation:
or 100,000 DD, per DD output,
See
src/consensus/digidollar.handsrc/digidollar/validation.cpp,src/digidollar/validation.cpp.V1 deliberately limits Paymaster clients and providers to non-pruned nodes
with
-prune=0,-txindex=1, and complete DD-creating transaction data. DDvalidation preferentially obtains amounts from the creating transaction through
txindex and then tries the block database or metadata registry
(
src/digidollar/validation.cpp);Core makes pruning and txindex incompatible
(
src/init.cpp). There is therefore nosufficiently demonstrated general pruned-node path for every collaborative
PSBT and restart case. Pruned support remains a later, general DD-index
extension with its own reindex/migration strategy and is not a Paymaster
consensus change.
Privacy requires one additional builder extension. The current transfer
parameters have no separate DD change destination
(
src/digidollar/txbuilder.h); thebuilder currently derives DD change from
spenderKey(
src/digidollar/txbuilder.cpp).The collaborative path therefore adds an explicit
ddChangeDestfreshlyreserved from the wallet's internal keypool. Fallback attempts in the same
session reuse the already authorized destination.
Today's logging is also unsuitable for Paymaster metadata: the builder writes
fee outpoints, recipients, amounts, and DD change to the general log
(
src/digidollar/txbuilder.cpp).The existing broadcast/Dandelion path also logs TXIDs and precise relay times
(
src/node/transaction.cpp).Before the Paymaster path is enabled, these messages must be removed or reduced
to non-correlatable state and error codes.
The current wallet path is nevertheless not multi-party capable. It selects DD
and DGB from the same wallet, signs the entire transaction, and broadcasts it
immediately
(
src/wallet/digidollarwallet.cpp,src/wallet/digidollarwallet.cpp).SignDDInputs()also expects all previous transactions to be present in thesame
mapWallet. A new wallet path that builds unsigned transactions and signsselectively is therefore required.
4. Architecture Decision
The security-critical functions remain in Core:
For V1, the sender wallet and the Paymaster require an updated Core version.
Recipients, miners, and other full nodes do not need Paymaster functionality to
validate the completed transaction under the existing DD rules. P2P nodes that
cache or relay
pmannouncedo require the new non-consensus relayimplementation; old nodes ignore these messages.
An operator does not necessarily need the Qt interface; an updated
digibytedwith a dedicated wallet is sufficient.
5. Provider Discovery on the P2P Network
5.1 P2P Discovery
Primary discovery uses new, non-consensus messages on the normal DigiByte P2P
network:
sendpmastersenables reception and relay; nodes with no interest do notneed to cache or validate Paymaster proofs.
pmannounceis gossiped and cached for a limited time.getpmasterssynchronizes the cache after a new peer connection.and PSBTs are exchanged exclusively over a direct connection to the selected
provider and are never gossiped; the announcement contains only the bounded
admission proof.
fresh client nonce using its announcement identity key. Intent data is
transmitted only after this binding between endpoint and provider ID.
existing encrypted P2P v2 transport. The codebase can already negotiate this
transport but does not currently enable it by default
(
src/net.h). A failed v2 setup does not fall backto v1 for a Paymaster session.
Paymaster connections use the configured proxy with randomized credentials
per connection; Core already supports this Tor stream isolation through
-proxyrandomize(src/init.cpp). An onionfailure must not cause a silent clearnet fallback.
(
src/net_processing.cpp).Cold start uses already connected, updated P2P peers. For tests, private
networks, and early rollout, a provider can additionally be configured directly
with
-addpaymaster=<address>. Even then, a provider is displayed only if itpasses cryptographic and local validation. Manual configuration may bypass
only discovery and admission display filters, never quote, slot, signature,
fee, prevout, policy, or full transaction validation.
5.2 Signed Announcement
Every operator has a long-lived identity key that is separate from the funding
keys:
offersis limited to at most four entries. Each entry is a complete publicoffer identified unambiguously by
offer_idandpolicy_hash.USER_PAIDuses the percentage fee in Section 7;
SPONSOREDcharges the user exactly0 DD.PUBLICmeans any matching request may ask for the offer;RESTRICTEDnever appears in this list. Terms such as community, merchant, orapplication are only signed display metadata and make no security or legal
assertion.
capability_flagssignals, among other things, P2P v2 transport and an onionservice. The display name is optional and contains no URL, email address, or
payment address. Core loads no external logos or other tracking resources.
PaymasterIdis the hash of the identity key. The signature is domain-separatedand binds at least the protocol version, genesis hash, display name, endpoint,
policy, complete admission proof, sequence, and expiration time. An endpoint or price change requires a
new sequence and signature. The display name is not unique; the GUI therefore
always also displays a shortened fingerprint of the provider ID.
To prevent gossip from being flooded by identities that can be created at no
cost, every relayable announcement contains a bounded admission proof for
exactly the client-side minimum capacity. Under the proposed default, this
consists of three disjoint DGB admission slots and, for every announced
USER_PAIDoffer, three associated admission carriers, each with a currentreference block and control signatures. This dedicated admission reserve is
never used in a user payment or as an operational quote slot. The Paymaster ID
therefore remains linked to the minimum reserve, but global gossip publishes no
operational payment UTXOs. Relay nodes validate the proof under strict CPU and
byte limits before caching or forwarding an announcement.
The admission proof's control signatures use a separate domain such as
"DigiByte Paymaster Admission v1"and bind the provider ID, announcementsequence, reference block, outpoint, and expiration time. They are not
interchangeable with the later, client-bound capacity proofs.
A provider that wants to dissolve an admission reserve first ends its
announcement. Those UTXOs are nevertheless never used in Paymaster payments;
a later spend can still publicly link the source of funding. The design makes
no claim of complete invisibility.
Suggested non-binding limits:
Where possible, the directory view is aggregated from multiple outbound peers
in different netgroups. This does not prevent a complete eclipse attack, but it
reduces dependence on a single relay. Automatic direct connections accept only
addresses that are routable under existing Core network policy and respect
proxy and Tor settings. Loopback, private, link-local, and other internal
destinations are permitted only with an explicit local
-addpaymasterconfiguration.
Public directory synchronization opens no direct connection to every provider
and requests no operational capacity snapshots in advance. Only the locally
selected candidate receives a direct, isolated session. Before the payment
intent, it may prove at most one operational candidate slot; this preliminary
proof does not yet guarantee funding for the unknown final transaction
template. After the intent, the quote checks the actual transaction weight and
proves and reserves exactly that slot with sufficient DGB funding. No client
receives the complete hot-wallet pool.
5.3 Restricted Merchant and Application Sponsorship
A restricted sponsorship offer is not gossiped globally. A payment request,
QR code, or local application instead supplies the wallet with a signed,
short-lived service descriptor containing the
provider_id, a routable P2Pendpoint,
offer_id,policy_hash, sponsor display name, authorization scheme, chainbinding, and expiration. The descriptor may pin a specific Paymaster. The
wallet then authenticates the endpoint exactly like a publicly discovered
provider and performs the same UTXO, quote, and transaction checks.
The provider signature approves the endpoint, concrete offer ID, policy, non-gossiped admission
reserve, and permitted sponsor authorization key. The wallet validates this
proof like a public offer. The payment-specific capability is signed by that
sponsor key. A display name and policy hash alone are never accepted as
authorization.
Any sponsorship authorization is an opaque, signed capability bound at least
to the genesis hash, provider ID, offer ID, policy hash, the single V1 recipient, amount,
payment-request nonce, and expiration. The Paymaster protects it against reuse.
Before issuing the quote it persists a
SponsorshipAuthorizationRecordwiththe capability hash, canonical payment binding, and state
RESERVED, but neverthe secret capability itself. At final provider commit, the same wallet
database transaction atomically changes it to
CONSUMED. The secretcapability is never announced or gossiped, written to debug output, or stored
persistently in plaintext; the intent, quote, and session retain only its hash.
Core accepts no arbitrary web URL from a payment request, only a
CServiceroutable under existing network policy or an explicitly locally permitted
address.
Public community and user-paid offers continue to use the discovery in
Section 5.1. A sponsor may arrange settlement with a merchant or application
outside Core; the user's DD service fee and Paymaster output remain exactly
zero. Core maintains neither sponsor accounts nor prepaid balances.
6. Mandatory Carrier and Liquidity Proof
6.1 Wallet Eligibility Rather Than a Consensus Rule
Multiple confirmed DGB slots are a prerequisite for automatic display in
Core. Sub-1-DD carrier support is additionally mandatory whenever a provider
offers a user-paid DD fee. This is deliberately a wallet policy:
The proposed default value is:
Local policy may raise, but not lower, the V1 discovery floor of three slots;
it is nevertheless not a consensus constant. Manual sponsorship remains
possible, and transaction validity never depends on this value.
6.2 A Liquidity Slot
A slot is the smallest unit that can be reserved in parallel:
For an admission slot,
schnorr_signaturecovers"DigiByte Paymaster Admission v1" || genesis_hash || provider_id || announcement_sequence || proof_block_hash || outpoint || script_pub_key || amount || confirmation_height || expires_at. The operational capacity proofinstead uses the challenge in Section 6.3. In either case, the outpoint and
script in
UtxoControlProofmust exactly match the enclosing verified objectand actual prevout, and its expiration may not exceed that of the enclosing
proof.
A user-paid operational slot contains:
verified_parallel_slotsis the provider-local number of complete, pairwisedisjoint operational slots. Their outpoints are not published in the
announcement. A large DGB UTXO or a single DD carrier counts as only one slot
because it cannot be reserved for multiple quotes at the same time. Admission
and operational outpoints are completely disjoint. Every DGB admission UTXO
contains at least
MIN_ADMISSION_DGB_SATOSHIS; a small dust UTXO does not countas admission liquidity. In V1, all carrier and provider DGB inputs must be
dedicated P2TR key-path outputs.
6.3 Capacity Challenge
The admission proof limits Sybil and gossip spam but proves neither the
operational pool nor exclusive or second-by-second availability. Before
displaying a provider, the wallet validates the three published admission
slots against its own chain and mempool view. It does not contact every
provider for this purpose. Only after price, policy, and local cooldown select
a candidate does it request from exactly that provider a fresh client-bound
proof for at most one operational candidate slot:
For every disclosed carrier or DGB UTXO, the operator additionally provides a
domain-separated Schnorr control signature over:
Core constructs the tagged hash to be signed itself from the fully parsed
fields. It must never pass an arbitrary 32-byte hash supplied by the network to
a wallet key for signing. This prevents a capacity request from being abused to
produce a signature for another protocol.
For V1, Paymaster reserve UTXOs must be held on dedicated P2TR keys. Core can
then validate the control signature directly against the x-only output key. The
identity key additionally signs the entire snapshot and binds all UTXO proofs
to the same provider.
The client wallet validates locally that:
local mempool.
that created it and that transaction's OP_RETURN, not from a value reported
by the provider.
DGB admission UTXO contains at least 10,000,000 satoshis.
valid.
across multiple provider identities.
The client enforces at least its own confirmation policy. A lower
min_confirmationsvalue published by the provider can never reduce it.The proof demonstrates current existence and key control, but neither global
exclusivity nor funding for the final transaction template, which is still
unknown at that point. Only an atomic quote checks its actual weight and
reserves exactly the disclosed slot with sufficient DGB funding. Before that
quote, the UI therefore says "Admission reserve verified"; afterward it
says "Payment slot reserved", never "capacity guaranteed."
7. Fee Model and Ranking
The transaction protocol remains identical for all funding models:
Protocol fields use only the objective values
SPONSOREDorUSER_PAIDandPUBLICorRESTRICTED. A restricted offer is usable only after its concretesponsorship authorization has been validated and bound to the payment request.
A Paymaster funded externally by a sponsor may not add another DD output or
carrier increase at the user's expense.
7.1 Deterministic Percentage Fee Only
The operator chooses a fee in increments of 0.1 percentage points, for example
0.5%,1.0%, or1.2%. The protocol never uses floating-point numbers. Therate is stored in basis points:
For V1,
MAX_PAYMASTER_RATE_BPS = 10000, andfee_rate_bpsmust also be amultiple of 10. The fee is rounded up to the
next whole DD cent using checked integer arithmetic:
At
0 bpsand for everySPONSOREDoffer, the user fee is exactly 0. V1 hasno hidden fixed amount and no
undisclosed minimum fee. A user-defined fee cap is local wallet policy, not a
network rule.
Rounding example: 1 DD at 0.5%
Because DD can be accounted for only in whole cents, the result is:
The 1-cent fee requires a carrier whose value increases, for example, from
1.00 DDto1.01 DD. The user wallet must provide suitable DD inputs worthat least
1.01 DD. As described in Section 8.1, any resulting user change mustbe either zero or at least
1.00 DD.In this case, the wallet displays the announced
0.5%rate, the rounded0.01 DDfee, the effective1.0%rate, and the total1.01 DDcharge. Thisprevents the unavoidable cent rounding from appearing as a hidden minimum fee.
7.2 Local Ranking
Public announcements contain the rate and supported payment range. The wallet
can therefore calculate the costs locally without sending the amount,
recipient, or user UTXOs to every paymaster:
The wallet first applies the security filter:
policy,
transport capabilities; this is not yet a fresh reachability claim,
Only then are providers sorted in ascending order by
user_total_cents. Ifprices are equal, the tie-breakers are:
The comparison list is a locally calculated, admission-verified shortlist, not
proof of the operational pool and not yet a UTXO reservation. Exactly one
operational candidate slot and then a specific quote are requested only for
the selected row. If its fee does not exactly match the fee calculated from the currently
ranked signed announcement, or no slot can be reserved, the wallet discards it
and tries the next candidate using the same user input set. This avoids
disclosing the complete payment details to every provider merely to compare
prices.
7.3 Local Reliability Assessment
A provider's reputation is exclusively local wallet policy. It is neither
published nor gossiped and affects neither consensus nor cryptographic
verification. It is stored under the
PaymasterId, not under the display nameor endpoint:
The reservation manager records at most one terminal outcome per
ProviderAttempt, keyed by its stable attempt ID; a session with severalprovider attempts may therefore update several providers. Only locally
verifiable events count as provider failures:
No response is not an unambiguously attributable protocol violation. It only
increments
availability_timeoutsand may trigger a short local reachabilitycooldown; it does not reduce the correctness rate. User cancellation, local
wallet or signing failures, reorganizations, and network or pool failures that
cannot be attributed unambiguously are recorded as neutral. Success is recorded
after local stempool or mempool acceptance; confirmation and reorganization
remain traceable as session states.
latency_ewma_msmeasures only successful request/response times within anauthenticated provider session, not confirmation time or general network
latency.
By default, at least five locally assessable sessions are required before a
success rate is displayed. Until then, the UI shows "Insufficient local data
so far." Repeated attributable failures trigger a temporary cooldown with
exponential backoff and a local upper bound. Statistics use 30 UTC daily
buckets; the oldest bucket is discarded on rollover. Older observations thus
lose weight without retaining individual payment records. All thresholds are
wallet configuration, not network parameters.
The selection order remains unambiguous:
An identity change starts with no local history; positive reputation is never
transferred to a new key. Only aggregate counters, timestamps, and cooldown
state are persisted—never recipients, amounts, user outpoints, or other payment
details. The user can inspect and clear the local history at any time.
A stable provider ID improves reputation and Sybil resistance but also makes
the operator recognizable over time. V1 therefore never transfers reputation
to a new or "rotated" key. An operator may deliberately start with a new
identity and onion service, but then starts without history; a cross-signature
would restore the link that the rotation intended to avoid.
7.4 Automatic Selection for Headless Operation
Automatic selection is a Core function, not merely a matter of calling
getpaymasteroffersand using its first entry. Qt,digibyte-cli, andthird-party software use the same selection routine in
PaymasterClient. Thesecurity filters, fee cap, input reservation, and fallback rules therefore
apply identically regardless of the interface.
Among the locally known signed public announcements and explicitly supplied
restricted service descriptors, the lowest-cost usable paymaster is the
provider with the lowest exactly rounded
user_total_cents. Core first sortswithout direct contact and then checks candidates strictly sequentially. The
first candidate in cost order that also passes endpoint authentication,
reachability, capacity, and quote validation is the lowest-cost currently
usable provider. The following checks apply to the specific payment:
endpoint, and admission proof are valid and fresh,
restricted offer has a valid payment-bound sponsorship authorization,
current candidate then discloses one controlled operational candidate slot,
payment + service_feewith valid DD change,
weight, reserves the suitable provider slot, and passes full revalidation.
Authorized zero-fee offers therefore have the lowest numerical total cost.
Manual selection remains the default in Qt. Only an explicit automatic policy
may choose the lowest-cost usable provider across all accepted models. A
provider pinned by a payment request is not silently replaced by a public one;
that requires the same explicit fallback authorization as any other pinned
provider ID.
The lowest percentage rate alone is insufficient because cent rounding and
transaction constructibility can change the result. When exact total costs are
equal, the tie-breakers in Section 7.2 apply.
getpaymasteroffersreturns only a locally sorted snapshot, not a priceguarantee or reservation. If the price, slot, reachability, or chain state
changes before a quote is obtained, Core rejects that candidate and tries the
next eligible provider sequentially. Core never sends parallel quote requests
for the same payment. After an ambiguous timeout, however, an old
provider-local reservation may remain until its short TTL expires while the
next sequential attempt starts;
maximum_provider_attemptsbounds the numberof such attempts.
7.5 Optional Privacy Selection
lowest_total_costremains the unambiguous default for automated workflows.The user may additionally opt into
privacy_weighted. Core first determinesthe lowest exact total price as before and then forms a group containing only
fully eligible offers up to
Core selects uniformly from that group using its CSPRNG; active cooldowns and
all security filters apply unchanged before selection. The tolerance is a hard
user limit and defaults to 0, making both methods identical. The high-privacy
profile sets
maximum_provider_attempts = 1and disables automatic providerswitching. Every additional attempt requires separate authorization because a
second provider learns the same inputs, recipient, amount, and closely
correlated time.
This option reduces permanent concentration of all payments at the cheapest
provider. It promises no on-chain anonymity and replaces neither Tor nor local
transaction verification.
7.6 Example for 10 DD
The DGB miner fee is displayed separately as “paid by the paymaster” and is
never combined with the DD service fee. The current builder uses at least 0.1
DGB for DD transactions
(
src/digidollar/txbuilder.cpp); thequote must check the DGB amount actually required based on the final size.
For
USER_PAID, the quote must exactly match the fee calculateddeterministically from the signed terms of the selected
offer_id. ForSPONSORED, both the rate and protocol fee in those terms are fixed at zero. A higher or lower rate for the applicable model, an additional fixed amount,or different rounding causes local rejection. A provider that wants to offer a
lower price first publishes new signed offer terms; the wallet can therefore
determine the complete local ranking without querying every provider for a
price.
8. Sub-1-DD Carriers
8.1 Transaction Model
A separate DD output of 0.05 DD would be invalid. Instead, the paymaster
contributes an existing carrier and receives it back with the fee added.
Example:
All outputs satisfy the 1-DD minimum. The fee is the verifiable difference:
The builder selects user DD for
payment + service_fee. User change must beexactly zero or at least 100 cents. A change amount between 1 and 99 cents
makes the template invalid and requires a different input selection. The
recipient, user change, carrier successor, and normal Paymaster fee outputs
must also each be at most 10,000,000 cents. A carrier whose successor would
exceed that limit is unusable for the quote.
8.2 When the Carrier Is Used
fee = 0: no carrier is required in the payment.1 <= fee < 100 cents: a carrier is mandatory.fee >= 100 cents: a normal, separate paymaster DD output is used; nocarrier is required for this transaction.
Nevertheless, every
USER_PAIDoperator displayed automatically by the walletmust prove the mandatory carrier pool at all times. A pure
SPONSOREDoperatorcharging exactly 0 DD instead proves only the required DGB slots.
8.3 Pool and Concurrency
A spent carrier's successor is initially unconfirmed and therefore cannot be
reused immediately. One carrier consequently represents exactly one parallel
slot. The provider holds multiple confirmed carriers and appropriately split
DGB UTXOs in a dedicated hot wallet.
Sub-1-DD fee revenue remains locked in the carrier until it can be extracted as
a valid output of at least 1 DD during a pool redistribution.
8.4 Limiting On-Chain Clustering
Carrier chains remain publicly traceable; wallet policy can only mitigate this
fundamental property. The operational pool therefore uses a fresh internal
P2TR key for every carrier successor and every DGB change output, selects
confirmed carriers cryptographically at random, and does not reuse a successor
immediately after its first confirmation. Carriers may have different valid
starting values so that every pool does not create the same
1.00 → 1.xx DDsignature.
Each slot preferably holds exactly one sufficiently large DGB fee UTXO;
multiple DGB inputs are allowed only when the concrete network fee cannot
otherwise be funded. The dedicated Paymaster wallet does not mix the
operational pool with private or otherwise used operator funds and does not
deterministically consolidate revenue to the same address after every payment.
These rules make heuristics harder but hide neither the carrier difference nor
the public transaction.
9. Payment-Specific Quote
9.1 Payment Intent
The wallet selects a provider only after local ranking. Only that provider
receives an intent:
V1 permits exactly one recipient per
PaymentSession; batch payments are notpart of V1. The payment amount used by the fee formula, quote, display, and
capability is exclusively
recipient.amount_dd_cents, so no redundant totalamount field can diverge.
The control proofs prevent an attacker from reserving arbitrary provider slots
using other parties' public DD outpoints. They are domain-separated message
signatures, not transaction signatures. Core first calculates an
intent_core_hashover every intent field exceptuser_input_proofs. Eachproof signature binds
"DigiByte Paymaster User Input v1" || genesis_hash || provider_id || client_nonce || intent_core_hash || outpoint || expires_at; it therefore cannotbe reused with another provider or intent.
accepted_fee_rate_bpsandmaximum_fee_dd_centsare expressly excluded fromthe transmitted intent. Both remain in the local authorized
PaymentSession;the provider does not need the user's willingness to pay in order to apply its
publicly signed rate. Core rejects the quote locally if it violates the rate or
fee cap.
For public community sponsorship,
sponsorship_authorization_hashmay bezero; for
RESTRICTED, a payment-specific capability is mandatory. In bothcases,
policy_hashidentifies the sponsorship policy signed by the provider.9.2 Quote
The provider atomically reserves a specific slot and responds:
The quote binds the chain, intent, concrete offer, funding model, scope, policy
and authorization hashes, exact fee, every prevout and provider output, the
complete unsigned transaction, and both expiration times.
unsigned_txidisthe hash of that witness-free transaction.
template_commitmentis adomain-separated tagged hash over the canonical payment intent or
intent_hash, the canonical quote projection excludingtemplate_commitmentand
identity_signature, the canonical unsigned transaction, every actualprevout value and script, the input roles, and the exclusively permitted
SIGHASH_DEFAULT, plus the creating TXID and verified DD amount for each DDinput. This binds, in particular, the genesis, provider, offer and
quote IDs, policy, funding model, and fee without creating a recursive hash
definition. It is
the only template identifier used by the rest of the protocol. The provider
may assign a given slot to only one active quote.
For a user fee from 1 through 99 cents,
reserved_carrierandcarrier_return_scriptmust be present andprovider_fee_scriptabsent. At100 cents or more, the carrier is absent and
provider_fee_scriptcontains thefresh P2TR fee destination. At zero fee, all three fields are absent. A
dgb_change_scriptis present only when a valid DGB change output is actuallycreated.
For
SPONSORED,fee_rate_bpsandservice_fee_dd_centsmust be zero andreserved_carrier,carrier_return_script, andprovider_fee_scriptmust beabsent. The wallet rejects every sponsored quote
that nevertheless charges a DD service fee or adds a Paymaster DD output. The
actual sponsorship authorization is transferred to the Paymaster only inside
the authenticated direct session; the quote hash then proves which
authorization was checked without exposing the capability in the transaction
or gossip.
10. Secure Signing and Broadcast Flow
change address.
ranked locally without direct provider contact.
provider first proves exactly one operational candidate slot. Only that
provider then receives the intent; the quote checks its actual weight and
reserves the proved slot with sufficient DGB funding.
transaction from the intent and quote itself or reconstructs it
deterministically byte for byte.
carrier difference, DGB change, miner fee, locktime, sequences, and unknown
outputs are checked.
provider ID, fee cap, fallback policy, and expiration time. In headless
operation, the authenticated canonical RPC request with its
request_id,hard fee cap, and bounded fallback policy replaces the dialog; it authorizes
only those exact limits.
Taproot
SIGHASH_DEFAULT. NoANYONECANPAY.template_commitment, valid user signatures, and UTXOs that remainreserved.
are signed.
testmempoolaccept, and commits one wallet-database transaction containingthe
ProviderCommitRecord, final raw transaction, TXID and raw-transactionhash, quote and idempotency indexes, all provider outpoints, and the atomic
slot transition from
RESERVEDtoCOMMITTED. Where restrictedsponsorship is used, the same database transaction changes its
SponsorshipAuthorizationRecordfromRESERVEDtoCONSUMED. Any writefailure aborts the entire database transaction. Only after a successful
commit does the provider pass that exact transaction to the existing
BroadcastTransactionpath. This preserves the default-enabled Dandelion stem relay(
src/net.h,src/node/transaction.cpp). Theuser receives the TXID, final raw transaction, and status in
PMRESULT.Why the Provider Signs Last
A transaction signature has no cryptographic expiration date. If the provider
signed first, a client could withhold the partially signed transaction for a
long time and broadcast it later at the provider's expense. Having the
provider sign last minimizes this risk and enables a final check immediately
before broadcast.
This UTXO protocol requires no separate bundler or trusted broadcaster role.
Optionally, the provider may send the already fully signed raw transaction to
a stateless relay service and the normal DGB network at the same time. The
relay may neither add inputs nor alter the quote, signing order, or provider
responsibility and is not a V1 requirement.
The provider treats
provider_id || client_nonce || intent_hash || quote_id || template_commitmentas aunique commit key.
retry_samemust reserve neither a new slot nor create a newquote or transaction variant. If a final commit already exists, the provider
returns exactly the same raw transaction and TXID and rebroadcasts it when
needed. If only an earlier state is stored, it reports that state unchanged. A
different request under the same key is rejected.
If no
ProviderCommitRecordexists,retry_samemay additionally retransmitthe exact user-signed PSBT persisted by the client for that
ProviderAttempt.The provider processes it under an exclusive lock for the commit key and
accepts it only when:
template_commitmentexactlymatch the persistent attempt,
its TTL expired, can be atomically rebound to that key without having been
spent or assigned to another attempt.
After successful verification, the provider atomically persists a
UserAuthorizationRecordcontaining the commit key, canonical hash of theuser-signed PSBT, and state
USER_PSBT_ACCEPTEDbefore signing its inputs. Ifthat record already exists, the hash must match. A crash before this write is
treated like a first receipt that was never accepted; the retransmitted PSBT
undergoes the same complete verification. A crash afterward resumes exactly
the same attempt.
retry_untildefaults to 24 hours after quote creation. Until that time, theprovider retains the quote, slot, and authorization state needed by
retry_same. Afterward, it replaces a non-finalized attempt with a rejectingidempotency tombstone and accepts no new finalization under that commit key.
This is a provider-retention rule and never evidence that lets the client
wallet release its inputs safely. Only local mempool, stempool, chain, and
input-spend reconciliation can resolve that risk.
Existing Core primitives support this flow: PSBTs can be serialized and
decoded again
(
src/external_signer.cpp,src/psbt.h); underlying transactions can becompared and input signatures fully checked through
PSBTInputSignedAndVerified()(
src/psbt.cpp,src/psbt.cpp). The canonical hash is calculatedonly after parsing, size checks, and removal or rejection of disallowed
metadata from the canonical normalized protocol projection, never from an
unvalidated network blob. Equality of a retransmitted PSBT means equality of
this canonical projection, not necessarily byte-for-byte equality of an
arbitrary serialization. Before persistence the client finalizes each of its
own inputs individually, leaves every provider input unsigned and unfinalized,
and verifies all user signatures with
PSBTInputSignedAndVerified().Retransmission is not a new authorization, quote, reservation,
ProviderAttempt, or transaction variant and requires no new user signature.If the original slot is no longer usable, the provider must never replace it
with different outpoints under
retry_same; it reportsSLOT_UNAVAILABLE, and the client sessionremains pending until a safe same-input resolution occurs.
Before a successful
ProviderCommitRecord, only the unsigned transaction,template_commitment, and user signatures are immutable. If the provider hasto sign again after a crash, its witness and therefore the raw-transaction hash
may differ only when no previous final variant was stored or published. From
FINAL_COMMITTEDonward, raw transaction, witness, TXID, and raw hash areimmutable and must be returned exactly.
PMRESULTis domain-separated and signed with the provider identity key andbinds every field.
result_sequenceincreases monotonically per commit key;the client ignores regressions. From
FINAL_COMMITTEDonward, TXID, raw hash,and the complete final transaction are mandatory. The client accepts them only
after revalidating TXID, template, user and provider signatures, and every
output.
raw_transaction_hashis the witness-inclusiveGetWitnessHash()ofthe final serialized transaction, while
txidis its witness-freeGetHash().Size limits apply before signature verification. Provider-reported
status never substitutes for local stempool, mempool, or chain observation; a
bare broadcast claim does not end
PENDING_PROVIDER.Dandelion makes attribution of the first normal P2P relay more difficult, but
it hides the transaction from neither the Paymaster nor the blockchain and
does not replace onion transport for the intent and quote.
Safe Fallback Rule
A user signature already provided does not technically expire with the quote
either. After an ambiguous provider failure, the wallet must therefore not
simply choose new DD inputs for the same payment. All fallback attempts use the
same user input set. Old and new provider variants consequently compete for
the same DD inputs, and no more than one payment can be confirmed.
The reservation manager selects and atomically reserves this user input set in
the wallet database before sending the first payment-specific network request.
Normal, automatically selected, and
selected_inputs-preset DD selection pathsmust all respect active paymaster reservations. No wallet lock is held during a
P2P or endpoint request. Fallback attempts are strictly sequential; for every
new provider fee, DD funding and valid change are checked again using the same
input set.
Before the first user signature, Core additionally performs a purely local
recovery preflight. Using the already reserved input set and signed public fee
formulas from signed announcements or explicitly supplied service descriptors,
it simulates every permitted fallback variant and rejects candidates
for which payment, service fee, and valid DD change cannot be represented
exactly. It also simulates
cancel_to_selfand checks whether the wallet canfund the DGB fee itself. No additional provider is contacted, no operational
slot is requested, and no additional input or signature is reserved.
The result is an advisory snapshot, not an availability guarantee: "Safe
fallback candidates: N" and "Self-funded recovery available/unavailable."
Quotes, UTXOs, and reachability may change later; every actual recovery still
requires complete revalidation and authorization.
A session remains visible as
PENDING_PROVIDERuntil:it then moves to
CANCEL_MEMPOOL, not yet toCANCELED_SAFE.The UI must not misrepresent a mere quote timeout as final revocation of an
authorization that has already been signed.
After a user signature has been transmitted, Core reconciles the session,
mempool, chain, and user-input spend status before every further attempt. An
ambiguous state remains
PENDING_PROVIDER. When apaymaster_idwas explicitlypinned, Core does not switch identities by default; the caller must expressly
authorize this through the
allow_provider_fallbackoption.Every fallback discloses the same user input set, recipient, amount, and a
closely correlated time to another operator. Qt explicitly displays this
disclosure before obtaining later authorization. The high-privacy profile
therefore allows only one provider attempt by default; the security-critical
same-input rule is not weakened in favor of new variants that could more
easily produce duplicate payments.
Recovery UX for
PENDING_PROVIDERPENDING_PROVIDERmeans a wallet reservation caused by a signature that maystill take effect, not a consensus-level lock of the UTXOs. The send view
therefore displays the last contact, affected inputs, current mempool/chain
finding, and three state-dependent actions:
session, local mempool, stempool, chain, and input-spend status again. This
read-only action requires no new signature.
input set, recipient, and recipient amount. New provider components and fees
are rebuilt and fully analyzed, then authorized and signed again after the
additional disclosure has been shown. The successfully propagated fallback
variant is itself the conflict transaction against the old variant.
transaction that returns every reserved user DD input to fresh internal DD
destinations of the same wallet. The return is deterministically split into
the minimum number of outputs, each between 100 and 10,000,000 DD cents; no
remainder from 1 through 99 cents is permitted. If the exact amount cannot
be represented under these rules, recovery preflight rejects that variant.
It uses the same input set and requires a DGB fee from
the user's wallet or an explicitly authorized recovery Paymaster. The user
reviews and signs it like any other payment; Core broadcasts and rebroadcasts
it through the normal
BroadcastTransactionpath. If the recovery Paymastercharges a DD fee, it is visibly deducted from the returned amount and is
subject to the same fee cap.
A recovery Paymaster counts as an additional provider attempt and receives the
recovery transaction data. It is therefore not automatically permitted by the
high-privacy profile. Without own DGB, the user must either continue waiting
for the original provider or explicitly authorize the profile change and
additional disclosure; Qt does not present this as a risk-free cancellation.
Release inputs is not an available action after a user signature has been
transmitted. It is offered only when Core can prove that no signature was sent
or a final spend is already known. A cancellation variant in the local mempool
improves practical availability but is not final: remote mempools may know a
competing variant and mempool entries can disappear. Until confirmation, Qt
therefore displays “Cancellation sent—confirmation pending”; only a
confirmed spend of the same inputs leads to
CANCELED_SAFEorCONFLICTED.Core claims neither BIP125 replaceability nor guaranteed displacement of the
old variant. Whichever valid variant confirms first determines the payment
outcome. Before the return transaction is signed, Qt therefore warns explicitly
that the original payment may still confirm. A recovery call is idempotent by
request_idand repeated calls create no additional cancellation variant.11. PSBT and Wallet Extensions
11.1 New Collaborative Builder
In
src/digidollar/txbuilder.h/.cpp:Requirements:
CKeyobject in the builder,reserved internal
ddChangeDest, never implicitly from the signing key,GetDustThreshold()instead of a fixed1000-satoshivalue,
PSBTs, or raw transactions; logs contain only local, non-payment state and
error codes.
11.2 DD-Capable Partial PSBT Flow
In
src/wallet/digidollarwallet.h/.cpp:Rules:
wallet ownership; roles or input positions are never trust sources.
complete=falseis a normal intermediate state.available.
created it.
transport aids only and are never authoritative.
The generic
combinepsbt,finalizepsbt, andsendrawtransactionRPCs can bereused. Transactions that created DD inputs, or verified DD metadata, must not
be removed during PSBT size optimization while another participant still
needs them for independent verification.
11.3 Persistent Reservation Manager
New module
src/paymaster/reservation.h/.cpp, persisted through the walletdatabase:
PENDING_PROVIDERremains the stable external umbrella state but has aninternally persisted phase:
The phase determines permitted recovery. With a known raw transaction, Core
can rebroadcast exactly that variant itself; without a final artifact,
retry_same, same-input fallback, or recovery remain available. No substatereleases inputs merely because time has passed.
STEMPOOLtransitions toMEMPOOLat fluff relay; a transaction may also beaccepted directly into the local
MEMPOOL. Neither state is final. Evictionfrom either pool returns a known final transaction to
PENDING_PROVIDER/PENDING_NETWORK; a reorganization moves a previouslyconfirmed session back according to local pool and spend observations. If a
payment variant confirms, the session becomes
CONFIRMEDand every recoveryvariant becomes
CONFLICTED. If self-recovery confirms, it becomesCANCELED_SAFEand all payment attempts becomeCONFLICTED. A confirmedunrelated spend of the bound user inputs results in
CONFLICTED.CANCEL_MEMPOOLremains a non-final substate ofPENDING_PROVIDERuntilconfirmation.
The unique key within the applicable wallet database is
request_id; thewallet itself already provides the namespace. V1 requires a canonical
lowercase, 36-character representation of a 128-bit UUID.
canonical_request_hashis an immutable value stored in the session record. Aconcurrent insert with the same ID atomically joins the existing session when
the hash matches and is rejected otherwise.
At a minimum, the hash binds the genesis hash, the single recipient and amount, any preset
inputs, and all authorization-relevant options after defaults are resolved. It
is calculated from typed, parsed, canonically serialized values, never from raw
JSON; semantically equivalent amount representations therefore produce the
same hash. Internally selected inputs are subsequently bound immutably to the
session as well.
The
PaymentSessionretains the payment request, authorized effective fee cap,and user input set selected once. It exists even when
autoselects the directDGB path. During the safety-relevant lifetime, every contacted provider
receives a separate, append-only
ProviderAttempt. State transitions add the provider ID, nonce,complete signed quote, exact template, user-signed PSBT, and—once available—the
provider-signed final transaction; once set, an artifact is never overwritten.
The user-signed PSBT is persisted before it is transmitted to the provider.
Every fallback builds a new complete transaction variant from the same user
input set, analyzes it again, and requests a new user signature. Because
SIGHASH_DEFAULTis used, a signature must never be transferred to a variantwith different provider inputs, outputs, or fees. Earlier attempts remain
stored until final conflict and duplicate-payment resolution and are not
overwritten by the new attempt during that period.
QUOTE_EXPIREDends only the individual provider attempt. Once the usersignature has been transmitted, neither quote expiration nor a timeout ends
the user authorization or input reservation; the parent session remains
PENDING_PROVIDER.failedis final only when Core can prove that no signed orstarted commit can still take effect. An ambiguous state is never
failedanddoes not release the user inputs.
The following are reserved atomically:
fee_mode_used: "dgb"is fixed,template_commitment, expiration time, and expected successors.After the
autopreflight, Core atomically transfers the planned inputs intothe session reservation under the same wallet synchronization and persists
fee_mode_usedbefore contacting a provider or starting direct signing. Aresumed session does not run the preflight again and must never switch fee
branches because the wallet balance has changed.
The
request_idmapping is persisted before mode selection and before anytransaction side effect. Concurrent calls with the same ID join the same
session even for
auto → dgb. BeforeCommitTransaction(), Core stores theDGB_COMMITTINGstate, TXID, reserved inputs, all final outputs and changescripts, and either the fully signed raw transaction or a fully reproducible
signed PSBT. After a lost RPC response or restart, it reconciles the wallet,
mempool, and chain against this record and returns the same TXID or rebroadcasts
that exact transaction; Core never builds a second payment with new inputs.
lockunspentalone is not sufficient for session management. The managerrequires a TTL, restart recovery, mempool/block reconciliation, and explicit
conflict handling.
11.4 Data Minimization and Retention
Crash safety takes precedence over deletion while an authorization is active
or ambiguous. Once a session is final and reorganization-safe, Core reduces
the additional Paymaster data according to state:
provider metadata after outcome accounting; only aggregate local reliability
remains.
PENDING_PROVIDER,USER_SIGNED,PROVIDER_SIGNED, and ambiguous conflictsretain every artifact required for duplicate-payment safety.
CONFIRMED, finalCONFLICTED,CANCELED_SAFE, or provableFAILEDand a configurable reorganization-safety period, the provider attempt,
nonce, quote, PSBT, and provider-related timestamps are removed.
request_id,canonical_request_hash, final state, and TXID where applicable remains sothat the same request ID cannot later create a second payment.
history; Paymaster code creates no second permanent payment profile beside
it.
On the provider side, the plaintext capability is never stored. Before the
provider signs, however, the wallet persists the minimal
UserAuthorizationRecordcontaining commit key, canonical PSBT hash, andstate, and for restricted sponsorship the secret-free
SponsorshipAuthorizationRecordcontaining capability hash, payment binding,and
RESERVED. Plaintext intent, IP address, and separate recipient or amountfields remain outside persistent storage except for unavoidable active
reservation data. Once the provider has signed its inputs, crash safety and
idempotency take precedence: one wallet-database transaction writes the
ProviderCommitRecord, exact final raw transaction, TXID, raw hash,commit/quote/idempotency indexes, provider outpoints, and slot state; where
applicable it simultaneously consumes sponsorship with
RESERVED → CONSUMED.Broadcast starts only afterward. This write-before-broadcast rule follows the existing
CWallet::CommitTransaction()pattern, which calls
AddToWallet()before mempool submission(
src/wallet/wallet.cpp).At source line 2526, the existing path currently writes
tx->ToString()tothe wallet log. A Paymaster implementation must therefore audit every path actually
used—the collaborative builder, selective signing,
CommitTransaction,BroadcastTransaction, Qt, and P2P processing—for sensitive logs.Paymaster-specific logs contain only shortened non-correlatable IDs, states,
and error classes, never TXID, raw transaction, PSBT, intent, capability, outpoints, recipient,
amount, IP address, or peer ID. Where an existing path logs more, the
Paymaster commit uses structured logging or a non-logging persistence
primitive. The ordinary wallet history of the final public transaction is
unaffected.
Existing wallet encryption protects private keys but does not automatically
encrypt transaction history or arbitrary metadata
(
src/wallet/crypter.h). V1 therefore doesnot falsely describe the commit record as fully encrypted at rest. Beyond the
necessarily retained final transaction, it persists no plaintext intent, IP
address, separate recipient or amount fields, sponsorship capability, or
complete PSBT. After a confirmed spend or final conflict and the replay/reorg
safety period, the additional commit index is reduced to the commit key, final
state, and TXID; ordinary wallet transaction history continues to follow
normal Core policy.
12. Proposed Modules and RPCs
New Core Modules
The new structures use named or strong amount types such as
DDCentsandDGBSatoshis. An unqualifiedCAmountmust not represent DD and DGBinterchangeably at any network or quote boundary.
Operator RPCs
For every enabled operator,
setpaymasterpolicyadditionally requiresmaximum_network_fee_dgb_satoshis, a positive absolute cap on the DGB networkfee paid by the provider per final transaction. The quote,
testmempoolacceptpreflight, and final transaction must satisfy both this capand the configured fee-rate policy; an estimate can never override the
absolute limit.
Client/Transaction RPCs
For
PENDING_PROVIDER,resolvepaymastersessionaccepts the actionsrefresh,retry_same,fallback, andcancel_to_self.fallbackretains the bounduser input set and enforces provider, fee, and privacy authorization.
cancel_to_selfdoes not locally release inputs; it creates a same-input,wallet-owned conflict transaction and returns its status and TXID. Every
mutating action uses the same persistent session and
request_id; a repeatedcall resumes the existing attempt.
The recommended escalation order is
refresh→retry_same→fallback→cancel_to_self.retry_samesends the original commit key to the sameprovider and, when no final artifact is known, the exact user-signed PSBT
persisted in the corresponding
ProviderAttempt. It accepts only the samestate or the same final raw transaction and TXID and creates no new
ProviderAttempt, quote, authorization, or signature. If the final rawtransaction is already known on the client, Core rebroadcasts it itself instead
of disclosing the payment again.
The current RPC has five positional parameters
(
src/rpc/digidollar.cpp) andimmediately invokes the single-wallet
DigiDollarWallet::TransferDigiDollar()path(
src/rpc/digidollar.cpp). For abackward-compatible extension,
optionsis appended only as a sixth, optionalparameter:
fee_rateremains in its current position as an ignored compatibilityparameter. An example
optionsobject for fully automated selection is:{ "fee_mode": "paymaster", "selection": "lowest_total_cost", "privacy_profile": "standard", "privacy_fee_tolerance_cents": 0, "paymaster_id": null, "accepted_funding_models": ["sponsored", "user_paid"], "sponsorship_context": null, "maximum_paymaster_fee_cents": 25, "allow_provider_fallback": true, "maximum_provider_attempts": 3, "request_id": "550e8400-e29b-41d4-a716-446655440000" }V1 has the following unambiguous semantics:
options, or withfee_mode: "dgb", the current direct path remainsunchanged: the wallet funds the DGB network fee itself and contacts no
paymaster. A supplied
request_idadds only the idempotent session wrapper,not a different transaction-construction path.
fee_mode: "paymaster"uses a paymaster regardless of the wallet's own DGBbalance. Without
paymaster_id, Core automatically selects the lowest-costusable provider. This directly supports “always use the lowest-cost
paymaster” in headless applications.
fee_mode: "auto"first performs a complete, side-effect-free fundingpreflight of the current DGB path, including final fee calculation and actual
coin selection. This adds a pure planning function with a typed funding
status to today's Boolean and text-error path. Only
INSUFFICIENT_DGB_FEE_INPUTS—no DGB fee inputs suitable under the currentwallet safety policy—triggers paymaster selection. An invalid recipient,
insufficient DD, or builder or validation failures return an error
immediately. A locked wallet never triggers Paymaster fallback; it pauses the
session in
AWAITING_WALLET_UNLOCK. Core must not switch to a paymaster after signing orcommit has started. DGB cost and the DD service fee are not compared because
no trustworthy exchange rate exists between them.
paymaster_idinitially pins exactly that identity. Core does notsilently switch to another provider on failure unless the caller explicitly
sets
allow_provider_fallback: true. Fallback is enabled by default forautomatic selection without an ID.
selectionsupportslowest_total_costandprivacy_weighted.Without a provider ID,
lowest_total_costis the default. The definitions inSections 7.4 and 7.5 apply, not merely the lowest announced rate.
privacy_fee_tolerance_centsis valid only forprivacy_weighted, must notbe negative, and is additionally limited by local wallet policy.
privacy_profileisstandardorhigh.highrequires an onion endpoint,proxy stream isolation,
maximum_provider_attempts: 1,allow_provider_fallback: false, disabled IP logging, and disabled P2Pmessage capture. There is no silent fallback to clearnet or P2P v1.
standardpermits clearnet only with successfully negotiated P2P v2. Core,not merely Qt, enforces these rules.
session while
-capturemessagesis active. The existing capture path writesapplication payloads together with the peer address to disk
(
src/net.cpp); transport encryption does notprotect that local plaintext copy. Regtest may override this restriction only
with explicitly synthetic test data. Although
-logipsis disabled bydefault (
src/logging.h), the high-privacyprofile additionally treats it as a hard readiness error.
accepted_funding_modelsis a non-empty allowlist containingsponsoredand/or
user_paid. When absent, an ordinary user-initiated payment allowsonly
user_paid; Qt adds a visible authorized sponsorship offer after userconfirmation. Headless applications must explicitly accept
sponsored.A newly received payment request therefore cannot silently change the
funding source of an existing session.
sponsorship_contextis permitted only forsponsoredand contains thesigned service descriptor and opaque authorization capability. Core validates
the chain, provider, policy, payment binding, and expiration, transfers the
capability only in the authenticated provider session, and persists or logs
only its hash. A descriptor with a provider ID pins that identity unless the
caller explicitly authorizes a provider change.
maximum_paymaster_fee_centsis a hard authorization limit. For anyhigh-level call in which a paymaster is permitted, it must be set either in
the request or in persistent wallet policy; “not set” never means
“unlimited.” Qt sets the limit from the display that the user explicitly
confirmed. Every concrete quote is checked again against the announcement,
wallet policy, and this limit.
effective_cap = min(request_cap, wallet_policy_cap). Core persists thiseffective value immutably when creating the session. A later relaxation of
wallet policy cannot increase it; a later tightening may further limit or
stop new attempts.
maximum_provider_attemptslimits the strictly sequential provider attempts;the contextual default is 1 when fallback is disabled and 3 otherwise. Every
attempt uses the user input set that was atomically reserved once.
request_idis mandatory whenever the call may use a paymaster. Qt generatesit before starting; Core persists it atomically when creating or joining the
session. An RPC caller supplies it. The same ID with the same canonical
payment request always returns the same persisted session or existing TXID.
The same ID with a changed recipient, amount, or options is
rejected. A transport retry of the same provider attempt with the same
(provider_id, client_nonce, intent_hash)returns the same quote and does notreserve a second slot; the same nonce with a changed intent is rejected. A
fallback is a new persisted provider attempt with a new
client_nonce. Therequest_idremains local wallet and RPC state and is neither gossiped norsent to the provider.
optionsfields and contradictory combinations are rejected, notignored. With
fee_mode: "dgb", none of the paymaster fields exceptrequest_idare valid. Whenallow_provider_fallbackis false,maximum_provider_attemptsmust be 1. Apaymaster_idset withautoappliesonly to a possible paymaster fallback.
The fee rate and fee cap are evaluated only in this local session and are not
serialized into
PaymasterPaymentIntent. The same applies to selection modeand privacy tolerance; the provider learns no additional willingness to pay
from them.
The current handler checks the wallet lock before parsing parameters
(
src/rpc/digidollar.cpp). Theoptions-based path deliberately changes this order: Core first parses and
normalizes the values required for
request_idand the request hash, then looksup the session. An identical completed session can therefore return its
existing TXID even if the wallet has since been locked. Only creating a session
or advancing it to a new signature requires the normal wallet unlock. Calls
that must persist a new session before signing, or resume one awaiting a
signature, return structured
session_state: "AWAITING_WALLET_UNLOCK",final: false, andwallet_unlock_required: true; no provider contact orfallback occurs. Qt requests an unlock. Calls without
optionsretain thecurrent ordering and error behavior.
The existing precheck against only the recipient amount is insufficient on the
paymaster path. Core checks the confirmed DD balance and any
selected_inputsagainst
payment + service_fee, while continuing to require change of exactlyzero or at least 100 cents. After receiving a quote, it revalidates the
identity, chain, intent, policy hash, expiration, prevouts, slot, template, and
fee; it never trusts the output of
getpaymasteroffersas a binding offer.Existing calls without
optionsretain their result and error behavior. Everycall with a
request_idadds the same session and idempotency fields—forexplicit
dgb, paymaster payments, and bothautobranches. For example, anunresolved paymaster result is:
{ "to_address": "DD...", "amount": 1000, "request_id": "550e8400-e29b-41d4-a716-446655440000", "session_id": "...", "requested_fee_mode": "paymaster", "fee_mode_used": "paymaster", "selection": "lowest_total_cost", "privacy_profile": "standard", "privacy_fee_tolerance_cents": 0, "paymaster_id": "...", "funding_model": "user_paid", "sponsorship_scope": "public", "fee_rate_bps": 50, "paymaster_fee_cents": 5, "user_total_cents": 1005, "provider_attempts": 1, "user_dd_inputs_used": 1, "provider_dd_inputs_used": 1, "provider_dgb_inputs_used": 1, "network_fee_paid_by": "paymaster", "network_fee_satoshis": 10000000, "status": "pending_provider", "session_state": "PENDING_PROVIDER", "pending_phase": "USER_SIGNATURE_SENT", "final": false, "broadcast_state": "unknown", "confirmation_state": "unconfirmed" }statusremains the coarse compatibility indicatorsuccess,pending_provider, orfailed; for calls withoptions,session_state,optional
pending_phase,final,broadcast_state, andconfirmation_stateare authoritative.
finalis true only in a terminal state. Stempool ormempool acceptance, including
CANCEL_MEMPOOL, remains unconfirmed andnon-final.
broadcast_statedistinguishes at leastnot_attempted,unknown,accepted_stempool,accepted_mempool,evicted, andrejected;confirmation_statedistinguishes at leastunconfirmed,payment_confirmed,recovery_confirmed,conflicted, andreorged. Aprovider report of
BROADCAST_ATTEMPTEDestablishes none of those localobservations.
txidmay be included once a validated final raw transaction isknown locally and is an optional
STR_HEXfield in the extendedRPCResult.faileddenotes only a persisted session that has demonstrably failed permanently andadditionally contains
failure_codeandfailure_message. An ambiguous stateafter a user signature remains
pending_provider. Validation, wallet, orbalance errors before session creation remain JSON-RPC errors, as they are
today.
For
auto → dgb, the mode fields arerequested_fee_mode: "auto"andfee_mode_used: "dgb"; paymaster fields are absent andnetwork_fee_paid_byisuser. The existinginputs_usedremains the count ofall transaction inputs for compatibility. The new unambiguous DD and DGB role
counters prevent applications from misinterpreting it as a DD-only input
count.
The JSON conversion table in
src/rpc/client.cppis extended with:{ "senddigidollar", 5, "options" }, { "getdigidollarsendsession", 0, "lookup" }, { "resolvepaymastersession", 0, "lookup" }, { "resolvepaymastersession", 2, "options" }, { "getpaymasteroffers", 0, "amount_cents" }, { "setpaymasterpolicy", 0, "policy" }, { "preparepaymasterpool", 0, "options" }, { "rebalancepaymasterpool", 0, "options" }, { "requestpaymasterquote", 1, "intent" },Index 5 is zero-based and designates the sixth parameter. Existing invocations
with up to five parameters and the current DGB path therefore remain usable
without modification.
getdigidollarsendsessionreturns any persistent send session without creatinga new payment attempt.
lookupis a JSON object containing exactly one ofsession_idorrequest_id; unknown fields, both fields, or neither field arerejected.
13. Qt and CLI Implementation
13.1 Principle: One Core Backend, Two Interfaces
Qt is an optional user interface, not a technical requirement for operating a
paymaster. Identity, policy, the liquidity pool, reservations, transaction
construction, signing, discovery, and operational state are handled entirely
by
digibyted, the wallet, and the Core modules described in Section 12. Anoperator can therefore configure and run the system exclusively with
digibyte-cliand a dedicated loaded wallet.Qt calls the same Core and wallet interfaces asynchronously through
WalletModel. There must be no paymaster configuration stored only in Qt andno state or signing path that is accessible only through Qt. This makes GUI
and headless operation functionally equivalent. A paymaster started in
digibyteddoes not require a running Qt process.13.2 Extending the DD Send Interface
The existing DD send interface currently displays only a fixed DGB fee and
invokes the direct single-wallet path
(
src/qt/digidollarsendwidget.cpp,src/qt/walletmodel.cpp).The proposed extension lets the user choose between own DGB fee,
paymaster, and automatic fallback when the DGB fee cannot be funded. In
paymaster mode, the user can pin a provider or choose automatically select the
lowest-cost provider; this mode uses a paymaster even when DGB is available.
Manual selection remains the GUI default. The user may optionally enable
Privacy selection with a visible maximum additional fee. The offer list additionally displays
the objective funding type Sponsored or User-paid and, for sponsored
offers, the signed sponsor display name and 0 DD user fee. Restricted
offers appear only for the current valid payment request and are not added to
the public directory. The list also displays the provider, percentage fee, DD
fee, total DD charge, and Admission reserve verified status. Operational
outpoints and claimed total free slots are not displayed before selection. Only
the concrete quote shows Payment slot reserved. After at least five assessable local sessions,
it additionally shows the success rate, sample size, and typical latency.
Sorting still prioritizes the lowest total charge; offers under an active local
cooldown are not shown.
The confirmation dialog must display:
name and bound policy hash,
percentage rate when rounding changes it,
every additional attempt, a warning that another operator receives the same
payment data,
If the price or provider changes before signing, the user reconfirms the
updated values. A later automatic fallback signature is permitted only if the
original dialog expressly authorized both the identity change and the displayed
hard fee cap.
All discovery and quote requests run asynchronously. No P2P, RPC, or
chainstate access may block the GUI thread.
The existing synchronous
WalletModel::sendDigiDollar()method remains for thecompatible direct call. A new asynchronous start call with
PaymasterSendOptionscreates the same persistentPaymentSessionas thehigh-level RPC and returns a new
DigiDollarSendStartResultcontaining either asession handle or an immediate validation error. The existing
DigiDollarSendResult(
src/qt/walletmodel.h) remains dedicatedto the synchronous legacy path. Changes are reported through a
paymentSessionChanged(session_id)signal; a separatePaymentSessionViewwithits own
PaymentSessionStatesupplies the mode, provider, exact costs, attemptcount, and pending, success, or failure state.
Because today's
WalletModel::UnlockContext(
src/qt/walletmodel.h) covers only thesynchronous call, it is never retained or captured across an asynchronous
network phase. The session enters
AWAITING_WALLET_UNLOCKorAWAITING_USER_SIGNATUREimmediately before each user signature. Qt thenrequests an unlock and retains the context only for the short signing step.
Cancellation or failure to unlock pauses the session and is not a provider
failure; neither the passphrase nor unlock context is persisted.
The common backend path invokes the existing wallet builder for
dgbandPaymasterClientforpaymasteror a triggeredautofallback. Qt implementsneither its own ranking nor a second fallback or signing path.
For
PENDING_PROVIDER, Qt replaces a misleading generic Cancel button witha recovery panel containing Check again, Try another Paymaster, and
Attempt recovery to own wallet. It clearly distinguishes inputs that are merely
reserved inside the wallet, a variant detected in the mempool, and a confirmed
spend. Before fallback or return, Qt displays the new fee, disclosure to an
additional provider, and the warning that the original variant may still take
effect until a conflicting spend confirms. Qt calls only
resolvepaymastersession; the same functions remain available to headlessapplications.
13.3 Dedicated Qt Area for Paymaster Operators
A dedicated Paymaster entry in the wallet navigation is proposed for
operators. It always shows exactly which currently selected wallet it applies
to. Operation is disabled for a watch-only wallet or a wallet without private
keys; this does not affect normal paymaster selection for outgoing payments.
The area consists of four views:
OFFLINE,STARTING,ONLINE, orDEGRADED, providerID, signed display name, endpoint, latest announcement, P2P reachability,
wallet lock state, and specific readiness errors.
restricted scope, percentage fee for
USER_PAID, minimum and maximumamounts, the absolute DGB network-fee cap per transaction, maximum
concurrent quotes, quote TTL, minimum confirmations, and optional automatic
start after the wallet is unlocked. Percentage pricing,
zero-fee sponsorship, and example costs are displayed separately before
settings are saved. Sponsor settlement and user accounts are not part of
this Core configuration.
confirmed, unconfirmed, and reserved DD carrier and DGB slots; locally
available DD and DGB liquidity; the minimum threshold of three disjoint
admission slots; and actions to prepare and repartition the pool. Before
execution, every action displays the outputs it will create and the
operator's own DGB network fee, and requires confirmation.
slot, its own DD fee revenue, expiration time, and TXID where applicable.
Recipient, user amount, user outpoints, IP/peer ID, intent, and capability
are not retained as an additional Paymaster activity profile. Ordinary
wallet history still contains the final public transaction and its publicly
observable outputs.
Cancellation is
allowed only in a protocol state in which no signature from another party
has yet been bound; Core enforces this rule independently of Qt.
Security warnings are shown directly on the overview rather than only in a
log: a locked wallet, missing carriers, insufficient DGB, unconfirmed pool
transactions, a stale announcement, an unreachable endpoint, conflicts, or a
node that is not fully synchronized. Private keys and wallet passphrases are
never displayed or stored in the paymaster configuration.
The Qt view can be implemented in
src/qt/paymasterpage.{h,cpp}and connectedthrough
WalletModel. It contains only presentation and user input.Validation and state transitions remain in
src/paymaster/and the wallet sothat Qt does not acquire a second, divergent protocol implementation.
13.4 Setup Wizard in Qt
The optional wizard guides an operator through the following steps:
operating limits,
every user-paid offer, three DD carriers,
UTXOs must never be created or repartitioned without explicit approval. Before
approval, the wizard displays the DD outputs, DGB outputs, expected network
fee, and remaining wallet balances. After each step, the operator can close
Qt and continue with the CLI; progress persists as Core and wallet state.
The start button is enabled only after Core reports
ready: true. Thisrequires, at a minimum, an identity, a synchronized node,
-prune=0,-txindex=1, complete DD transaction data, an unlockedsigning-capable wallet, a valid endpoint, and three confirmed disjoint DGB
admission slots.
USER_PAIDadditionally requires three matching admissioncarriers; every admission outpoint is separate from the operational pool. At
least one complete operational slot must also be available for
ONLINE. Thesame readiness check covers the selected privacy profile,
-capturemessages,-logips, transport type, and proxy isolation.startpaymasterperforms the same check andreturns structured error reasons when invoked through the CLI; the restriction
is therefore not merely a GUI safeguard.
13.5 Full CLI Parity
Every Qt action uses the Core interfaces defined in Section 12. Identity,
policy, pool, readiness, reservations, start, and stop are therefore fully
accessible through RPC; user functions are likewise available through
getpaymasteroffersand the extendedsenddigidollar.For example, a fully headless setup can be operated as follows; the exact JSON
quoting depends on the shell in use:
A user payment can likewise be automated safely and idempotently without Qt.
Named arguments avoid passing empty placeholders for the existing optional
positional parameters:
Third-party software may use
getpaymasteroffersto build a custom interfaceor additional local policy and then pass an explicit
paymaster_id. Theidempotent high-level call is nevertheless recommended for unattended
payments: Core revalidates the snapshot and keeps selection, quoting, input
reservation, signing, and safe fallback together in one persistent session.
A sponsorship capability is secret and must not appear as a visible command
line argument or in shell history.
digibyte-clialready supports-stdinforreading additional arguments line by line from standard input
(
src/digibyte-cli.cpp,src/digibyte-cli.cpp). Headlessintegrations use that path or a locally authenticated RPC client with protected
process memory; concept examples never contain a real capability.
For unattended operation, the paymaster wallet is loaded using the existing
Core wallet configuration; activation and policy are stored persistently in
that wallet. A passphrase, seed, or private identity key must not be supplied
in
digibyte.confor as a command-line argument. An encrypted paymasterenables signing only after an authorized wallet unlock and safely falls back
to
OFFLINEorDEGRADEDwhen the wallet is locked.14. Security Model
14.1 Non-Negotiable Invariants
verifying it against the chain.
locally.
verified.
SIGHASH_DEFAULT, with noANYONECANPAYin the MVP.template_commitment.broadcasts it immediately afterward.
to the active chain.
payment outpoints.
transaction artifacts appear in no Paymaster production log.
immutably before the first signature.
discloses an operational slot proof to the selected client.
retry_samecontinues with at most the same user-signed PSBT and returns or rebroadcasts
only that transaction.
14.2 Principal Attacks and Countermeasures
SIGHASH_DEFAULTProviderCommitRecordbefore broadcast; signedPMRESULT; exact recovery throughretry_sameretry_sameretransmits exactly that artifact; atomicUserAuthorizationRecordprevents a divergent continuationdebug.log-capturemessagesis activeddChangeDestbound locally before signingfee_rate_bps = 0, a zero DD fee, and no Paymaster DD output14.3 Limits of Protection
Without a consensus change, no protocol can force a voluntary operator to
respond or actually sign a reserved quote. A malicious paymaster can pretend
to be available, abort, or promise the same liquidity to multiple clients.
Provided the invariants above are maintained, however, it cannot:
The remaining risks primarily concern availability, timing, and privacy, not
unrestricted control over user funds.
14.4 Privacy Boundaries
The selected Paymaster necessarily sees the complete transaction that it must
validate before signing: user inputs, recipient, user change, amount, and time.
Tor hides the ordinary source IP, not this transaction data. A malicious
provider can store received data outside the reference software; the protocol
cannot cryptographically prevent that behavior.
On-chain, the carrier input and successor, DGB fee inputs, change, and later
spends remain publicly analyzable. A long-lived Paymaster ID and stable onion
endpoint are pseudonyms, not proofs of anonymity. The precise protection goal
is therefore:
15. Why No Consensus Change Is Required
The following remain unchanged:
DD_TX_TRANSFERand its version value,The paymaster function changes only:
New P2P messages, wallet database states, builder, PSBT, RPC, and Qt functions
are not consensus-critical. Recipients, miners, and ordinary validators check
only the final standard transaction and need no Paymaster upgrade. P2P nodes,
however, must be upgraded to understand or relay the new announcements and
direct protocol messages. The boundary would be crossed only if provider
IDs, special admission rules, or fee exemptions became part of transaction or
block validity. Such changes are explicitly outside the scope of this
proposal.
16. Test Strategy
Phase 0: Mandatory Regtest Proof of Feasibility
Three separate wallets:
The test creates a transaction with Alice's DD, a paymaster carrier, and
paymaster DGB, combines both partial signatures, runs
testmempoolaccept,mines the transaction, and verifies all wallet states after a restart.
Unit and Wallet Tests
1 DD × 0.5% → 0.01 DDroundingcase, overflow, and the local fee cap,
SPONSOREDenforces exactlyfee_rate_bps = 0, a zero DD fee, and noprovider DD output regardless of external sponsor metadata,
provider, policy, recipient, amount, request nonce, and expiration; replay,
manipulation, and plaintext persistence are rejected,
contradictory amount fields,
successors, ordinary provider fees, user change, and recovery outputs;
carrier overflow and deterministic splitting of large recovery amounts,
entry; all fallbacks in the same session retain the bound destinations,
capabilities, TXIDs, PSBTs, and raw transactions appear in no Paymaster
production log,
100 → 101...199cents,unfinalized provider inputs, disallowed fields, and modified prevouts, roles,
or sighash types; before durable commit only a provider witness that was
never stored may vary, while afterward witness and raw transaction must be
exactly identical,
operational pool, and exactly one disclosed operational candidate slot,
recovery,
PaymentSessionwith multiple append-onlyProviderAttemptrecords andimmutable artifacts once set; quote expiration after a user signature does
not release the user inputs,
request_id;cancel_to_selfuses theexact bound input set and a fresh internal DD destination, and an RPC retry
creates no second conflict variant,
ProviderCommitRecordsurvives crashes before and afterbroadcast;
retry_samealways returns the same raw transaction and TXID andreserves neither a second slot nor creates a second quote,
retransmission of the exact client-persisted user-signed PSBT; changed hashes,
templates, signatures, fields, or provider slots are rejected,
the atomic transition of the secret-free record from
RESERVEDtoCONSUMED,payloads while retaining the minimal idempotency tombstone and continuing to
prevent a second payment for the same
request_id,ProviderAttempt, a neutralavailability-timeout class, 30-day buckets, aging, and a short cooldown for
local provider reputation,
network-fee cap,
request_idhandling, including rejection when an ID is reused with a changed payment
request,
representations produce the same hash,
wallet fee caps, and an immutable session cap,
Functional and P2P Tests
admission outpoints; only the selected candidate receives an isolated
session and discloses at most one operational slot,
a 0 DD user fee; manual Qt selection and the headless request allowlist remain
authoritative,
request; it is not gossiped, and a pinned sponsor is not replaced without
explicit fallback authorization,
slots, while
USER_PAIDis ineligible without three admission carriers;both require a disjoint operational slot for a quote,
paymasterwithout a provider ID selects the lowest exactly rounded eligibletotal cost, not merely the lowest rate,
privacy_weighteduses a controlled test RNG and selects only within theauthorized cent tolerance; tolerance 0 is equivalent to
lowest_total_cost,-logips,-capturemessages, fallback, and more than one attempt; standard clearnetrequires successful P2P v2,
autoremains on the direct path when the DGB fee can be fully funded andswitches only on
INSUFFICIENT_DGB_FEE_INPUTSunder the current wallet safetypolicy; other errors, or signing or commit already having started, do not
trigger fallback,
PENDING_PROVIDER;refresh, same-input fallback, andcancel_to_selfproduce the specified state transitions while mere local release is rejected,
reconciliation, exact rebroadcast, or a new same-input variant,
and self-funded recovery without provider contact; its result remains marked
as a non-binding snapshot,
spend; cancellation mempool acceptance remains non-final before confirmation
and is reconciled again after eviction or reorganization,
payment conflict, and self-recovery;
CONFIRMEDapplies only to the confirmedpayment and
CANCELED_SAFEonly to confirmed self-recovery,provider attempts remain until final safety resolution,
sequential attempts,
signed
PaymasterOfferTerms, and any price, slot, or chain-state change, isdetected before the user signs,
request_idcreate neither a second payment nor a duplicate provider-attemptreservation for either
auto → dgborauto → paymaster,CommitTransaction()recovers the same persisted rawtransaction; a resumed
autorequest does not switch its fixed fee branchwhen the balance has changed,
UserAuthorizationRecord, after that record but beforeProviderCommitRecord,after final commit but before broadcast, and after broadcast but before
PMRESULT; every stage resumes only the same attempt and can confirm at mostthe same transaction variant,
PMRESULTsignature, monotonicresult_sequence, mandatory final rawtransaction from
FINAL_COMMITTED, and proof that provider status does notsubstitute for a local pool or chain observation,
BroadcastTransactionpath,-prune!=0, missing txindex, and incomplete DD transactiondata; general pruned support remains outside V1,
upgraded nodes relay announcements, and only the sequentially selected
provider receives payment data,
Qt Tests
after an explicit choice,
when DGB is available, while “Auto” falls back only when the DGB fee cannot
be funded,
is claimed only after concrete reservation,
restricted offers disappear when the payment request expires,
displayed unambiguously,
with a visible fee cap and attempt count,
another provider are displayed unambiguously,
without blocking the GUI thread,
PENDING_PROVIDERdisplays all three recovery actions, distinguishes walletreservation, mempool, and confirmation, and warns that the original variant
may still confirm,
recovery as a non-binding preflight; the action is labeled "Attempt
recovery to own wallet,"
AWAITING_WALLET_UNLOCK; canceling the unlockremains neutral and creates no new session,
-prune=0,-txindex=1, complete DD data, synchronization, and endpoint are shownclearly,
CLI and Qt Parity Tests
using RPC alone,
senddigidollarcalls with up to five parameters retain the directDGB path; the sixth
optionsobject works both positionally and with namedCLI arguments,
senddigidollar/options,getdigidollarsendsession/lookup,resolvepaymastersession/lookupand/options,getpaymasteroffers/amount_cents,setpaymasterpolicy, both poolRPCs, and
requestpaymasterquote/intent,result fields for the same request,
auto → dgbandauto → paymasterreturn the same session and idempotencyfields;
session_state, optionalpending_phase,final,broadcast_state,confirmation_state, finalfailed, and an optional TXIDare unambiguous,
AWAITING_WALLET_UNLOCK, Qt requests an unlock, and both resume the samesession afterward,
wallet has since been locked; session lookup accepts exactly one
session_idor
request_id,restarts,
confirmed user payment,
-stdininstead of aprocess argument or shell history,
stoppaymasterstops accepting new quotes in a controlled manner withoutleaving existing signed transactions or reservations inconsistent.
17. Implementation Steps
prevouts, and complete reduction of sensitive logs.
subphases, and recovery preflight.
provider commit,
UserAuthorizationRecord, and idempotent PSBTretransmission through
retry_same.admission slots that are never used operationally.
at most one operational slot disclosed to the selected client.
service descriptors for restricted merchant and application sponsors.
send call with automatic selection, an optional Qt operator area, security
filters, cost comparison, and confirmation.
guards, privacy selection, and state-dependent data reduction.
This sequence proves the builder and signing mechanisms before expanding the
public P2P attack surface.
18. V1 Acceptance Criteria
V1 is considered successful when:
disjoint, confirmed DGB admission slots; every user-paid offer additionally
proves at least three admission carriers, and operational payment outpoints
never appear in global gossip.
using a carrier.
locally.
ProviderAttempt, any change to the recipient, amount,change, fee, or inputs after its user signature causes rejection. A fallback
keeps the recipient, recipient amount, and user input set unchanged;
provider-dependent components may change only in a new, fully verified and
newly signed attempt.
confirmed payment.
required.
failures, and never replaces security or liquidity verification.
persistent Core session, enforce a hard DD fee cap, and avoid creating a
second payment when the same
request_idis retried.senddigidollarcalls withoutoptionscontinue to use thewallet's own DGB fee path unchanged.
charge the user exactly 0 DD service fee, and require no user balance at the
Paymaster.
in plaintext and are cryptographically bound to the concrete payment.
payment details are written to production logs.
isolation, exactly one provider attempt, and no clearnet/P2P-v1 fallback.
discloses at most one operational slot exclusively to that client; the fee
cap and privacy tolerance remain local.
without losing idempotency or duplicate-payment protection.
non-guaranteed anonymity.
idempotent recovery through another check, same-input fallback, or a
same-input return to the user's wallet; timeout never releases the inputs,
and a cancellation variant is treated as final only after a confirmed
spend.
restore it exactly on
retry_same; pending subphases and the localconstructibility preflight select the appropriate recovery after a crash or
connection loss without contacting additional providers in advance.
resumed by retransmitting the exact same persisted user-signed PSBT without
creating a new quote, reservation, authorization, signature, transaction
variant, or
ProviderAttempt.a carrier successor, provider fee, change, and split recovery—is between
100 and 10,000,000 cents.
-prune=0,-txindex=1, and complete DDtransaction data as readiness requirements; recipients, miners, and normal
validators need no Paymaster feature, while only upgraded P2P nodes relay
Paymaster announcements.
PMRESULTis monotonic, signed, and fromFINAL_COMMITTEDbound to exactlyone final raw transaction; the atomic wallet-database commit writes every
record, outpoint, index, and any
RESERVED → CONSUMEDtransition togetherbefore broadcast begins.
CANCEL_MEMPOOLremains under
PENDING_PROVIDER, and RPC/Qt expose identical finalitythrough
session_state,pending_phase,final,broadcast_state, andconfirmation_state.in
AWAITING_WALLET_UNLOCK, and operator policy enforces a positiveabsolute DGB network-fee cap.
All reactions