Drops Protocol documentation

Security and safety

What this actually proves

A proof that is described accurately is more useful than a proof that is described impressively. This page states the guarantee precisely, lists the ways an implementation can weaken it, and gives the safety rules for anyone creating or holding a Drop.

The guarantee, precisely

A confirmed, verified Drop proves exactly four things:

  1. These exact body bytes, this content type, this marker and this public key were committed together in a single Tapscript leaf.
  2. That leaf is a leaf the spent Taproot output committed to, before the spend existed.
  3. The declared SHA-256 equals the revealed body.
  4. The spend appears in a block on the chain the reading implementation follows, at or beyond its confirmation depth.
And nothing beyond that
  • Not authorship. The creator key is whoever signed the reveal. Anyone can commit to anyone's content.
  • Not rights. Recording an image does not grant, transfer or evidence any right to it.
  • Not value. The protocol has no opinion about what a record is worth.
  • Not performance. A Pact with a recorded enforcement floor proves its terms were committed, not that anyone honoured them.
  • Not agreement between implementations. Another indexer may read the same chain differently. Verify the implementation you rely on.
  • Not availability of referenced content. If a body holds a pointer, Bitcoin proves the pointer and nothing about the thing pointed at.

Threat model for implementers

Every entry below is a real way to turn a sound protocol into an unsound product.

Implementation threats
ThreatConsequenceMitigation
Recording a leaf without checking its Taproot commitment Anyone can put any leaf in any witness. Records become forgeable at will. Specification section 4 in full. Conformance C-B3 through C-B7.
Treating a verification exception as anything but a rejection A malformed input that crashes the verifier is accepted by the error path. Fail closed. Conformance C-B8.
Validating the creator key by length only 32 arbitrary bytes are treated as a key. Downstream signature logic behaves unpredictably. Curve-point validation. Vector IV-8.
Accepting non-minimal push encodings Two distinct byte strings decode to the same fields, so a record has more than one serialization and content-addressing breaks. Re-serialization equality. Conformance C-A2.
Accepting another protocol's marker Two ledgers merge. A token event becomes an artifact and no result is reproducible. Marker allowlist. Vector IV-2.
Rendering a body with its declared content type An SVG or HTML body executes script in your origin, with access to whatever that origin holds. Passive-content policy: inline only text/plain and raster images, everything else an opaque attachment, with nosniff and a restrictive CSP.
Trusting a body's declared type when storing or scanning it The declared type is attacker-controlled. A body labelled text/plain may be anything. Treat the type as a claim; treat the bytes as hostile.
Serving a record without stating the confirmation depth or indexed height A consumer cannot tell whether an absent record is absent or merely not yet scanned. Publish network, depth and height. Conformance C-C10.
Patching a projection after a reorganisation Custody drifts from immutable history and cannot be reproduced by a second implementation. Roll back and rebuild. Conformance C-C8.
Guessing at an ambiguous reorganisation ancestor An unreproducible history that no audit can confirm. Stop with a named reason. Conformance C-C9.
Presenting a partial Pact outcome check as a full one A user believes an outcome was verified when only its structure was checked. State which of P-1 through P-9 were applied, every time. Conformance C-P4.
Presenting Pacts as having authority it does not have Someone signs or sends funds on the strength of a reference-only surface. Surface mode: reference and the enforcement floor. Conformance C-P5 and C-P6.

Safety for people, not implementations

Always true
  • No Drops or Pacts interface ever needs your seed phrase or private key. Any request for one is an attack, without exception.
  • Review the body, the network, the destination and the fee before signing. A confirmed Bitcoin transaction is difficult to reverse.
  • A wallet preview, a draft, or an unconfirmed transaction is not a record. For Drops artifacts there is no pending state at all.
  • Rehearse on signet or regtest. It costs nothing.
If something looks wrong
  • If a screen says a Pact transaction is pending: do not sign, do not send funds. The reference service holds no funds and no live Pact state.
  • Keep the full blueprint and the plan hash together. They are what let you prove later which document you agreed to.
  • Verify any Bitcoin transaction independently, in a wallet or explorer you chose yourself.
  • If two screens disagree, believe the chain. Fetch the record, fetch its body, and check the hash yourself with the decoder.

A note on content

Anyone can commit anything to Bitcoin, and a Drops indexer records what it finds. An implementation that serves bodies is publishing third-party content it did not choose and cannot remove from the chain. Serving decisions, including refusing to serve particular records, are a matter for the operator and the law that applies to them. The protocol has no removal mechanism, and adding one would not remove anything from Bitcoin.

Reporting a vulnerability

Report privately, not in a public issue. Use GitHub's private vulnerability reporting on the repository that holds the affected code.

If the issue is inReport at
This documentation: a rule stated wrongly, a vector that is wrong, a tool that reports a false resultdrops-protocol-docs advisories
The OP_DROP carrier protocol or its documentationop-drop advisories
A Bitcoin Universe product or serviceThe security policy on docs.bitcoinuniverse.io

A useful report includes what you did, what you expected, what happened, and the smallest input that reproduces it. For a protocol-level finding, a test vector in the style of the test vectors page is the most useful thing you can send.

Especially wanted

A byte string that this documentation says is invalid but the reference implementation accepts, or the reverse. A disagreement between the specification and the code is the most valuable bug either can have, and it is worth reporting even when neither behaviour is obviously dangerous.