Skip to content

One dust coin at the public CAT address permanently blocks every mirror create #461

Description

@MichaelTaylor3d

Task

One unauthenticatable coin, placed by anyone for dust, permanently blocks every mirror-coin create on a node.

The mechanism

crates/dig-node-service/src/mirror/funding.rs:269-271:

cats.push(authenticate(...)?);

The ? aborts the entire selection on the first coin that fails authentication. Three facts compose into the attack:

  1. The address is publicly derivable. Coins are gathered from dig_cat_puzzle_hash(owner), which anyone can compute from the operator's public owner puzzle hash.
  2. Selection is largest-first. So a coin with an amount above the largest honest coin is examined first.
  3. Failure aborts rather than skips. One unauthenticatable coin at the front of that ordering means the selection never reaches the operator's real coins.

Cost to the attacker: dust. A single CAT coin at that address with a large declared amount and no valid lineage. Cost to the operator: no mirror coin can ever be created, so no content can be bonded, and the node silently stops participating in the collateral economy it is configured for.

The fix, and the direction it must fail

Skip and continue, rather than abort. An unauthenticatable coin is not this operator's coin — it is noise at a public address, and noise at a public address is the normal condition of a public address. It should cost one authentication attempt and nothing else.

Two things for whoever takes it:

  • Do not simply swallow the error. A coin that fails authentication for an unexpected reason is worth a log line, because the same code path also covers a genuine bug in lineage checking. Skipping silently would hide that. Skip, count, and log at a level an operator can see.
  • Watch the bound interaction. MAX_SELECTED_FUNDING_COINS = 32 (funding.rs:69) caps how many inputs are selected. If skipping consumes budget against that cap, an attacker can still exhaust it with 32 dust coins — the skip must not count toward the selection limit, or the DoS survives in a slower form.

Provenance — not introduced by the work that found it

Found by the security gate of #457, which correctly rated it pre-existing and outside its diff and did not gate on it. That PR strictly improves this surface (it adds the input bound); it did not create this.

Evidence

funding.rs:269-271 read directly. Not fired — no coin was placed at a real address to confirm the ordering empirically, and the largest-first claim is from reading the selection, not from observing it. Confirming that ordering is the first step of any fix.

Acceptance

An unauthenticatable coin at the operator's public CAT address does not prevent a create — proven by a test that places one ahead of a valid coin in the selection order and asserts the create still succeeds using the valid one. A test that merely asserts authenticate returns an error proves nothing about the selection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions