# Class BSV::Wallet::Store <a id="class-BSV-Wallet-Store"></a>

**Inherits:** `Object`
**Includes:** `BSV::Wallet::Interface::Store`

SQL-backed persistence for the wallet.

Abstract base class. Concrete implementations (SQLite, Postgres) provide
database-specific configuration and input-locking semantics.

Usage:
    store = BSV::Wallet::Store.connect('sqlite://wallet.db')
    store.migrate!
    store.create_action(action: { description: 'payment' })

## Constants
### `BASKET_UNSPECIFIED` <a id="constant-BASKET_UNSPECIFIED"></a> <a id="BASKET_UNSPECIFIED-constant"></a>
Sentinel for "basket filter not applied". Distinct from `nil`, which means
"outputs with no basket row" (the spec's unbasketed-outputs semantics).
Distinct from any String, which names a basket.

### `INVALIDATE_BATCH_CHUNK` <a id="constant-INVALIDATE_BATCH_CHUNK"></a> <a id="INVALIDATE_BATCH_CHUNK-constant"></a>
Chunk size for the descent-invalidation UPDATE (HLR #516 Sub 6.2). Half
`VERIFY_BATCH_CHUNK`: the WHERE predicate is more complex (large +action_id IN
(...)+ set plus the +verified_via IS NOT NULL+ gate) and this keeps a single
chunk safely under +SQLITE_MAX_VARIABLE_NUMBER = 32_766+ while leaving planner
headroom on Postgres.

### `VERIFY_BATCH_CHUNK` <a id="constant-VERIFY_BATCH_CHUNK"></a> <a id="VERIFY_BATCH_CHUNK-constant"></a>
Chunk size for +wtxid IN (?...)+ queries. Postgres allows up to 65_535 bind
parameters per statement; 10k leaves headroom for additional predicates and
keeps a single chunk's plan small. Public:
+Engine::AnchorLivenessCache#heights_for_verified+ uses the same ceiling so
every large wtxid-IN query in the wallet respects one bind-limit invariant.

## Attributes
### `db` [R] <a id="attribute-i-db"></a> <a id="db-instance_method"></a>
Returns the value of attribute db.

### `identity_pubkey_hash` [R] <a id="attribute-i-identity_pubkey_hash"></a> <a id="identity_pubkey_hash-instance_method"></a>
Returns the value of attribute identity_pubkey_hash.

## Public Class Methods
### `connect(url, identity_pubkey_hash: = nil, **db_opts)` <a id="method-c-connect"></a> <a id="connect-class_method"></a>
Factory: return a SQLite or Postgres instance based on the URL.
- **@param** `url` [String] database URL (sqlite:// or postgres://)
- **@param** `identity_pubkey_hash` [String, nil] 20-byte +hash160+ of the
wallet's identity pubkey. Required to migrate (the per-wallet
+outputs.spendable_recoverable+ CHECK embeds the WIF-derived
root P2PKH script as a literal — HLR #467). Reading specs that
never call +#migrate!+ may pass +nil+.
- **@param** `db_opts` [Hash] extra options passed through to
+Sequel.connect+. CLI tools omit this (Sequel default pool
suffices for single-process, single-fiber use). The walletd
daemon supplies +max_connections+ sized for its concurrent
fiber inventory after enabling +Sequel.extension(:fiber_concurrency)+.
See #268 + bin/walletd.
- **@return** [BSV::Wallet::Store::SQLite, BSV::Wallet::Store::Postgres]

## Public Instance Methods
### `abort_action(action_id:)` <a id="method-i-abort_action"></a> <a id="abort_action-instance_method"></a>
Not documented.

### `broadcast_provider_for(wtxid:)` <a id="method-i-broadcast_provider_for"></a> <a id="broadcast_provider_for-instance_method"></a>
Not documented.

### `broadcast_status(action_id:)` <a id="method-i-broadcast_status"></a> <a id="broadcast_status-instance_method"></a>
Not documented.

### `child_actions_of(action_id:)` <a id="method-i-child_actions_of"></a> <a id="child_actions_of-instance_method"></a>
Return action_ids of every action whose inputs spend an output of `action_id`.
The forward-walk for reject_action's cascade.

### `clear_broadcast_attempted(action_id:)` <a id="method-i-clear_broadcast_attempted"></a> <a id="clear_broadcast_attempted-instance_method"></a>
Revert `broadcast_at` to NULL on the 503 / backpressure path so the row
returns to the queued / push-discovery set for clean retry next cycle. See
#266 + plan §4.2.

Guarded by +tx_status: nil+: if the SSE listener concurrently delivered SEEN /
REJECTED for the same wtxid (Arcade fanned out an event before responding 503,
or a previous attempt actually made it through), the row has already
transitioned and this clear becomes a no-op. Without the guard, racing the
listener could reset state for an action that has moved on. Material edge case
-- specced.
- **@param** `action_id` [Integer]
- **@return** [Integer] number of rows updated (0 when guarded out, 1
when the clear took effect)

### `complete_internal_action(action_id:, wtxid:, raw_tx:, sign_outputs:, change_outputs:, promote_outputs:)` <a id="method-i-complete_internal_action"></a> <a id="complete_internal_action-instance_method"></a>
Atomically complete an internal (`no_send`) action in one transition: sign,
save its proof, promote its outputs, and make change spendable —so a crash can
never strand a signed-but-unpromoted internal action (#327) or a promoted
action with unspendable change (#328).

Composes the existing per-step methods inside a single
<code>db.transaction</code>; their own +@db.transaction+ blocks flatten into
this one (Sequel reuses the open transaction), so it is all commit-or-rollback
together. This is the same proven pattern +Engine#import_utxo+ Phase 1 uses
inline — lifted into Store so atomicity lives here, not in the Engine.
- **@param** `action_id` [Integer]
- **@param** `wtxid` [String] 32-byte wire-order wtxid
- **@param** `raw_tx` [String] signed raw transaction binary
- **@param** `sign_outputs` [Array<Hash>] outputs for +sign_action+ (empty on the
internal path — real outputs are promoted, not staged)
- **@param** `change_outputs` [Array<Hash>] change outputs for +sign_action+
- **@param** `promote_outputs` [Array<Hash>] output specs to promote

### `configure_db()` <a id="method-i-configure_db"></a> <a id="configure_db-instance_method"></a>
Database-specific setup (PRAGMAs, extensions). Subclasses override.
- **@raise** [NotImplementedError]

### `create_action(action:, inputs: = [])` <a id="method-i-create_action"></a> <a id="create_action-instance_method"></a>
--- Action Lifecycle ---

### `delete_certificate(type:, serial_number:, certifier:)` <a id="method-i-delete_certificate"></a> <a id="delete_certificate-instance_method"></a>
Not documented.

### `descendant_action_ids_of(action_ids:, max_depth: = 100)` <a id="method-i-descendant_action_ids_of"></a> <a id="descendant_action_ids_of-instance_method"></a>
Structural descent walk (HLR #516 Sub 6.2).

Descent linkage (each hop from a parent action to its child):

    outputs.action_id (parent) → outputs.id
                               → inputs.output_id
                               → inputs.action_id (child)

Implemented as a recursive CTE via +Sequel::Dataset#with_recursive+. The CTE
is named `descent` with columns +(action_id, depth)+. The base case seeds
`descent` from `action_ids` at depth 0. The recursive step joins the last
descent row through `outputs` and `inputs` to produce the child action_ids at
+depth + 1+, stopping once the depth column hits `max_depth`.

Three properties matter for correctness:

*   +union_all: false+ so the CTE emits UNION (dedup); a diamond ancestry
    where two paths from X reach Y still yields one Y row.
*   The depth counter caps recursion at `max_depth` (default 100 =
    coinbase-maturity ceiling) — a natural bound above which any descendant is
    beyond every re-org's reach.
*   The depth cap is also the cycle guard. A contrived cyclic input graph
    (shouldn't exist in real tx graphs but the CHECKs don't forbid it) would
    re-visit rows through dedup; combined with the depth ceiling, the walk
    terminates in bounded time regardless.

Returns the seed ids plus every descendant reachable within the depth cap. The
caller feeds this set to `invalidate_verification` for coarse row-clearing.
- **@param** `action_ids` [Array<Integer>, Set<Integer>] seed action_ids
- **@param** `max_depth` [Integer] recursion ceiling (default 100)
- **@return** [Set<Integer>] seed action_ids + all transitive descendants

### `disconnect()` <a id="method-i-disconnect"></a> <a id="disconnect-instance_method"></a>
Not documented.

### `find_action(id: = nil, wtxid: = nil, reference: = nil)` <a id="method-i-find_action"></a> <a id="find_action-instance_method"></a>
Not documented.

### `find_block(height:)` <a id="method-i-find_block"></a> <a id="find_block-instance_method"></a>
Not documented.

### `find_or_create_basket(name:)` <a id="method-i-find_or_create_basket"></a> <a id="find_or_create_basket-instance_method"></a>
Not documented.

### `find_or_create_labels(names:)` <a id="method-i-find_or_create_labels"></a> <a id="find_or_create_labels-instance_method"></a>
--- Labels, Tags, Baskets ---

### `find_or_create_tags(names:)` <a id="method-i-find_or_create_tags"></a> <a id="find_or_create_tags-instance_method"></a>
Not documented.

### `find_output(id:)` <a id="method-i-find_output"></a> <a id="find_output-instance_method"></a>
--- Queries ---

### `find_proof(wtxid:)` <a id="method-i-find_proof"></a> <a id="find_proof-instance_method"></a>
Not documented.

### `find_spendable(satoshis:, basket: = BASKET_UNSPECIFIED, exclude: = [])` <a id="method-i-find_spendable"></a> <a id="find_spendable-instance_method"></a>
`basket` accepts the same vocabulary as `query_outputs`:
    - omitted (default) → all spendable outputs.
    - +nil+              → unbasketed outputs only (no +output_baskets+ row).
    - +String+           → outputs in that named basket.
    - +Array<String>+    → outputs in any listed basket.

### `get_setting(key:)` <a id="method-i-get_setting"></a> <a id="get_setting-instance_method"></a>
--- Settings ---

### `header_at(height:)` <a id="method-i-header_at"></a> <a id="header_at-instance_method"></a>
The raw 80-byte header at `height`, or `nil` when the height holds no row or
only a trusted-service (header-NULL) row. The presence of a non-nil return is
the "this height is locally validated" signal.
- **@param** `height` [Integer]
- **@return** [String, nil] the 80 raw wire bytes, or nil

### `increment_broadcast_retry(action_id:)` <a id="method-i-increment_broadcast_retry"></a> <a id="increment_broadcast_retry-instance_method"></a>
Bump the broadcasts.retry_count for an action whose reject_action raised
CannotRejectInternalActionError. Visibility into stuck rows for dashboards;
the row itself stays alive so the resolution loop re-encounters it next cycle.

### `initialize(url: = nil, db: = nil, identity_pubkey_hash: = nil, db_opts: = {})` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
- **@return** [Store] a new instance of Store

### `invalidate_stale_anchors!(heights_to_roots:)` <a id="method-i-invalidate_stale_anchors-21"></a> <a id="invalidate_stale_anchors!-instance_method"></a>
Anchor-liveness invalidation writer (HLR #516 Sub 6.1).

Clears the three verification-cache columns (`verified_at`, `verified_via`,
`verifier_version`) on any `tx_proofs` row whose stored anchor disagrees with
the tracker's current view. The anchor key is +(block_height, computed_root)+
in wire-order 32-byte binary bytes — NOT hex, NOT BUMP-encoded bytes. The
persisted `merkle_path` is folded through the SDK's +MerklePath#compute_root+
so that BUMP-encoding variability (offset-0 leaf duplicates, unbalanced
padding, hash-side ordering) closes at the canonical root before comparison —
two BUMPs with the same computed root produce identical invalidation decisions
regardless of their on-wire bytes.

Coarse-clear rule: all three columns move together to satisfy the
`verification_state_coherent` CHECK. Only rows already carrying a trust mark
(+verified_via IS NOT NULL+) are touched; unverified rows are left alone (no
work for the descendant walker to undo).

`heights_to_roots` semantics:
*   present with a 32-byte value ⇒ compare, invalidate on mismatch.
*   present with `nil`          ⇒ height is "unknown" to the tracker
    (transient outage, sync gap) — never invalidate. The trust set must not
    decay under a network blip.
*   height absent from the Hash ⇒ untouched (caller did not ask).

No chain_tracker dependency here — this method is a pure writer.
<code>Engine::AnchorLivenessCache</code> resolves `heights_to_roots` from the
tracker and hands it in.
- **@param** `heights_to_roots` [Hash{Integer => String, nil}] wire-order
32-byte binary current roots keyed by block height
- **@return** [Array<Integer>] +actions.id+ values whose proofs were
invalidated (needed by Sub 6.2's descendant walk)

### `invalidate_verification(action_ids:)` <a id="method-i-invalidate_verification"></a> <a id="invalidate_verification-instance_method"></a>
Shared row-clearing primitive for verification-cache invalidation (HLR #516
Sub 6.2). Called by the descent walk wired into
<code>Engine::AnchorLivenessCache</code> after
<code>invalidate_stale_anchors!</code> returns invalidated anchor
`action_ids`.

Two gates on the UPDATE:

*   +action_id IN (chunk)+ — the coarse-cleared descent set
*   +verified_via IS NOT NULL+ — the security-specialist DoS defence. The
    descent WALK is unbounded on the read side (structural descendants can be
    poisoned by an adversary grafting synthetic rows); the UPDATE is bounded
    to rows carrying a trust mark. Rows without `verified_via` have no cache
    state to clear and would trip the coherent CHECK.

All three verification columns move together in one UPDATE so
`verification_state_coherent` is satisfied atomically.
`INVALIDATE_BATCH_CHUNK` (5k rows/statement) keeps the per-chunk
bind-parameter count and planner cost bounded on both backends.
- **@param** `action_ids` [Array<Integer>, Set<Integer>]
- **@return** [Integer] rows cleared

### `label_action(action_id:, label_ids:)` <a id="method-i-label_action"></a> <a id="label_action-instance_method"></a>
Not documented.

### `link_proof(action_id:, tx_proof_id:)` <a id="method-i-link_proof"></a> <a id="link_proof-instance_method"></a>
Not documented.

### `load_sse_cursor(token:)` <a id="method-i-load_sse_cursor"></a> <a id="load_sse_cursor-instance_method"></a>
--- SSE Cursors ---

### `lock_inputs(action_id:, inputs:)` <a id="method-i-lock_inputs"></a> <a id="lock_inputs-instance_method"></a>
Not documented.

### `mark_broadcast_attempted(action_id:)` <a id="method-i-mark_broadcast_attempted"></a> <a id="mark_broadcast_attempted-instance_method"></a>
Not documented.

### `mark_transmission_acked(action_id:, counterparty:, wtxids: = [], acked_at: = Time.now)` <a id="method-i-mark_transmission_acked"></a> <a id="mark_transmission_acked-instance_method"></a>
Mark a transmission acknowledged by the peer and record the wtxids that the
BEEF carried (the known-set, written here and only here — two-phase, HLR
#385). Idempotent: re-ack updates `acked_at` and adds only wtxids not already
recorded.

Returns the transmission id, or `nil` when no matching row exists for
(action_id, counterparty).
- **@param** `action_id` [Integer]
- **@param** `counterparty` [String] BRC-43 identity pubkey (66-char hex)
- **@param** `wtxids` [Array<String>] wire-order binary wtxids carried by the BEEF
- **@param** `acked_at` [Time] ack timestamp
- **@return** [Integer, nil]

### `mark_verified(wtxid:, via:, at_time: = nil)` <a id="method-i-mark_verified"></a> <a id="mark_verified-instance_method"></a>
Not documented.

### `mark_verified_batch(wtxids:, via:, at_time: = nil)` <a id="method-i-mark_verified_batch"></a> <a id="mark_verified_batch-instance_method"></a>
Not documented.

### `max_block_height()` <a id="method-i-max_block_height"></a> <a id="max_block_height-instance_method"></a>
Not documented.

### `max_verifier_version_seen()` <a id="method-i-max_verifier_version_seen"></a> <a id="max_verifier_version_seen-instance_method"></a>
Not documented.

### `migrate!(target: = nil)` <a id="method-i-migrate-21"></a> <a id="migrate!-instance_method"></a>
Run pending migrations against this wallet's database. Populates
<code>BSV::Wallet::Migration.identity_pubkey_hash</code> (and the matching
<code>models::Output.expected_root_script</code> class accessor) from
+@identity_pubkey_hash+ so the per-wallet
<code>outputs.spendable_recoverable</code> CHECK literal can be built at
+CREATE TABLE+ time. Resets the global in `ensure` — the migrator is the only
consumer of the global, and a leaked value across wallets would silently
mis-bake the next wallet's schema.

### `pending_proofs(limit: = 100)` <a id="method-i-pending_proofs"></a> <a id="pending_proofs-instance_method"></a>
Not documented.

### `pending_resolutions(limit: = 100)` <a id="method-i-pending_resolutions"></a> <a id="pending_resolutions-instance_method"></a>
Not documented.

### `pending_submissions(limit: = 100)` <a id="method-i-pending_submissions"></a> <a id="pending_submissions-instance_method"></a>
Not documented.

### `promote_action(action_id:, outputs:)` <a id="method-i-promote_action"></a> <a id="promote_action-instance_method"></a>
Internal-path Phase 4: write outputs as already promoted, insert spendable
rows in the same transaction. Used by incoming actions (internalize_action,
import_utxo, wbikd) where broadcast_intent == 'none' — outputs join the
canonical UTXO set immediately.

### `promote_action_outputs(action_id:, authorising_status:)` <a id="method-i-promote_action_outputs"></a> <a id="promote_action_outputs-instance_method"></a>
Send-path Phase 4: record the promotions row (the canonical-state fact) and
insert spendable rows for wallet-owned outputs. Called when the broadcast is
accepted (inline or via the daemon) with the authorising tx_status. Idempotent
— a second invocation finds the promotions row present and is a no-op. The
promotions row's composite FK to broadcasts(action_id, tx_status) means it can
only be created while the broadcast holds that (non-rejected) status.

### `promote_change_to_spendable(action_id:)` <a id="method-i-promote_change_to_spendable"></a> <a id="promote_change_to_spendable-instance_method"></a>
Not documented.

### `proof_exists?(wtxid:)` <a id="method-i-proof_exists-3F"></a> <a id="proof_exists?-instance_method"></a>
- **@return** [Boolean]

### `query_actions(labels:, label_query_mode: = :any, limit: = 10, offset: = 0, include_labels: = false, include_inputs: = false, include_input_locking_scripts: = false, include_input_unlocking_scripts: = false, include_outputs: = false, include_output_locking_scripts: = false)` <a id="method-i-query_actions"></a> <a id="query_actions-instance_method"></a>
Not documented.

### `query_certificates(certifiers:, types:, limit: = 10, offset: = 0)` <a id="method-i-query_certificates"></a> <a id="query_certificates-instance_method"></a>
Not documented.

### `query_change_output_vouts(action_id:)` <a id="method-i-query_change_output_vouts"></a> <a id="query_change_output_vouts-instance_method"></a>
Not documented.

### `query_outputs(basket: = BASKET_UNSPECIFIED, tags: = nil, tag_query_mode: = :any, aggregate: = nil, limit: = 10, offset: = 0, include_locking_scripts: = false, include_custom_instructions: = false, include_tags: = false, include_labels: = false)` <a id="method-i-query_outputs"></a> <a id="query_outputs-instance_method"></a>
Query spendable outputs with optional filters and aggregation.
- **@param** `basket` [String, Array<String>, nil, omitted] - omitted (default) → no basket filter; all spendable outputs match.
- +String+           → outputs in that named basket.
- +Array<String>+    → outputs in any of the named baskets.
- +nil+              → outputs with no +output_baskets+ row (unbasketed).
- **@param** `aggregate` [:sum, :count, nil] - +nil+ (default) → returns +{ total:, outputs: }+ (paginated rows + match count).
- +:sum+          → returns Integer (sum of +satoshis+ over matched outputs).
- +:count+        → returns Integer (count of matched outputs).

### `reap_action(action_id:)` <a id="method-i-reap_action"></a> <a id="reap_action-instance_method"></a>
Reclaim a single orphaned action (#325). Tears the action down in one
transaction and deletes it, cascading its `inputs` rows so the locked UTXOs
return to the spendable set.

Re-validates inside the transaction: the action may have advanced past
reapability between discovery and here — promoted, or signed (which creates a
broadcasts row and hands it to the broadcast loops). A +FOR UPDATE+ row lock
closes the check-then-delete race: `sign_action` updates the action row, so it
serialises against this lock. Returns `false` (no delete) when the action is
no longer reapable — the caller emits <code>task.skipped</code>.
- **@param** `action_id` [Integer]
- **@return** [Boolean] true if reclaimed, false if no longer reapable

### `record_block_header(height:, merkle_root:, block_hash: = nil, header: = nil)` <a id="method-i-record_block_header"></a> <a id="record_block_header-instance_method"></a>
Persist a `blocks` row, append-or-reject (#335).

The `blocks` table carries two row flavours distinguished by the `header`
column. A <strong>trusted-service</strong> row (header NULL) records only the
`merkle_root` a chain-query Service handed back. A **validated** row (header
present, the raw 80 bytes) is one whose PoW the wallet checked locally — its
presence is the structural "this height is validated" signal (there is no
status column). The `header_root_match` CHECK ties the indexed `merkle_root`
to the bytes the header embeds, so the two can never disagree.

Append-or-reject preserves the validated chain as evidence (and the
competing-header reorg trace, #245):

*   Passing <code>header:</code> writes/upgrades a validated row. At an
    already-validated height carrying a **different** header, this raises
    {CompetingBlockHeaderError} rather than overwriting — a fork at an
    occupied height is reorg evidence to investigate, not an upsert to
    silently win. The same header re-presented is an idempotent no-op. A
    header-NULL row at that height is **upgraded** in place (trusted →
    validated).
*   The trusted-service path (no <code>header:</code>) may still refresh
    `merkle_root` / `block_hash`, but its update is scoped to header-NULL
    rows: it can never downgrade a header-bearing row to NULL, nor mutate the
    `merkle_root` a validated header pins (which would trip
    `header_root_match`).
- **@param** `height` [Integer]
- **@param** `merkle_root` [String] 32 wire bytes (or hex; coerced)
- **@param** `block_hash` [String, nil] 32 wire bytes (or hex; coerced)
- **@param** `header` [String, nil] raw 80-byte header — present ⇒ validated row
- **@raise** [BSV::Wallet::CompetingBlockHeaderError] on a conflicting validated header

### `record_broadcast_provider(wtxid:, provider:)` <a id="method-i-record_broadcast_provider"></a> <a id="record_broadcast_provider-instance_method"></a>
Not documented.

### `record_broadcast_result(action_id:, tx_status:, arc_status: = nil, block_hash: = nil, block_height: = nil, merkle_path: = nil, extra_info: = nil, competing_txs: = nil)` <a id="method-i-record_broadcast_result"></a> <a id="record_broadcast_result-instance_method"></a>
--- Broadcasts ---

### `record_transmission(action_id:, counterparty:)` <a id="method-i-record_transmission"></a> <a id="record_transmission-instance_method"></a>
Record that `action_id`'s BEEF was (or is being) transmitted to
`counterparty`. Idempotent and concurrency-safe at grain (action, peer) via
+INSERT ... ON CONFLICT DO UPDATE RETURNING id+: a re-transmit refreshes
`updated_at` and returns the same row.

NOTE: this does NOT write `transmission_txids` rows. The known-set is written
only on ack (`mark_transmission_acked`) — recording wtxids the peer never
received would over-trim a future BEEF into unverifiability (HLR #385
two-phase gate).
- **@param** `action_id` [Integer]
- **@param** `counterparty` [String] BRC-43 identity pubkey (66-char hex)
- **@return** [Integer] transmission id

### `reject_action(action_id:)` <a id="method-i-reject_action"></a> <a id="reject_action-instance_method"></a>
Unwind a broadcast action whose network outcome was terminal- rejected.
Speculatively-promoted outputs (the wallet's optimistic bet on a non-rejected
ARC response) get rolled back along with every dependent — and cascades
forward through any child action that consumed this action's outputs,
recursively.

Single outer transaction. Children are torn down before the parent so
outputs.action_id RESTRICT doesn't block the parent's output deletes (a
child's input row references this action's output; deleting the output
requires the input gone first, which happens when the child's action_id
CASCADE-deletes its inputs).

Raises CannotRejectInternalActionError if the target — or any cascade
descendant — has broadcast_intent='none'. Internal-path actions produce
canonical wallet state and are not the domain of this method; encountering one
mid-cascade means an invariant was violated upstream. Rollback leaves the
broadcasts row intact for the resolution loop to discover next cycle.

Idempotent: calling on an already-deleted action_id is a no-op.

### `relinquish_output(output_id:)` <a id="method-i-relinquish_output"></a> <a id="relinquish_output-instance_method"></a>
Not documented.

### `resolve_inputs_for_signing(action_id:)` <a id="method-i-resolve_inputs_for_signing"></a> <a id="resolve_inputs_for_signing-instance_method"></a>
--- Input Resolution ---

### `sanity_sweep_verified_anchors!(chain_tracker:, sample_size: = 100)` <a id="method-i-sanity_sweep_verified_anchors-21"></a> <a id="sanity_sweep_verified_anchors!-instance_method"></a>
Boot-time cache sanity sweep (HLR #516 Sub 6.3, failsafe-for- failsafe).
Samples a bounded number of `tx_proofs` rows whose verification is +'spv'+ and
whose `merkle_path` is populated, then compares each row's computed root
against the `chain_tracker`'s current view at the same block height. On
divergence: log via <code>BSV.logger.warn</code>.

This method does NOT invalidate rows — that job belongs to
<code>invalidate_stale_anchors!</code> /
<code>Engine::AnchorLivenessCache</code> on the per-verify-walk path. This is
a divergence **detector**: it surfaces silent drift (schema-level bug,
tracker-write race, migration corruption) that the per-walk path would
eventually catch on next reference but might not for cold rows.

Env-gated by +BSV_WALLET_VERIFY_BOOT_SWEEP=1+. Unset → the whole method is a
no-op (CLI tools boot fast, the daemon opts in). Non-fatal at every failure
surface: DB errors, `chain_tracker` outages, empty samples, and unparseable
BUMPs all fall through to a debug log and return.

Not part of any per-walk hot path. Cost is a single indexed SELECT bounded by
`sample_size` plus one batched `known_roots_for_heights` call — an order of
magnitude below the ongoing chain-tracker traffic even at daemon boot.
- **@param** `chain_tracker` [#known_roots_for_heights] anchor-liveness-
capable chain tracker
- **@param** `sample_size` [Integer] upper bound on rows to sample
- **@return** [Integer] number of rows whose stored root diverged from
the tracker's current view (0 when the sweep did nothing)

### `save_certificate(certificate)` <a id="method-i-save_certificate"></a> <a id="save_certificate-instance_method"></a>
--- Certificates ---

### `save_proof(wtxid:, proof:)` <a id="method-i-save_proof"></a> <a id="save_proof-instance_method"></a>
--- Proofs ---

### `save_sse_cursor(token:, last_event_id:)` <a id="method-i-save_sse_cursor"></a> <a id="save_sse_cursor-instance_method"></a>
Not documented.

### `set_setting(key:, value:)` <a id="method-i-set_setting"></a> <a id="set_setting-instance_method"></a>
Not documented.

### `sign_action(action_id:, wtxid:, raw_tx:, outputs: = [], change_outputs: = [])` <a id="method-i-sign_action"></a> <a id="sign_action-instance_method"></a>
Not documented.

### `stage_action(action_id:, wtxid:, raw_tx:, outputs: = [])` <a id="method-i-stage_action"></a> <a id="stage_action-instance_method"></a>
Not documented.

### `stale_action_ids(threshold:, limit:)` <a id="method-i-stale_action_ids"></a> <a id="stale_action_ids-instance_method"></a>
Discovery side of the reaper (#325). Returns up to `limit` IDs of orphaned
actions ready to reclaim, for the Scheduler loop to push to Engine::Reaper.

The reaper reclaims **orphaned** actions — structurally valid states (the
schema forbids invalid ones) that hold locked inputs but have no recovery
owner and can no longer progress. An action is reapable when it is past the
staleness threshold, has no promotions row (promoted is protected), has no
broadcasts row, and is either broadcastable (+broadcast_intent != 'none'+) or
pre-sign (+wtxid IS NULL+).

Two ownership boundaries:

*   **No broadcasts row** (PR #379): `sign_action` creates the broadcasts row,
    so any signed broadcastable action is owned by the broadcast loops —
    success promotes it, terminal failure unwinds it via `reject_action`.
    Reaping one would release inputs of a tx that may already be on the
    network (a crash mid-submit leaves `broadcast_at` stamped but no status) —
    a double-spend.
*   **Broadcastable OR pre-sign**: internal (+intent 'none'+) actions complete
    synchronously via `complete_internal_action`, which signs and promotes
    atomically — so a **signed** internal action is either promoted (excluded
    above) or a deliberately-parked/OP_RETURN completion (kept). Only a
    <strong>pre-sign</strong> internal action (+wtxid IS NULL+) is an orphaned
    mid-funding lock with no owner. Reaping those, but never a signed internal
    action, closes the internal pre-sign leak (#329) without touching
    completed internal state.
- **@param** `threshold` [Integer] age in seconds
- **@param** `limit` [Integer] max IDs per discovery pass
- **@return** [Array<Integer>] stale action IDs

### `sweepable_state()` <a id="method-i-sweepable_state"></a> <a id="sweepable_state-instance_method"></a>
Snapshot of "would dropping this DB destroy on-chain-anchored state?"

Returns a {SweepableState} carrying `at_risk_outputs` (count of spendable
derived outputs whose action has been signed and broadcast) and
`at_risk_actions` (distinct actions owning them). <code>clean?</code> is true
iff `at_risk_outputs` is zero. Consult before any destructive operation (DB
drop, blank-slate reset, spec setup recreation, future +bsv-wallet destroy+
CLI). HLR #448.

The query intentionally excludes:
    * Root outputs (no derivation triple) — recoverable from the
      identity key alone, so destroying them costs only re-import.
    * Unsigned / aborted actions (+actions.wtxid IS NULL+) — no
      broadcast happened, so nothing on chain to orphan.
- **@return** [SweepableState]

### `transmission_known_wtxids(counterparty:)` <a id="method-i-transmission_known_wtxids"></a> <a id="transmission_known_wtxids-instance_method"></a>
The set of wtxids `counterparty` has acknowledged across every transmission to
them — the BeefParty known set for trimming the next BEEF. Wire-order binary
wtxids; deduplicated across transmissions.
- **@param** `counterparty` [String] BRC-43 identity pubkey (66-char hex)
- **@return** [Array<String>] wire-order binary wtxids

### `validated_tip(from_height:)` <a id="method-i-validated_tip"></a> <a id="validated_tip-instance_method"></a>
Highest height of the contiguous run of **validated** (header-present) rows
that starts at `from_height` (the checkpoint) — the validated tip (#335).

Structural and gap-stopping: a header-island sitting above a missing height is
NOT the validated tip. Walks the ascending set of header-present heights
at/above `from_height` and returns the last height before the first break in
the +h, h+1, h+2, …+ sequence. Returns `nil` when `from_height` itself is not
validated (the chain has not even been seeded).
- **@param** `from_height` [Integer] the checkpoint height (run anchor)
- **@return** [Integer, nil] the validated tip, or +nil+ if unseeded

### `verification_state(wtxid:)` <a id="method-i-verification_state"></a> <a id="verification_state-instance_method"></a>
Not documented.

### `verified_wtxids(wtxids:, version_at_least:, via_in:)` <a id="method-i-verified_wtxids"></a> <a id="verified_wtxids-instance_method"></a>
Not documented.

### `verify_schema!()` <a id="method-i-verify_schema-21"></a> <a id="verify_schema!-instance_method"></a>
Verify the per-wallet <code>outputs.spendable_recoverable</code> CHECK literal
in the database matches the expected root P2PKH script for the WIF currently
driving this wallet (HLR #467). Catches schema drift, restore-to-wrong-DB, and
WIF rotation — any of which would let the wallet sign spends against a CHECK
that no longer mirrors its identity.

No-op on SQLite (pragma_check / sqlite_master parsing isn't round-trip stable
across versions; the SQLite path is for fast logic-only specs that don't
exercise the per-wallet CHECK).
- **@raise** [BSV::Wallet::SchemaIntegrityError] when the literal does
not match +identity_pubkey_hash+ or cannot be located.

### `verify_verifier_version!()` <a id="method-i-verify_verifier_version-21"></a> <a id="verify_verifier_version!-instance_method"></a>
Verify the running binary's `VERIFIER_VERSION` is not older than the highest
version stamped into `tx_proofs` (ADR-033 / HLR #516). A downgraded binary
that trusted higher-version rows would honour entries produced under logic it
can no longer reproduce.
- **@raise** [BSV::Wallet::SchemaIntegrityError] when the code's version
is lower than +MAX(tx_proofs.verifier_version)+
