pub trait OutputLockStore {
type Error: Debug;
type AccountId: Copy + Debug + Eq + Hash;
// Required methods
fn lock_outputs(
&mut self,
outputs: &[OutputRef],
owner: LockOwner,
lock_expiry_height: BlockHeight,
) -> Result<usize, LockError<Self::Error>>;
fn unlock_output(
&mut self,
output: &OutputRef,
owner: LockOwner,
) -> Result<bool, Self::Error>;
fn clear_locked_outputs(
&mut self,
account: Self::AccountId,
) -> Result<usize, Self::Error>;
fn get_locked_outputs(
&self,
account: Self::AccountId,
) -> Result<Vec<OutputRef>, Self::Error>;
}Expand description
The subset of wallet-storage operations that manage output locks.
WalletWrite requires this trait with its Self::AccountId and Self::Error types
tied to the corresponding WalletRead types, so every writable wallet backend is an
OutputLockStore. The separate trait exists so that lock storage can be implemented,
delegated, and reasoned about independently of the full wallet-mutation surface.
Required Associated Types§
Required Methods§
Sourcefn lock_outputs(
&mut self,
outputs: &[OutputRef],
owner: LockOwner,
lock_expiry_height: BlockHeight,
) -> Result<usize, LockError<Self::Error>>
fn lock_outputs( &mut self, outputs: &[OutputRef], owner: LockOwner, lock_expiry_height: BlockHeight, ) -> Result<usize, LockError<Self::Error>>
Locks the specified outputs on behalf of owner so that, by default, they are not
selected for spending at any height less than or equal to the given height.
Locks are advisory. Input selection excludes locked outputs by default, but a caller may
deliberately draw on them by supplying an owner-scoped
LockedInputPolicy (via
SpendPolicy::with_locked_input_policy),
scoped to the lock owners it names; doing so spends through the lock during selection but
never releases it. A locked output can be released only via Self::unlock_output (by its
owner) or Self::clear_locked_outputs.
Returns the number of row updates performed on success (equal to the number of provided
references; a duplicated reference is counted per occurrence), or a LockError on
failure, wrapping either an error from the underlying storage backend or the first output
that could not be locked.
A lock may be acquired when the output holds no lock, when its existing lock has expired
as of the chain tip, or when its existing lock is held by the same owner; in the
latter case the lock’s expiry height is updated. Same-owner re-locking makes the
operation idempotent, so a caller that crashes between locking and persisting its
proposal can safely retry the flow under the same LockOwner. Acquisition fails only
when an unexpired lock is held by a different owner.
Implementations of this method must either succeed completely, successfully locking each provided output on success, or fail completely leaving all lock state unmodified if any of the outputs is actively locked by a different owner.
This is the mechanism by which overlapping proposals for the same account avoid selecting
the same inputs by default. Because note selection and locking cannot be performed as a
single atomic step above the storage layer, two callers may independently select an
overlapping set of outputs before either locks them (a time-of-check/time-of-use race); the
conflict is resolved here, at the storage layer, where the second caller’s lock_outputs
fails with LockError::LockFailure naming the already-locked output. Callers that lock
via a proposal-creation function surface this as
ProposalError::InputsLocked. The
losing caller has not partially locked anything and should treat the failure as “the
account is busy” and retry.
Sourcefn unlock_output(
&mut self,
output: &OutputRef,
owner: LockOwner,
) -> Result<bool, Self::Error>
fn unlock_output( &mut self, output: &OutputRef, owner: LockOwner, ) -> Result<bool, Self::Error>
Unlocks the specified output if it is locked by the given owner, making it once again
available for spending and balance computations.
Returns true if a lock held by owner (whether or not it had already expired) was
removed from the output, and false otherwise: in particular, a lock held by a
different owner is left in place, so one flow cannot accidentally release another’s
locks.
Sourcefn clear_locked_outputs(
&mut self,
account: Self::AccountId,
) -> Result<usize, Self::Error>
fn clear_locked_outputs( &mut self, account: Self::AccountId, ) -> Result<usize, Self::Error>
Unlocks every currently-locked output belonging to the specified account, regardless of lock expiry height.
This is intended as a recovery mechanism for callers that have lost track of their in-flight proposals or PCZTs (for example, because the application was terminated by the operating system before the corresponding transactions could be built). By clearing all locks for the account, the caller declares that it has no pending proposals holding those outputs.
§Warning
This releases every lock for the account regardless of its owner, including locks held by proposals that are still legitimately in flight; those proposals’ inputs immediately become selectable by new proposals, re-creating the conflict that locking exists to prevent. Only call this when no in-flight proposal or PCZT for the account remains.
Returns the number of outputs that were unlocked.
Sourcefn get_locked_outputs(
&self,
account: Self::AccountId,
) -> Result<Vec<OutputRef>, Self::Error>
fn get_locked_outputs( &self, account: Self::AccountId, ) -> Result<Vec<OutputRef>, Self::Error>
Returns the set of currently locked outputs for the given account.
Locked outputs are excluded from note selection, and are tallied separately in balance computations. A wallet needs this to show the user which funds are reserved by an in-flight proposal, so it is part of the ordinary interface rather than a testing affordance.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".