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

**Inherits:** `Object`

Wallet-to-peer BEEF delivery domain — sibling to
<code>Engine::Broadcast</code> and <code>Engine::TxProof</code> over the
shared <code>Engine::Hydrator</code> substrate. Tracked by HLR #385; design
recorded in ADR-025 and <code>docs/reference/transactions.md</code>.

Background-worker sibling shape: no <code>Interface::Transmission</code>
module (only shape-extracted services consumed cross-sibling, e.g. `Hydrator`
and `BeefImporter`, carry interface modules). This matches `Broadcast` and
`TxProof`.

Domain distinction: broadcast ships Extended Format to the miner network
(anonymous, fungible) for consensus validation; transmit ships Atomic BEEF to
a named peer for SPV. The recipient's **job** fixes the wire shape, not its
knowledge — peer-knowledge is the orthogonal trimming axis (BeefParty, layered
on top of BEEF, not mirrored into broadcast). Per-counterparty state is the
deciding difference and lives here, never in `Broadcast`.

Synchronicity is an invocation mode, not a property of <code>#transmit</code>.
v1 ships sync (an inline caller awaits a self-contained
<code>#transmit</code>), and the same operation must be drivable async by the
daemon later — single code path, mirroring `Broadcast`'s `broadcast_intent`
inline/delayed split (ADR-024 / #183 inline-equals-delayed robustness).

## Constants
### `BRC43_COMPRESSED_LOWERCASE` <a id="constant-BRC43_COMPRESSED_LOWERCASE"></a> <a id="BRC43_COMPRESSED_LOWERCASE-constant"></a>
BRC-43 canonical counterparty hex: compressed pubkey (02|03 prefix),
lowercase. Mirrors the Postgres CHECK on the transmissions table in
<code>db/migrations/001_create_schema.rb</code> so the engine boundary rejects
the same shapes the schema rejects — closes the H1 correctness drift where
uppercase hex passed the engine then died as
<code>Sequel::CheckConstraintViolation</code> at DB write time.

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

## Public Instance Methods
### `initialize(store:, hydrator:, delivery: = nil)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
Explicit DI — no engine back-reference. Mirrors sibling shape (`Broadcast`,
`TxProof`, `Hydrator`, `BeefImporter`).
- **@param** `store` [BSV::Wallet::Store]
- **@param** `hydrator` [BSV::Wallet::Engine::Hydrator] shared-substrate
owner of +#build_atomic_beef+ (the BEEF the peer receives).
- **@param** `delivery` [#deliver, nil] Phase-2 transport seam, wired
in #390 (caller-supplied-endpoint synchronous HTTP delivery
via +Network::PeerDelivery+). Nil-tolerant in unit-spec
contexts and in this skeleton task; left here so #390's wiring
slots in by constructor arg, not class shape change.
- **@return** [Transmission] a new instance of Transmission

### `transmit(counterparty:, action_id:, outputs:, sender_identity_key:, endpoint: = nil)` <a id="method-i-transmit"></a> <a id="transmit-instance_method"></a>
Construct the wire payload for a peer BEEF delivery and record the
transmission row at grain (action × counterparty).

Order: validate counterparty at the engine boundary (curve-point shape, no
+'self'<code>/</code>'anyone'+ sentinels) BEFORE any DB write or BEEF
construction. A typo'd hex must never produce a phantom `transmissions` row.

Per-peer trimming (#388). The full atomic BEEF is built first, then handed to
a fresh <code>Transaction::BeefParty</code> alongside the peer's already-known
wtxids — pre-fetched ONCE from the Store at the top of this method, so
downstream never calls back into the store. Known entries are demoted to
`TxidOnlyEntry` via `make_txid_only` before `trimmed_beef_for_party` drops
them —the SDK's BeefParty trim only removes `TxidOnlyEntry` records, so the
demote step is what actually shrinks the wire when the ancestor arrived in our
bundle as a `ProvenTxEntry`. The subject (this action's `wtxid`) is always
retained in full (guarded post-trim — see Subject-protection below).

Fresh `BeefParty` per call is load-bearing: +BeefParty#merge_txid_only+
mutates the receiving party's state, so reusing an instance across
counterparties would accumulate ghost TXID-only entries in the egress bundle.
Each <code>#transmit</code> constructs its own.

Subject-protection (defence-in-depth, HLR #385 crypto gate). The subject
should never appear in the peer's known-set by construction (it is the new
transaction we are transmitting). If it does — a poisoned `transmission_txids`
row — the demote step above would turn the subject into a `TxidOnlyEntry` and
the trim would drop it, shipping a BEEF the peer cannot SPV-verify. The
post-trim invariant catches both cases (subject missing OR demoted to
`TxidOnlyEntry`) and raises <code>BSV::Wallet::Error</code> BEFORE
serialisation.

Egress SPV-honesty contract (HLR #385 Task 4 / #389). Post-trim and BEFORE
`record_transmission`, the trimmed bytes go through
+Hydrator#validate_for_handoff!+ with +allow_txid_only: true+ —trim
deliberately produces `TxidOnlyEntry` records (entries the peer already
holds), so structural verification must tolerate them. A failure raises
`EgressBeefInvalidError` which propagates unchanged; no transmission row is
written.

Two-phase invariant (HLR #385): this method does NOT write
`transmission_txids`. The returned `sent_wtxids` is what the ACK handler
(#390) will pass to `mark_transmission_acked` once the peer confirms receipt —
recording wtxids the peer has not yet acknowledged would over-trim a future
BEEF into unverifiability.

Return shape carries everything #390 (deliver) needs without re-querying:
    - +transmission_id+ — canonical state reference (the row),
      mirroring +Engine::Broadcast+'s return-the-row idiom; lets
      #390 thread the same id into +mark_transmission_acked+.
    - +beef+ — wire-format trimmed Atomic BEEF binary
      (+to_atomic_binary(subject_wtxid)+ over the BeefParty
      output).
    - +sent_wtxids+ — the non-TxidOnly wtxids in the trimmed
      BEEF: what the peer actually receives + can keep. #390's
      ACK handler passes these to +mark_transmission_acked+.
    - +outputs+ — BRC-29 derivation metadata for the peer's
      +internalize_action+; passed through unchanged to #390's
      envelope (without it, the peer has BEEF bytes but cannot
      recover the locking key).
    - +sender_identity_key+ — BRC-29 sender identity; same
      passthrough role in #390's envelope.

Phase-1 delivery (#390). When an <code>endpoint:</code> is supplied, the
trimmed BEEF is POSTed via +@delivery+ AFTER the row is written. ACK success
drives `mark_transmission_acked` — the two-phase boundary stays intact:
`transmission_txids` are only ever written on confirmed delivery, so a future
BEEF can never over-trim against a stale knowledge claim. When
<code>endpoint:</code> is nil (deferred-by-caller path), the row is recorded
and the caller decides how/when to deliver — same shape as
<code>Engine::Broadcast</code> which separates `submit` from the delayed
broadcast worker.
- **@param** `counterparty` [String] BRC-43 identity pubkey hex
(66-char compressed, +02+/+03+ prefix, **lowercase**).
Validated at the engine boundary by
+Transmission#validate_counterparty!+ — the BRC-43 canonical
form (+\A0[23][0-9a-f]{64}\z+) which mirrors the Postgres
CHECK in migration 003 exactly. Rejects +self+/+anyone+
derivation sentinels, uppercase/mixed-case hex, and any
non-BRC-43 shape before any DB write.
- **@param** `action_id` [Integer]
- **@param** `outputs` [Array<Hash>] BRC-29 derivation metadata —
+{ vout:, satoshis:, derivation_prefix:, derivation_suffix: }+
entries the peer's +internalize_action+ consumes to recover
each output's locking key.
- **@param** `sender_identity_key` [String] this wallet's identity key
hex; the BRC-29 envelope's +sender_identity_key+.
- **@param** `endpoint` [String, nil] absolute HTTPS URI of the peer's
delivery endpoint. When supplied, +@delivery+ POSTs the
trimmed BEEF and a successful ACK fires
+Store#mark_transmission_acked+. When nil, the caller takes
responsibility for delivery and ACK-recording.
- **@raise** [BSV::Wallet::InvalidParameterError] counterparty shape
- **@raise** [BSV::Wallet::Error] action missing or unsigned, or
subject-protection invariant violated by the trim
- **@return** [Hash] +{ transmission_id:, beef:, sent_wtxids:,
outputs:, sender_identity_key:, delivery: }+ — +delivery+ is
the +PeerDelivery::Result+ when an endpoint was supplied,
+nil+ otherwise.
