Review of Spark's trust model

Privacy, control, and safety

The wallet gate is enforced across cooperative exits, deposit operations, leaf renewal, transfer claims, and privileged token broadcasting

Public Spark code applies the wallet gate to cooperative exits, ordinary and static deposit-address operations, deposit-tree creation, leaf renewal, transfer claims, and privileged token broadcasting. Privileged broadcaster authorization expressly does not bypass the target wallet's gate. 12345678

8 sourcesEvidence reviewed through July 14, 2026

What the evidence shows

Technical and product details

  1. Documented behavior

    Guarded operations include cooperative exits, ordinary and static deposit-address generation, static-address rotation, deposit-tree creation, leaf renewal, and transfer claims. 1234567

    Supporting material shown on this page

    cooperative exit gatePinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    60	}
    61
    62	if req.GetTransfer().GetTransferPackage() != nil {
    63		knobsService := knobs.GetKnobsService(ctx)
    64		if knobsService.GetValue(knobs.KnobUseConsensusCoopExit, 0) > 0 {
    65			return h.cooperativeExitConsensus(ctx, req)
    66		}
    ordinary deposit-address gatePinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    137		return nil, errors.InvalidArgumentMalformedKey(fmt.Errorf("invalid identity public key: %w", err))
    138	}
    139	if err := authz.EnforceSessionIdentityPublicKeyMatches(ctx, o.config, reqIDPubKey); err != nil {
    140		return nil, err
    141	}
    142	if err := authz.EnforceWalletNotKillSwitched(ctx, reqIDPubKey); err != nil {
    143		return nil, err
    static deposit-address gatePinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    310func (o *DepositHandler) GenerateStaticDepositAddress(ctx context.Context, config *so.Config, req *pb.GenerateStaticDepositAddressRequest) (*pb.GenerateStaticDepositAddressResponse, error) {
    311	ctx, span := tracer.Start(ctx, "DepositHandler.GenerateStaticDepositAddress")
    312	defer span.End()
    313
    314	if req == nil {
    static-address rotation gatePinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    712func (o *DepositHandler) RotateStaticDepositAddress(ctx context.Context, config *so.Config, req *pb.RotateStaticDepositAddressRequest) (*pb.RotateStaticDepositAddressResponse, error) {
    713	ctx, span := tracer.Start(ctx, "DepositHandler.RotateStaticDepositAddress")
    714	defer span.End()
    715
    716	network, err := btcnetwork.FromProtoNetwork(req.GetNetwork())
    deposit-tree creation gatePinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    892// StartDepositTreeCreation verifies the on chain utxo, and then verifies and signs the offchain root and refund transactions.
    893func (o *DepositHandler) StartDepositTreeCreation(ctx context.Context, config *so.Config, req *pb.StartDepositTreeCreationRequest) (*pb.StartDepositTreeCreationResponse, error) {
    894	ctx, span := tracer.Start(ctx, "DepositHandler.StartDepositTreeCreation")
    895	defer span.End()
    896	if req == nil {
    leaf-renewal gatePinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    141	}
    142
    143	if err := authz.EnforceSessionIdentityPublicKeyMatches(ctx, h.config, leaf.OwnerIdentityPubkey); err != nil {
    144		return nil, err
    145	}
    146	if err := authz.EnforceWalletNotKillSwitched(ctx, leaf.OwnerIdentityPubkey); err != nil {
    147		return nil, err
    transfer-claim gatePinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    90	if err := authz.EnforceSessionIdentityPublicKeyMatches(ctx, h.config, parsed.ownerIDPK); err != nil {
    91		return nil, err
    92	}
    93	// Mirror claimTransferLegacy: a kill-switched wallet must be blocked on the
    94	// consensus path too. Without this the cutover would silently re-open
    95	// claims for kill-switched wallets the moment KnobUseConsensusClaim flips.
    96	if err := authz.EnforceWalletNotKillSwitched(ctx, parsed.ownerIDPK); err != nil {
  2. Documented behavior

    A privileged token broadcaster may bypass an ordinary identity check but is still expressly forbidden from bypassing the target wallet's gate. 8

    Supporting material shown on this page

    privileged-broadcaster invariantPinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    31// In either branch, the wallet kill switch is enforced against idPubKey — a privileged
    32// broadcaster must not be able to bypass the freeze on the target wallet.
    33func enforceBroadcastPolicy(ctx context.Context, config *so.Config, idPubKey keys.Public) error {
    34	if canBroadcastForSession(ctx) {
    35		logging.GetLoggerFromContext(ctx).Sugar().Infof(
    36			"authorized broadcaster bypassing sender identity check for target %s", idPubKey,
    37		)

Interpretation

Why this matters for Spark's trust model

The control is a maintained policy invariant across fast exit, deposit-management, claim, and privileged-service paths—not a UI switch affecting one ordinary send method. 1268

This section states the trust-model consequence of the cited evidence. Conditional or inferred claims remain bounded by the next section.

Scope control

What this does not establish

  • These are state-mutating Spark protocol operations. The gate cannot prevent someone from broadcasting Bitcoin to an already-known on-chain deposit address, and cooperative-path refusal does not invalidate a usable pre-signed unilateral exit. The privileged-broadcaster example concerns tokens. 1234568

Sources and excerpts

Primary sources

The relevant details and available source-code excerpts appear alongside the claims above. This list preserves the complete original-source trail for independent verification.

1

cooperative exit gate

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
2

ordinary deposit-address gate

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
3

static deposit-address gate

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
4

static-address rotation gate

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
5

deposit-tree creation gate

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
6

leaf-renewal gate

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
7

transfer-claim gate

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
8

privileged-broadcaster invariant

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source

How sources, absence findings, and limitations were evaluated