did-btcr2-js

ADR 037: Rename Beacon to SinglePartyBeacon and the Two-Axis Beacon Model

Status: Accepted

Date: 2026-06-19

Branch / PR: refactor/single-party-beacon

References: ADR 018, ADR 008, ADR 020

Context

ADR 018 established the beacon class hierarchy: an abstract Beacon base with three subclasses (SingletonBeacon, CASBeacon, SMTBeacon). The base class name is generic, but what it actually provides is specific: single-party broadcast machinery, buildSignAndBroadcast and the P2PKH / P2WPKH / P2TR single-input build-sign-broadcast phases (packages/method/src/core/beacon/beacon.ts). One party holds one key and broadcasts one 32-byte signal.

The real design space is two orthogonal axes, not one inheritance chain:

CAS and SMT beacons are first-class on both axes: a CAS or SMT beacon can be broadcast single-party (the subclass’s own broadcastSignal, which builds a single-entry announcement/tree and spends solo) or through an aggregation cohort of any N ≥ 1, including a cohort of one. Singleton is single-party only (aggregation is incompatible by design). The generic name Beacon hides this two-axis structure and invites the wrong refactor, e.g. merging Beacon and SingletonBeacon, which would conflate “the single-party broadcast base” with “the singleton beacon type.”

The aggregation path does not live in the class hierarchy: it is buildAggregationBeaconTx plus the AggregationService state machine, and it produces an unsigned tx because the signature comes from a MuSig2 round rather than a local key. The inheritance shape therefore already expresses the matrix correctly; only the base class’s name is wrong.

  single-party direct (P2PKH / P2WPKH / P2TR) aggregation cohort of N ≥ 1 (P2TR MuSig2)
singleton SinglePartyBeacon.buildSignAndBroadcast N/A (incompatible by design)
CAS SinglePartyBeacon.buildSignAndBroadcast AggregationService (any N ≥ 1)
SMT SinglePartyBeacon.buildSignAndBroadcast AggregationService (any N ≥ 1)

Decision

Rename the abstract base class Beacon to SinglePartyBeacon and adopt the two-axis model as the design vocabulary for ongoing aggregation work.

  1. Rename Beacon to SinglePartyBeacon (beacon.ts). The three subclasses extend it for their single-party broadcast path. The name now states what the base provides (single-party broadcast), not the general beacon concept.
  2. The concept “Beacon” is unchanged. BeaconService, BeaconSignal, BeaconFactory, BeaconUtils, BeaconError, and all prose about “a beacon” keep the name. SinglePartyBeacon names a broadcast role, not the beacon abstraction.
  3. extends SinglePartyBeacon does not mean “cannot aggregate.” It means “this beacon type has a single-party broadcast implementation.” Aggregation is the orthogonal axis, handled by AggregationService, available to CAS and SMT regardless of the base class.
  4. Add AggregationRunner.solo(): a cohort-of-1 wrapper over AggregationService that exercises the BIP-341 P2TR MuSig2 path with a single participant. It makes single-participant aggregate broadcasts first-class (and reproducible for aggregate test-vector generation).

Rejected alternatives

Consequences

Positive

Negative

Accepted

References