Skip to main content

Module locking

Module locking 

Source
Expand description

§Output locking

This module is the single home for the wallet’s advisory output-locking vocabulary: the identity of a lock holder (LockOwner), the error surface of lock acquisition (LockError), the storage contract through which wallet backends persist lock state (OutputLockStore), the request through which proposal-creation functions acquire locks (LockRequest and unlock_proposal_inputs), and the policy types through which input selection interacts with lock state (LockedInputPolicy and LockFilter).

§Semantics

All lock state is a pair of columns on each received output: a lock_expiry_height and a lock_owner. Every behavior is derived from the following invariants, stated here once:

  • Locked: an output is locked while lock_expiry_height >= target_height, where the target height is the height at which a new transaction would be mined (chain tip + 1). Balance computations tally an output as locked under exactly this condition.
  • Eligible for selection: input selection eligibility is the exact complement of the locked condition, owner-scoped under a LockedInputPolicy: an output is eligible when it carries no lock, when its lock has expired (lock_expiry_height < target_height), or when its lock_owner is one of the owners the policy admits. An output locked by any other owner is never selected.
  • Acquisition: a lock may be acquired (OutputLockStore::lock_outputs) when the output is unlocked, when its existing lock has expired as of the chain tip, or when the existing lock is held by the same owner (an idempotent re-acquire/extend, so a flow that crashed after locking may safely retry under its original owner token). Acquisition fails only on an active foreign lock. There is no stealing: because “expired as of the chain tip” (h <= chain_tip) is exactly “not locked for selection” (h < target_height), a foreign lock is replaceable exactly when the output has already become selectable again.
  • Advisory: locks are advisory in selection. An owner-scoped policy override spends through a lock during selection; it never releases the lock.
  • Release: there are exactly four release paths: owner-scoped unlock (OutputLockStore::unlock_output, unlock_proposal_inputs), owner-agnostic clearing (OutputLockStore::clear_locked_outputs), unlock-on-store (implementations of WalletWrite::store_transactions_to_be_sent unlock outputs recorded as spent, the spend records having taken over double-selection protection), and expiry (the passage of the chain tip beyond lock_expiry_height).
  • Expiry re-opens the race: if an operation outlasts its lock window, the lock expires and a concurrent proposal may select and spend the same outputs. Lock windows must be chosen conservatively with respect to the worst-case time between proposal creation and transaction storage.

§Integration map

The locking feature touches the codebase at the following points, by role:

  • Schema: the note_locking migration in zcash_client_sqlite (wallet::init::migrations::note_locking) adds the lock_expiry_height and lock_owner columns to the four received-output tables; the table definitions in zcash_client_sqlite::wallet::db carry them forward.
  • Acquisition: the five proposal-creation functions (propose_transfer, propose_standard_transfer_to_address, propose_send_max_transfer, propose_shielding, and propose_shielding_coinbase) accept an optional LockRequest and lock every selected input via OutputLockStore::lock_outputs.
  • Selection: the seven zcash_client_sqlite leaf queries that select spendable outputs (get_spendable_note, select_unspent_notes, select_spendable_notes_matching_value, and unspent_notes_meta in wallet::common; get_spendable_transparent_outputs, get_spendable_transparent_outputs_for_addresses, and select_spendable_transparent_outputs in wallet::transparent) embed the eligibility fragment parameterized by a LockFilter.
  • Balance: four classification sites tally locked value separately: the shielded-note branch of get_wallet_summary and the three transparent tallies (get_transparent_balances and the two branches of add_transparent_account_balances, including the coinbase arm) in zcash_client_sqlite.
  • Release: the four release paths listed under Semantics above.
  • Proposal decoding: proposal decoding in crate::proto reads wallet contents with LockFilter::Unfiltered, since a persisted proposal must be reconstructible regardless of current lock state.
  • Consumers: zcash_client_sqlite’s PoolMigrations::migration_lock_owners exposes the owners of a pool migration’s in-flight locks so callers can construct owner-scoped policies; Zallet consumes the locking API for its transaction flows.

Macros§

ambassador_impl_OutputLockStore
A macro to be used by [ambassador::Delegate] to delegate OutputLockStore

Structs§

LockOwner
An opaque token identifying the holder of an output lock.
LockRequest
A request to lock the inputs selected by a proposal, made when calling one of the proposal-creation functions (propose_transfer and friends).

Enums§

LockError
Errors that occur when attempting to lock an output.
LockFilter
How a query filters candidate outputs by lock state.
LockedInputPolicy
Governs whether input selection may draw on locked outputs, and with what preference.

Traits§

OutputLockStore
The subset of wallet-storage operations that manage output locks.

Functions§

unlock_proposal_inputs
Unlocks all inputs selected by the given proposal, reversing the locks acquired when the proposal was created with a LockRequest under the same owner.