Skip to main content

WalletSyncPhase

Enum WalletSyncPhase 

Source
pub enum WalletSyncPhase {
    NotStarted,
    Syncing,
    Synced,
    NoWalletEnrolled,
    WalletNotUnlocked,
    Unrecognized(UnknownPhaseToken),
}
Expand description

How far the node’s wallet chain replica has got — the states a background sync can be in.

Named states rather than a boolean, because “has never started” and “is caught up” are different facts and a bool can only carry one of them. Paired with a peak_height a boolean forces a never-started wallet to report some height, and 0 is the only one available — which reads as synced to the genesis block, a claim about the chain that is simply false.

§Nothing to watch is TWO states, not one

A sync with no addresses to follow is idle for one of two reasons, and they are different sentences to a user with different remedies. NoWalletEnrolled is the honest all-clear: there is no wallet, so watching nothing is correct and complete. WalletNotUnlocked is the opposite — a wallet EXISTS and is not being watched — and reporting it as the all-clear tells a user with real coins that their balance is fully accounted for while the node follows none of their addresses. Merging the two would put a money-lie behind a green tick, so the contract keeps them apart.

§An unrecognised token is a VALUE, not a parse failure

Unrecognized exists because this enum was once closed, and a node that grew a new phase took every consumer’s whole response down with it — see the variant’s own docs. Consumers MUST treat an unrecognised phase as unknown, never as progress.

Variants§

§

NotStarted

No sync has begun: the wallet holds no replica of the chain and is not building one.

§

Syncing

A sync is running — either the initial catch-up, or the ongoing task that keeps the replica current. A wallet whose catch-up finished but whose peer connections have all dropped is Syncing, not Synced: it is trying to be current and is not.

§

Synced

The initial catch-up completed AND at least one Chia peer connection is currently live: the replica is caught up and CONNECTED, so it is in a position to be kept current.

That is what the predicate delivers, and no more. A live connection to a stalled or lagging peer satisfies it while the replica quietly goes stale, so this phase MUST NOT be read as proof that the data is FRESH — only that nothing is known to be preventing freshness.

§

NoWalletEnrolled

The honest all-clear: no wallet is enrolled on this node, so there are no addresses to follow and a sync would have nothing to do. Not a degraded state and not an error — a node that has never had a wallet is working exactly as intended.

A consumer MAY present this as settled. It is the ONLY nothing-to-watch phase for which that is true: WalletNotUnlocked looks identical from inside the sync loop and means the opposite.

watched_addresses accompanying this phase is Some(0) — an observed zero, and the zero that is genuinely fine.

§

WalletNotUnlocked

A wallet IS enrolled, but the node holds no addresses for it, so it is watching nothing. The user’s coins are not being followed and their balance is not being maintained.

This is the common state after every restart, because the address set is derived from key material the node cannot reach until the wallet is unlocked, and nothing back-fills it while locked. It is emphatically NOT NoWalletEnrolled: the difference between them is the difference between nothing to do and something to do that is not being done.

A consumer MUST NOT render this as synced, settled, or up to date, and MUST NOT present a balance read under it as complete. The honest rendering names the wallet and the remedy — “locked, so it is not being watched yet” — because unlocking is the action that resolves it.

The name says NOT UNLOCKED rather than locked on purpose. An empty address set is what the node can observe; a lock is only the usual cause of it, and a manifest that never carried the keys reaches the same state without anything having been locked. The phase claims the observation, and leaves the cause to whatever the node can actually establish.

§

Unrecognized(UnknownPhaseToken)

A phase token this build does not know, carried verbatim.

§Why this variant exists

