Late-Publishing Attacks and Immutable History: How did:btcr2 Ensures DID Integrity

Jul 10, 2026

Late-Publishing Attacks and Immutable History: How did:btcr2 Ensures DID Integrity

Introduction

A decentralized identifier is only useful if you can trust its history. If an attacker can retroactively insert, reorder, or suppress operations in a DID's history, the entire trust model collapses. did:btcr2 is designed around a specific threat: the late-publishing attack. Understanding this threat, and how btcr2 defends against it, is essential for anyone building on or evaluating the method.

What Is a Late-Publishing Attack?

A late-publishing attack occurs when someone creates a valid DID operation but deliberately withholds it from the network, publishing it later to manipulate the DID's perceived state.

Scenario

  1. Alice creates a DID and publishes the genesis document
  2. Alice creates Update A (adding a new service endpoint) and anchors it to Bitcoin at block height 800,000
  3. Bob verifies Alice's DID and sees the service endpoint from Update A
  4. Later, Alice reveals Update B, which she had actually signed before Update A but never published. Update B removes Bob's access.
  5. If the system accepts Update B, Alice can retroactively change the DID's history

Why It Matters

Late-publishing attacks undermine:

  • Credential verification: A credential issued under one DID state might be retroactively invalidated by a late-published update
  • Access control: Permissions granted based on a DID document can be retroactively changed
  • Non-repudiation: Actions taken under a specific DID state can be disputed if the state can be retroactively altered

btcr2's Defense: Canonical Single History

btcr2 enforces that every DID has exactly one canonical operation history. This is achieved through several mechanisms:

Ordered Anchoring

Every signed update operation includes:

  • A source hash: the SHA-256 hash of the JCS-canonicalized (RFC 8785) DID document before the update
  • A target hash: the SHA-256 hash of the JCS-canonicalized DID document after the update
  • A target version ID: the sequence number this update produces, which must be exactly one greater than the document it updates (did:btcr2 update data structures)

The signed update payload itself carries no block height or transaction ID. Anchoring is external: the update is announced by a Beacon Signal, a Bitcoin transaction whose output script commits to a hash of the signed update. During resolution, the resolver discovers these signals on-chain and derives each update's block height from the transaction that carries it.

This creates a hash-linked chain: each operation explicitly references the document state it transforms. Update B can't be inserted before Update A if Update A's source hash matches the genesis state and Update B's source hash doesn't.

Bitcoin Ordering

Bitcoin provides a total ordering of transactions. If two updates reference the same source document state (a fork), the one anchored earlier in Bitcoin's chain is applied first. If the later update hashes to the same value once its proof is removed and the canonicalized payload is re-hashed, it is confirmed as a duplicate and ignored; if it conflicts (same targetVersionId, different content), its hash will not match the recorded version hash and the resolver raises a LATE_PUBLISHING error rather than returning a result.

This is where Bitcoin's immutability directly serves DID integrity. An attacker can't retroactively reorder Bitcoin blocks without controlling a majority of the network's hash rate.

Duplicate Detection

The resolution algorithm expects each update to declare a targetVersionId, and detects conflicts by that version sequence (did:btcr2 resolve algorithm):

  1. Each update declares a targetVersionId; the resolver expects exactly current_version_id + 1
  2. An update whose targetVersionId was already seen is confirmed as a duplicate by removing its proof, re-hashing the unsigned update, and comparing against the recorded update hash for that version
  3. A version gap (targetVersionId > current_version_id + 1) or a hash mismatch raises a LATE_PUBLISHING error: evidence of a withheld or conflicting operation

Late-Publishing Rejection

An operation that arrives "late", published to Bitcoin after a subsequent operation has already been processed, is detected because:

  1. It declares a targetVersionId the resolver has already passed, so it is checked against the recorded version hash and (since its content differs) fails that comparison
  2. The canonical chain, ordered by targetVersionId with Bitcoin block height as the tiebreaker, has already moved forward past the version this operation claims
  3. The resolver raises a LATE_PUBLISHING error regardless of when the operation was signed

The signing timestamp doesn't determine ordering: the Bitcoin anchor does. This means an attacker can't sign an operation early and publish it late to claim priority.

The Resolution Algorithm

When resolving a btcr2 DID, the resolver:

  1. Starts from genesis: either reconstruct the genesis document from the DID (for key-based DIDs) or use the provided genesis document (for document-based DIDs)
  2. Scans Bitcoin: read the Beacon services declared in the current DID document, parse each service endpoint as a Beacon Address, and find the Bitcoin transactions at those addresses whose output commits to a signed update for this DID
  3. Orders operations: sort by each update's targetVersionId (ascending), using the anchoring Beacon Signal's block height as a tiebreaker
  4. Validates each operation in order:
    • Check the update's targetVersionId: confirm it is current_version_id + 1 (a gap raises LATE_PUBLISHING), or, if already seen, confirm it as a duplicate against the recorded version hash
    • Confirm the source hash matches the current document state
    • Verify the BIP340 Schnorr signature against the current document's authorized keys (not the final state's keys)
    • Apply the JSON Patch to produce the new document
    • Verify the target hash matches the patched document
  5. Returns the final state: the document after all valid operations are applied

Each step is deterministic. Given the same Bitcoin chain state and the same sidecar data, every resolver produces the same result.

Threat Model Summary

ThreatDefense
Insert a fake operationBIP340 signature verification against authorized keys
Reorder operationsBitcoin's total transaction ordering
Fork the historyDuplicate detection (targetVersionId sequencing)
Late-publish a withheld operationSource hash chain linkage + Bitcoin ordering
Suppress an operationSidecar data retention by the controller; Bitcoin anchor is immutable
Tamper with an anchored operationSHA-256 hash commitment in the Bitcoin transaction

What btcr2 Can't Defend Against

No system is omnipotent. btcr2's security model has boundaries:

  • Key compromise: If an attacker obtains the controller's private key, they can create valid operations. btcr2 doesn't define separate update and recovery keys the way Sidetree-based methods like ION do; instead, an update is authorized by invoking a ZCAP root capability (urn:zcap:root:<did>) and signing it (via BIP340) with a verification method listed in the DID document's capabilityInvocation relationship. That still leaves key compromise as fundamentally a key management problem: a controller can rotate or remove a compromised key in a later update, but only if the attacker hasn't already used it to seize control first.
  • 51% attack: If an attacker controls a majority of Bitcoin's hash rate, they could reorganize the chain and reorder anchors. btcr2 inherits Bitcoin's security assumption here.
  • Sidecar loss: If the controller loses their sidecar data and no one else has it, the DID's history beyond what can be reconstructed from the blockchain is lost.

Conclusion

Late-publishing attacks are a fundamental threat to any identity system with asynchronous updates. btcr2 addresses this by making Bitcoin's transaction ordering the sole arbiter of operation sequence, enforcing hash-linked operation chains, and implementing deterministic duplicate detection in the resolver. The result is a DID with a single, auditable history whose ordering is as hard to rewrite as the Bitcoin blocks it's anchored to, subject to the boundaries above (key management, Bitcoin's own security assumptions, and sidecar-data availability).

Jintek LLC