pub struct SpendsListResult {
pub spends: Vec<AutomatedSpend>,
pub complete: bool,
pub cursor: Option<String>,
pub unreadable_lines: u32,
}Expand description
control.spends.list — one page of the automated-spend audit record.
§Why this method is the only sanctioned reader
The record is a node-private file (dig-node SPEC §23). Every other view — dig-app’s Activity tab included — reads it THROUGH the node, and this is that route. A second process parsing the file would be a second implementation of a growing append-only format, which is how two views of “what did the node spend” start disagreeing, on the one subject where disagreeing is least affordable.
§A page, and it says so
spends is bounded by
SPENDS_LIST_MAX_LIMIT. Whether it is the whole matching
set is stated by complete and never left to be inferred from the page’s
length: a node may return a short page for its own reasons, and a matching set that is an exact
multiple of the page size makes the last full page indistinguishable from a truncated one.
Without an explicit flag a caller cannot tell “there are no more spends” from “we stopped telling
you” — and on an audit record those read the same and mean opposite things.
§The order is part of the contract
A node MUST return rows by DESCENDING initiated_ms, breaking
ties by ASCENDING id, and MUST keep that order stable across the pages of
one walk. after_id means strictly after this row
in that order. The tiebreak is required rather than incidental: automated spends are issued by a
cycle and several can share a millisecond, so a time-only order names no position and a walk
would repeat some rows and skip others.
§An empty page is an ANSWER, never a fallback
spends: [] with complete: true means this node has moved no money unattended that matches the
filters. It is NEVER what a caller gets when the record could not be read: that is
SpendAuditUnreadable. “Nothing to
report” and “I could not look” are different answers, and the first is the one a person stops
investigating on.
Fields§
§spends: Vec<AutomatedSpend>One page of matching spends, newest-initiated first, possibly empty.
complete: boolIs this page the WHOLE matching set?
true means every matching spend the node holds is in spends. false
means the answer was TRUNCATED and more exist — resume from cursor.
Required on the wire, and stated positively so the reading a caller falls into when the field
is absent or defaulted is the SAFE one. A boolean spelled truncated would default to
false, i.e. to “this is everything”, which is the claim that ends a walk early; complete
defaults to “there may be more”, which costs at worst one redundant request.
cursor: Option<String>The id of the last row in this page — the value to resume from — or null for an empty
page.
It is the id the caller was HANDED, never a marker for where the record “got to”. Pass it as
after_id.
The key MUST be present; null is meaningful and an absent key must not decode into it.
unreadable_lines: u32How many entries in the record the node could NOT parse.
Part of the answer rather than a log line, and a client MUST surface a non-zero value. An audit trail that lost entries to corruption and reads as a shorter, tidy list is indistinguishable from one where those spends never happened — which is the same lie as a missing entry, told more convincingly.
It counts unreadable entries across the WHOLE record, not just this page: a corrupt entry has no parsed timestamp and no parsed id, so it cannot be attributed to a page or excluded by a filter. A caller therefore MUST NOT read it as “this many rows are missing from this page”.
Trait Implementations§
Source§impl Clone for SpendsListResult
impl Clone for SpendsListResult
Source§fn clone(&self) -> SpendsListResult
fn clone(&self) -> SpendsListResult
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more