pub enum MirrorReconcileResult {
Planned {
capsules_stale: u32,
capsules_affordable: u32,
collateral_reclaimed_dig_base_units: u64,
collateral_relocked_dig_base_units: u64,
fee_estimate_mojos: u64,
stale_url_sets: Vec<Vec<String>>,
url_current: Vec<String>,
targets_seen_last_7_days: u32,
},
Refused {
reason: MirrorReconcileRefusal,
url_current: Option<Vec<String>>,
},
Submitted {
reclaims_submitted: u32,
reclaims_rejected: u32,
recreates_owed: u32,
left_unchanged: u32,
left_reason: Option<String>,
url_current: Vec<String>,
},
}Expand description
control.mirror.reconcile — reconcile this node’s mirror coins to its CURRENT advertise URL:
reclaim every coin advertising a stale URL, then recreate it advertising the URL this node
advertises TODAY. Shared by the manual “reset mirrors” action and the same primitive the
node’s DAILY detector runs (dig-node#570) — ONE reconcile primitive, exposed two ways
(dig_ecosystem#3203).
§Three outcomes, and a client MUST be able to tell them apart
They mean different things about the user’s money, which is why this is a tagged union rather than a single struct with optional fields:
Planned—dry_run: trueonly. A PRICED PLAN; nothing was spent.Refused— nothing was spent. SeeMirrorReconcileRefusalfor why.Submitted— reclaims were SUBMITTED to the mempool; recreates are OWED to a later pass. Neither is confirmed by the time this call returns.
§completed/partial do not exist, and cannot: a reclaim and a create are separate bundles
A create’s $DIG is selected by a chain scan of CONFIRMED coins; a reclaim’s collateral becomes
spendable only once the reclaim ITSELF confirms — a LATER pass, never inside this call. So at
return time the node can state how many reclaims the mempool accepted or rejected and how many
recreates are therefore owed, but it CANNOT state how many creates confirmed: that fact does
not exist yet. A completed{created: 3}-shaped answer would be a money statement about the
future reported as though it were the present — the exact class of lie dig-node SPEC §25.8
exists to remove. Submitted’s counts are the honest version of the same
information: what the node actually knows at the moment it answers.
§Refused MUST mean nothing was spent, unconditionally
A caller distinguishes refused from submitted precisely so it never has to guess whether a
“no” cost money. An implementation that spends anything under a Refused outcome breaks the
one guarantee this contract exists to give the operator.
§A dry run that would in fact refuse reports Refused, never Planned
Planned prices a plan the node believes it CAN execute — computed against the same
validation (corroborated URL, changed URL, affordability) a real run performs. A dry run whose
candidate URL is uncorroborated, unchanged, or otherwise refusable answers Refused exactly
like a real run would, so a caller previewing a reset sees the same refusal a real attempt
would hit rather than a plan for a call that cannot execute.
§The load-bearing invariant: K is sized BEFORE any reclaim, and exactly K are reclaimed
Planned::capsules_affordable and Submitted‘s counts describe the SAME quantity, computed
the SAME way: the affordable prefix K, sized against the wallet’s balance AUGMENTED by the K
coins’ own collateral (since reclaiming them is what funds their matching recreate), decided
BEFORE any reclaim is attempted. An implementation MUST NOT reclaim more than it has already
decided it can recreate — reclaiming n and creating only K leaves the node holding
uncollateralised capsules it has stopped advertising, which is the half-run failure this whole
method exists to prevent. “No worse off” therefore means the bond COUNT is unchanged, never
merely that no XCH fee was wasted.
Variants§
Planned
dry_run: true only — a priced plan. Nothing was spent.
Fields
capsules_affordable: u32Of those n, how many the wallet can actually afford to reconcile right now (K) —
sized the SAME way, and BEFORE the same event, that a real run’s
Submitted counts are: see “The load-bearing invariant” above.
K < capsules_stale is a normal answer, not a warning sign; a client renders it as
“will reconcile K of n” rather than treating it as a partial failure.
collateral_reclaimed_dig_base_units: u64The $DIG this plan would FREE, summed over the K coins it would reclaim — read from those coins, never recomputed from today’s requirement.
collateral_relocked_dig_base_units: u64The $DIG this plan would RE-LOCK, K × this epoch's margined requirement.
Deliberately NOT combined with
collateral_reclaimed_dig_base_units
into one “cost” figure: collateral is reclaimed and relocked, not spent, and the net
$DIG movement is collateral_relocked_dig_base_units - collateral_reclaimed_dig_base_units — zero in the common case where the requirement
has not moved. Presenting the round-trip as a single cost teaches an operator their
reset burns $DIG, which is the money-lie class in the reassuring-looking direction’s
opposite: it makes a free action look expensive rather than a costly one look free.
fee_estimate_mojos: u64The ACTUAL cost of this plan: the XCH network fee, in mojos (1e-12 XCH). The one
figure in this result that is genuinely spent rather than reclaimed-and-relocked.
stale_url_sets: Vec<Vec<String>>The DISTINCT URL set(s) the n stale coins currently advertise, bounded.
A Vec<Vec<String>> rather than one flattened list: coins created at different past
times can each carry a DIFFERENT advertise URL, so a single list would either merge
them (implying they agree when they may not) or silently show only one coin’s memo. A
client renders each inner list as one distinct “was advertising” group.
url_current: Vec<String>The URL(s) this node’s mirror coins would advertise once the K creates confirm — this
node’s CURRENT target, i.e. what control.config.get’s mirror_advertise reports NOW.
targets_seen_last_7_days: u32How many DISTINCT advertise URLs this node has held in the last 7 days.
dig-node SPEC §25.13.8’s rotating-address warning: a node whose public address keeps changing pays reconcile’s fee repeatedly for a problem reconcile cannot fix. A client SHOULD surface this when it is greater than one or two, rather than only after the operator has already paid for several resets.
Refused
Refused. Nothing was spent — see MirrorReconcileRefusal for why.
Fields
reason: MirrorReconcileRefusalWhich of the ten reasons this refusal is.
Submitted
Reclaims were SUBMITTED to the mempool; matching recreates are OWED to a later pass — the
only outcome an implementation may report once anything has been spent, because a create’s
confirmation is never observable synchronously (see “completed/partial do not exist”
above). A left_unchanged of zero means every stale capsule was affordable and submitted;
greater than zero is a NORMAL outcome, not an error — the caller can tell because the
counts say so, not because a tag says “partial”.
Fields
reclaims_rejected: u32Of the SAME K attempted, how many the mempool REJECTED (e.g. a coin spent elsewhere in
a race). Distinct from left_unchanged: a rejected
reclaim was ATTEMPTED and failed at runtime, an unchanged capsule was never attempted
because K was sized before it.
recreates_owed: u32How many recreates are now OWED to the ordinary create pass — always equal to
reclaims_submitted: only an accepted reclaim’s
collateral will ever confirm and become available to fund its matching create. Never
created inside this call; see the type-level doc for why.
left_unchanged: u32How many stale capsules (n − K) this call left exactly as they were, because sizing
K happened BEFORE any reclaim was attempted.
left_reason: Option<String>Why the plan stopped short of every stale capsule. None when
left_unchanged is zero — there is nothing to
explain when nothing was left behind.
url_current: Vec<String>The URL(s) the K submitted recreates are targeting — this node’s CURRENT target, on
the same terms as Planned::url_current.
Trait Implementations§
Source§impl Clone for MirrorReconcileResult
impl Clone for MirrorReconcileResult
Source§fn clone(&self) -> MirrorReconcileResult
fn clone(&self) -> MirrorReconcileResult
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more