jmwallet.wallet.rows
jmwallet.wallet.rows
Canonical row model for wallet info displays.
All wallet views (the static CLI info --extended today, the interactive
explorer later) are fed from a single, view-independent row model. Renderers
consume rows; they never re-derive visibility, labels or ordering from wallet
internals (a second derivation path drifting from the real display logic was
a bug source in earlier rework attempts).
Vocabulary (three levels, kept as separate row fields):
label: what the funds are (new,dep,cj-o,cj-c,chg,used,flag,rsvd,fb-a,fb-x). Short display forms (max 4 chars) keep the label column narrow; the underlyingAddressStatuskeeps the historic long names (persisted in wallet metadata), e.g.non-cj-changeis surfaced aschg(see_STATUS_TO_LABEL).- state: spendability, derived per row from the
frozen/pendingflags and the confirmation count (spend/frozn/pend/unconf; renderers showspendexplicitly as a positive signal). Fidelity bonds carry their lock state in the label (fb-a/fb-x) and the locktime date as a separate data field (locktime, formatted viafb_date_str). reused: independent flag. A reusedcj-ois still acj-o(label) that was reused (flag) — see issue #564.
Pipeline stages (deliberately separate, so later CLI/explorer filter and sort flags become thin layers over tested stages)::
build -> filter -> sort -> group -> render
The static CLI currently uses the identity filter and renders in build
order; the sort stage with :func:base_order_key exists for the explorer
and future flags.
Ordering note: the build stage preserves the input order of the address
lists, which for external/internal branches is derivation-index order and
for the fidelity bond branch is the service's locktime-then-index order
(the meaningful chronological axis for bonds). base_order_key defines
the canonical sorted order as (mixdepth, branch rank, derivation index,
intra-address position); applied to the fidelity bond branch this can
differ from the legacy locktime listing, which is acceptable because the
static renderer does not sort.
Attributes
AddressRowLike: TypeAlias = UtxoRow | AddressRow
module-attribute
DEFAULT_NEW_ADDRESS_LIMIT = 6
module-attribute
EXTERNAL_BRANCH = 0
module-attribute
GroupableRow: TypeAlias = UtxoRow | AddressRow | HiddenRow | BranchSummaryRow
module-attribute
INTERNAL_BRANCH = 1
module-attribute
Row: TypeAlias = UtxoRow | AddressRow | HiddenRow | BranchSummaryRow | MixdepthSummaryRow
module-attribute
RowLabel = Literal['new', 'dep', 'cj-o', 'cj-c', 'chg', 'used', 'flag', 'rsvd', 'fb-a', 'fb-x']
module-attribute
__all__ = ['DEFAULT_NEW_ADDRESS_LIMIT', 'EXTERNAL_BRANCH', 'INTERNAL_BRANCH', 'AddressRow', 'AddressRowLike', 'BranchSummaryRow', 'GroupableRow', 'HiddenRow', 'MixdepthSummaryRow', 'Row', 'RowLabel', 'UtxoRow', 'base_order_key', 'build_branch_rows', 'derive_label', 'fb_date_str', 'filter_rows', 'group_rows', 'sort_rows', 'summarize_branch', 'summarize_mixdepth']
module-attribute
Classes
AddressRow
dataclass
One row for a visible address without UTXOs.
Covers new/rsvd (always shown) and, with show_empty,
used/flag addresses. Also used for unfunded fidelity
bond addresses (redeemed bonds stay visible, with their locktime).
reused carries the RU flag for empty-but-reused addresses so
renderers can still warn about them.
Source code in jmwallet/src/jmwallet/wallet/rows.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |
Attributes
address: str
instance-attribute
address_index: int
instance-attribute
branch: int
instance-attribute
label: RowLabel
instance-attribute
locktime: int | None = None
class-attribute
instance-attribute
mixdepth: int
instance-attribute
original_seq: int = 0
class-attribute
instance-attribute
path: str
instance-attribute
pending: bool
instance-attribute
reused: bool
instance-attribute
user_label: str
instance-attribute
BranchSummaryRow
dataclass
Aggregated balance for one branch of one mixdepth.
Source code in jmwallet/src/jmwallet/wallet/rows.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
Attributes
balance: int
instance-attribute
branch: int
instance-attribute
expired_balance: int = 0
class-attribute
instance-attribute
expired_frozen_balance: int = 0
class-attribute
instance-attribute
expired_spendable: int
property
Expired bond funds that are spendable right now.
Frozen expired bonds are excluded: frozen means excluded from
any transaction, so counting them would contradict the frozn
state shown on the row.
frozen_balance: int = 0
class-attribute
instance-attribute
locked_balance: int = 0
class-attribute
instance-attribute
mixdepth: int
instance-attribute
spendable: int
property
Balance usable for automatic coin selection.
Fidelity bond funds are excluded from normal automatic selection
(locked ones cannot move; expired ones need explicit selection or
a bond-only sweep, see :attr:expired_spendable), so the branch
contributes 0, matching the legacy "(spendable: 0 sats)" display.
HiddenRow
dataclass
Count of empty addresses hidden by the gap/show-empty logic.
Source code in jmwallet/src/jmwallet/wallet/rows.py
135 136 137 138 139 140 141 | |
Attributes
branch: int
instance-attribute
hidden_count: int
instance-attribute
mixdepth: int
instance-attribute
MixdepthSummaryRow
dataclass
Aggregated balance for one mixdepth (all branches incl. bonds).
Source code in jmwallet/src/jmwallet/wallet/rows.py
180 181 182 183 184 185 186 | |
Attributes
mixdepth: int
instance-attribute
spendable_balance: int
instance-attribute
total_balance: int
instance-attribute
UtxoRow
dataclass
One row per funded UTXO (hard rule: one line per UTXO).
Source code in jmwallet/src/jmwallet/wallet/rows.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
Attributes
address: str
instance-attribute
address_index: int
instance-attribute
branch: int
instance-attribute
frozen: bool
property
Spendability deviation: frozen UTXOs are excluded from coin selection.
label: RowLabel
instance-attribute
locktime: int | None = None
class-attribute
instance-attribute
mixdepth: int
instance-attribute
original_seq: int = 0
class-attribute
instance-attribute
path: str
instance-attribute
pending: bool
instance-attribute
reused: bool
instance-attribute
unconfirmed: bool
property
user_label: str
instance-attribute
utxo: UTXOInfo
instance-attribute
Functions:
base_order_key(row: AddressRowLike) -> tuple[int, int, int, int]
Canonical ordering: mixdepth, branch (external < internal < fidelity bonds), derivation index, then the UTXO's original position within its address (cache order).
Formalizes the order the legacy display produced for the external/internal branches. There is deliberately no txid/vout tiebreak: the intra-address cache order is the stable is-order.
Source code in jmwallet/src/jmwallet/wallet/rows.py
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | |
build_branch_rows(addresses: list[AddressInfo], *, mixdepth: int, branch: int, pending_addresses: set[str] | None = None, show_empty: bool = False, new_address_limit: int = DEFAULT_NEW_ADDRESS_LIMIT, now: int | None = None) -> list[Row]
Build the canonical rows for one branch of one mixdepth.
Produces one :class:UtxoRow per UTXO (hard rule: one line per UTXO),
one :class:AddressRow per visible address without funds, and a single
:class:HiddenRow when the gap/show-empty logic hid empty addresses.
Rows are emitted in input order, which equals the legacy display order
(derivation index for external/internal, locktime for fidelity bonds).
Visibility mirrors the legacy display exactly: reserved addresses
are always shown; up to new_address_limit unused new addresses
are shown; empty used-empty/flagged addresses are hidden.
show_empty disables all hiding. The fidelity bond branch never
hides anything (redeemed/unfunded bonds must stay visible).
Source code in jmwallet/src/jmwallet/wallet/rows.py
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | |
derive_label(addr: AddressInfo, *, now: int | None = None) -> tuple[RowLabel, bool]
Translate an AddressInfo into the three-level row vocabulary.
Returns (label, reused):
status == "reused"→(base_status, True).base_statusis always set in that case (issue #564); defensively falls back todep/chgby branch if it is missing or unusable.- bond addresses →
fb-auntilnowstrictly passes the locktime,fb-xonce strictly past it (the lock state lives in the label; the locktime date is a separate row field). - every other status maps onto its label via
_STATUS_TO_LABEL.
The CLI supplies chain median-time-past as now; standalone display
callers default to host time.
Source code in jmwallet/src/jmwallet/wallet/rows.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | |
fb_date_str(locktime: int) -> str
Format a fidelity bond locktime as YY-MM-DD (local time).
Source code in jmwallet/src/jmwallet/wallet/rows.py
243 244 245 | |
filter_rows(rows: list[Row], predicate: Callable[[Row], bool] | None = None) -> list[Row]
Pipeline filter stage.
Identity when no predicate is given — the static CLI has no filters yet; the interactive explorer (and possible future CLI flags) will pass one.
Source code in jmwallet/src/jmwallet/wallet/rows.py
332 333 334 335 336 337 338 339 340 | |
group_rows(rows: list[GroupableRow]) -> dict[tuple[int, int], list[GroupableRow]]
Pipeline grouping stage: bucket rows by (mixdepth, branch),
preserving their order within each bucket.
Source code in jmwallet/src/jmwallet/wallet/rows.py
377 378 379 380 381 382 383 | |
sort_rows(rows: list[AddressRowLike], key: Callable[[AddressRowLike], Any] | None = None) -> list[AddressRowLike]
Pipeline sort stage.
Without a key, sorts into :func:base_order_key order. With a user key
(explorer/future CLI flags), rows are first brought into base order so
ties under the user key fall back to the stable base order; the key is
expected to keep address groups together (renderer concern).
Source code in jmwallet/src/jmwallet/wallet/rows.py
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | |
summarize_branch(rows: list[Row], *, mixdepth: int, branch: int, now: int | None = None) -> BranchSummaryRow
Aggregate the rows of a single branch into a :class:BranchSummaryRow.
Expects the rows produced by one :func:build_branch_rows call.
Hidden rows contribute nothing (hidden addresses are empty by
construction). For the fidelity bond branch, funds are additionally
split into locked (locktime in the future) and expired, and the
frozen share of the expired funds is tracked separately so the
spendable (explicit) figure can exclude it.
Source code in jmwallet/src/jmwallet/wallet/rows.py
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 | |
summarize_mixdepth(branches: list[BranchSummaryRow], *, mixdepth: int) -> MixdepthSummaryRow
Aggregate branch summaries into the mixdepth total.
The total includes fidelity bond funds; the spendable figure excludes them (their branch summary reports 0 spendable).
Source code in jmwallet/src/jmwallet/wallet/rows.py
429 430 431 432 433 434 435 436 437 438 439 | |