The enum shipped closed. dig-node then grew a phase, and because serde rejects an unknown variant, the unknown token did not degrade one field — it aborted the entire WalletSyncStatusResult. dig-app’s sync read became Err, its chain-sync state collapsed to unknown, and the surface rendered nothing at all (dig_ecosystem#2609). Every consumer built against an older contract than the node it talks to hit it at once.

§It is deliberately NOT silent

The token is preserved rather than discarded so the state is observable: a consumer can say which token it failed to understand, and a developer can read it out of a log instead of reaching for a packet capture. This incident stayed invisible until somebody built a probe against the published crate; the variant that replaces it should not need one.

Mapping an unknown token onto Synced or Syncing would be far worse than the parse error it replaces. A parse error is loud and obviously wrong; a coerced phase is a confident, plausible statement about the user’s money that the node never made. Consumers MUST render this as unknown and MUST NOT infer progress, completion, or a trustworthy balance from it.

§The payload is untrusted text

It is whatever the node sent. A consumer that displays it MUST escape and bound it like any other foreign string rather than splicing it into a message unchecked. Debug escapes it, as String’s always has; as_wire deliberately does not, because a relay must be able to hand on the exact bytes.

§Not the same idea as PeerSoftware::Unknown

The two look alike and are not. PeerSoftware::Unknown is the ABSENCE of a report — the peer said nothing, or said something unparseable, and there is no datum to keep. Here the node DID report, and the token it used is a real observation this build cannot interpret. That is why this variant carries a payload and that one does not, and why the names differ: calling it Unknown would suggest nothing was said.

Implementations§

Source§

impl WalletSyncPhase

Source

pub const ALL: &'static [WalletSyncPhase]

Every phase this build KNOWS, in progress order — the enumeration a machine reads, and the anchor the conformance KATs pin the wire tokens against.

Unrecognized is absent by definition: it is the absence of a known token rather than one of them, and it has no fixed wire spelling to pin. A node MUST NOT emit anything outside this list; a consumer that meets something outside it gets Unrecognized instead of a failed response.

Source

pub fn as_wire(&self) -> &str

This phase’s exact wire spelling, or the verbatim token for Unrecognized.

The one place a phase becomes a string, so serialization and any display path cannot drift into two different spellings of the same state.

Source

pub fn unrecognized_token(&self) -> Option<&str>

The token a build does not understand, or None for every phase it does.

Lets a consumer log or surface the exact unrecognised spelling without matching the variant open-coded, which is how the two spellings drift apart.

Source

pub fn is_recognized(&self) -> bool

Whether this build understands the phase at all.

The predicate a consumer branches its “your node may be newer than this app” path on.

Source

pub fn unrecognized_token_value(&self) -> Option<&UnknownPhaseToken>

The unrecognised token as its own type, giving access to the escaped renderings.

unrecognized_token hands back a raw &str; this hands back the UnknownPhaseToken, whose Display escapes and whose display_bounded also truncates.

Source

pub fn may_render_as_settled(&self) -> bool

Whether a consumer may present this phase as SETTLED — nothing outstanding, nothing to do.

§Why this is a method and not a rule in the docs

Two phases mean “the sync is idle” and only one of them is good news. NoWalletEnrolled is complete and correct; WalletNotUnlocked is a wallet whose coins nobody is following. Rendering the second as settled is the money-lie this family exists to prevent, and it is one mistaken || away in every consumer that writes the rule itself.

Stating it once here makes it a compiler-checked fact rather than a paragraph each consumer re-derives — a second implementation of a rule like this is a drift bug waiting to happen. An unrecognised phase is never settled: this build cannot know what the node meant.

use dig_node_control_interface::results::WalletSyncPhase;
assert!(WalletSyncPhase::Synced.may_render_as_settled());
assert!(WalletSyncPhase::NoWalletEnrolled.may_render_as_settled());
// A wallet exists and nothing is watching it — never settled.
assert!(!WalletSyncPhase::WalletNotUnlocked.may_render_as_settled());
assert!(!WalletSyncPhase::from("a_newer_token").may_render_as_settled());

Trait Implementations§

Source§

impl Clone for WalletSyncPhase

Source§

fn clone(&self) -> WalletSyncPhase

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WalletSyncPhase

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for WalletSyncPhase

Source§

fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>

Accepts ANY string. A non-string is still a type error — a number or an object where a phase belongs is a malformed response, not a newer node.

Source§

impl Eq for WalletSyncPhase

Source§

impl From<&str> for WalletSyncPhase

Source§

fn from(token: &str) -> Self

Every token maps to a phase — an unknown one to Unrecognized. Total by construction, so no caller can reintroduce the fail-closed behaviour this type exists to remove.

Source§

impl Hash for WalletSyncPhase

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for WalletSyncPhase

Source§

fn eq(&self, other: &WalletSyncPhase) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for WalletSyncPhase

Source§

fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

A bare JSON string, exactly as the derived rename_all = "snake_case" produced before this type grew an unrecognised arm — so an Unrecognized token round-trips back out byte-identical rather than being rewritten or dropped by a relay.

Source§

impl StructuralPartialEq for WalletSyncPhase

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.