Bitcoin Anchor Verification v1
Status: v1.1 — drafted 2026-05-05
Spec URI: https://projectmaya.deoscomputing.io/anchor/bitcoin/v1
Maintainer: DEOS Computing (did:web:deos.computing)
Companion: /anchor/v1 — the v1.1 OTS calendar attestation flow
This document specifies how a verifier upgrades from "OTS proof is present" (v1.1, --require-anchor) to "OTS proof is anchored in a confirmed Bitcoin block" (v1.2, --require-bitcoin-anchor). It also specifies multi-rail anchoring so a single calendar's compromise does not break timestamp guarantees.
1. Goals
A v1.2 verifier with Bitcoin-anchor support must:
- Confirm Bitcoin block-header validity for the block(s) the substrate's anchor proof claims to commit to. Header validity = proof-of-work + difficulty target + chain continuity from a known checkpoint.
- Walk the OpenTimestamps proof from the substrate's MMR root through SHA-1, RIPEMD-160, prepend/append byte operations, to a Bitcoin transaction's
OP_RETURNdata. - Confirm the Bitcoin transaction is included in a Bitcoin block via the standard Merkle proof (Bitcoin-side).
- Bound the timestamp by the block's
nTime(loose; ±2 hours per Bitcoin consensus) and the median-time-past (tighter; ±1 hour by network convention). - Support multiple anchor rails (OTS + Ethereum L1 + private TSA) so a single calendar / chain compromise is not load-bearing.
A v1.2 verifier also rejects:
- Pending (unconfirmed) OTS proofs when
--require-bitcoin-anchoris set. - Proofs whose terminal Bitcoin block-height is below the verifier's configured minimum confirmation depth (default: 6 confirmations).
- Proofs whose block header does not chain back to a verifier-trusted checkpoint (defense against a substrate that submits to a forked / private chain).
2. v1.1 baseline (today)
receipt-verifier --require-anchor checks:
- The receipt's
anchorsfield is present and non-empty. - At least one anchor record has
service == "opentimestamps"and a non-empty proof field. - The proof URL extraction succeeds (basic well-formedness).
It does not check:
- Whether the OTS calendar has actually upgraded the proof to a Bitcoin block.
- Whether that block exists on the Bitcoin chain.
- Whether the block's height meets a confirmation threshold.
- Whether the proof's leaf hash equals
SHA256(SHA256(substrate_root))(the OTS canonical leaf).
This baseline is what /anchor/v1 ships. It is enough to detect a substrate that publishes no anchor at all; it is NOT enough to detect a substrate that submits to a fake / private OTS calendar that returns plausible bytes but never reaches Bitcoin.
Receipts with --require-anchor carry an OTS-server promise, not a Bitcoin consensus statement.
3. v1.2 verification flow
The flow MUST run in this order so a verifier short-circuits on the cheapest checks first.
3.1 Decode the OTS proof
OTS proofs are a binary format (file extension .ots). The on-disk layout per the OpenTimestamps protocol:
magic : 31 bytes — "\x00OpenTimestamps\x00\x00Proof\x00\xbf\x89\xe2\xe8\x84\xe8\x92\x94"
version : 1 byte — currently 0x01
file-hash: opcodes that derive the leaf hash from the substrate's input bytes
timestamp: opcodes + attestations
Opcodes are single-byte tags followed by length-prefixed arguments:
| Opcode | Meaning |
|---|---|
| 0x00 | sha1 |
| 0x02 | ripemd160 |
| 0x03 | sha256 |
| 0x67 | keccak256 |
| 0x08 | append (concatenate constant bytes after the digest) |
| 0xf0 | prepend (concatenate constant bytes before the digest) |
| 0x83 | hash-and-fork (rare; produces multiple subtrees) |
Attestations are terminal records identifying where the proof commits:
| Attestation tag | Meaning |
|---|---|
\x05\x88\x96\x0d\x73\xd7\x19\x01 (8 bytes) |
Bitcoin block-attestation: 4-byte block height (big-endian) follows |
\x06\x86\x9a\x0d\x73\xd7\x1b\x45 |
Pending — not yet anchored to Bitcoin |
\x83\xdf\xe3\x0d\x2e\xf9\x0c\x8e |
Litecoin block-attestation (out of v1.2 scope) |
A v1.2 verifier MUST parse the full opcode tape and extract:
- The leaf bytes the proof is over (typically
SHA-256(substrate_root)). - The terminal Bitcoin block height (or pending status).
- The expected Bitcoin transaction
OP_RETURNpayload the proof reduces to (the result of running the opcodes against the leaf bytes).
Reference implementation: opentimestamps-client (Python, public). A pure-Rust port lives in DEOS Computing's working repo.
3.2 Verify the Bitcoin block
Given the terminal block height H from §3.1, fetch the block header from a verifier-trusted Bitcoin SPV source. The verifier MUST:
- Validate the header's proof-of-work:
H(header) <= difficulty_targetper the consensus rules. - Validate that the header chains back to a known checkpoint via parent-hash references (defense against a fork-mined chain that has valid PoW but is not Bitcoin mainnet). Checkpoints SHOULD be hardcoded for blocks at well-known heights (e.g. genesis, halving heights).
- Validate that the height H is at least
--min-confirmationsblocks below the verifier-known chain tip (default: 6). - Extract the block's
nTimeand themedian-time-past(computed from the previous 11 blocks per BIP 113); the receipt's effective timestamp upper bound is the LATER of these.
Sources for block headers:
| Source | Trust model | Recommended for |
|---|---|---|
| Local Bitcoin Core node | Operator-controlled, full validation | High-stakes / federal deployments |
| Esplora (mempool.space) | Public SPV server, multiple operators | Production substrates |
| BlockCypher | Single-vendor SPV | Cost-sensitive deployments |
| Hardcoded recent checkpoints + difficulty-extension | Verifier-controlled, no network | Fully air-gapped verifiers |
The substrate itself does NOT verify Bitcoin; it only submits the OTS proof. Verification is a verifier-side operation. The substrate's responsibility ends at "I made the OTS submission and stored the calendar's response."
3.3 Match the OP_RETURN
A Bitcoin transaction in block H carries an OP_RETURN output whose data field commits to the OTS leaf via the proof opcodes. The verifier MUST:
- Fetch the transaction from the SPV source (or local node).
- Compute the result of running the OTS opcodes from §3.1 against the substrate's MMR root.
- Confirm the computed result equals the
OP_RETURNdata of the transaction. - Confirm the transaction is in block H via the Bitcoin-side Merkle proof (transaction → Merkle root in header).
Step 4 requires fetching the Merkle path. SPV servers expose this as GET /tx/{txid}/merkle-proof or similar.
3.4 Bound the receipt's effective timestamp
If all checks pass, the receipt's issued_at_ns claim is bounded:
- Lower bound:
(receipt.issued_at_ns - calendar_max_skew_ns). The calendar's clock can drift, so the receipt could have been signed slightly beforeissued_at_ns. v1.2 default skew: 5 minutes. - Upper bound:
min(block_nTime, median_time_past) + propagation_delay. The OTS proof reaches Bitcoin within ~10 minutes typically.
A receipt whose claimed issued_at_ns is OUTSIDE this window MUST be rejected as anachronistic — either the substrate is lying about its timestamp or the OTS proof corresponds to a different submission.
4. Multi-rail anchoring (ANCH1.1-5, P1 by audit)
A v1.2 substrate MAY submit each MMR root to multiple anchor providers. Today's --ots-calendars URL,URL,... already supports submission to multiple OTS calendars; the multi-rail extension goes further:
| Rail | Service | Anchor latency | Cost |
|---|---|---|---|
| OTS | OpenTimestamps calendar | ~30 min to Bitcoin block | Free |
| Ethereum L1 | Sepolia/mainnet OP_RETURN-equivalent (storage commit) |
~12 sec per block | ~$5-50 per anchor |
| Private TSA | RFC 3161 trusted timestamp authority | seconds | per-vendor |
| Filecoin | DataCap / FVM commit | minutes | per-vendor |
| EigenDA | data-availability commit | seconds | per-vendor |
The receipt's anchors array carries one entry per rail:
"anchors": [
{ "service": "opentimestamps", "proof": "<base64 of OTS bytes>" },
{ "service": "ethereum-l1", "proof": "<base64 of {block_hash, tx_hash, receipt_proof}>" },
{ "service": "rfc3161-tsa", "proof": "<base64 of CMS TSTInfo>" }
]
A verifier with --require-bitcoin-anchor requires the OTS rail to verify; a verifier with --require-any-anchor requires at least one rail to verify; a verifier with --require-anchors=2 requires ≥2 distinct rails to verify.
The cost of multi-rail submission falls on the substrate operator. For high-value tenants (banking, government), multi-rail is expected; for low-value SaaS deployments, OTS-only remains acceptable.
5. Substrate-side obligations
A v1.2 substrate adds:
--bitcoin-headers-source URL— verifier hint for where to fetch headers (NOT used by the substrate itself; published in/trust-rootso verifiers know what the operator recommends).--anchor-rails RAIL1,RAIL2,...— enabled rails. Default:opentimestamps. Addingethereum-l1requires--ethereum-rpc URLand--ethereum-private-key-from-env VAR.GET /attestation/v1/anchor/{root_hex}— already shipped in v1.0; v1.2 extends the response to include Bitcoin block height + transaction id once the OTS proof has been upgraded to Bitcoin (ANCH1.1-3).- Background "upgrade job" (ANCH1.1-3) that periodically re-fetches pending OTS proofs from the calendar to check for Bitcoin confirmation. Implementation: a separate thread or a periodic timer that re-issues
GET /tip/{calendar_url}/{leaf_hash}against each calendar.
6. Verifier-side CLI
The receipt-verifier CLI gains:
receipt-verifier
[--require-anchor] # v1.1: OTS-presence (already shipped)
[--require-bitcoin-anchor] # v1.2: full Bitcoin block-header verification
[--require-any-anchor] # v1.2: any rail must verify
[--require-anchors=N] # v1.2: at least N distinct rails must verify
[--bitcoin-headers-url URL] # v1.2: SPV source
[--min-bitcoin-confirmations N] # v1.2: default 6
[--bitcoin-checkpoints FILE] # v1.2: trusted checkpoint file (optional)
[--ethereum-rpc-url URL] # v1.2: for ethereum-l1 rail verification
<api-base-url> <receipt-id>
The v1.1 baseline of --require-anchor is preserved exactly so v1.1 callers don't break. New flags are additive.
7. Security considerations
This section satisfies the RFC 3552 requirement.
7.1 OTS calendar trust
A single OTS calendar can lie. Mitigations:
- Submit to multiple calendars (already supported via
--ots-calendars URL,URL,...). - Verifier checks that ≥2 calendars' proofs converge on the same Bitcoin block and OP_RETURN data.
- Operator can run a private OTS calendar for highest assurance.
7.2 Bitcoin SPV trust
The SPV source can lie about block headers, transaction inclusion, or chain tip. Mitigations:
- Run a local Bitcoin Core node (full validation).
- Use multiple SPV sources and require agreement.
- Ship hardcoded checkpoints; require the chain to extend from them.
7.3 Eclipse attacks on the SPV source
A network-level attacker can isolate the verifier's SPV source from the real Bitcoin network. Detection requires querying multiple sources from independent network paths. Out of scope for v1.2; v2 may add a "fetch from N independent endpoints over different ASes" requirement.
7.4 Reorg-after-confirmation
A 6-confirmation block can theoretically be reorged. For receipts where this risk matters (high-value financial transactions), the verifier SHOULD use a higher confirmation depth. The probability of a 6-block reorg on Bitcoin mainnet is operationally negligible (<10^-5 historically) but not zero.
7.5 Delayed Bitcoin confirmation
A receipt issued at time T and submitted to OTS may not reach Bitcoin for ≥30 minutes. During that interval, the proof is in a "pending" state. Verifiers MUST handle pending proofs explicitly:
--require-anchor(v1.1 baseline): pending proofs satisfy.--require-bitcoin-anchor(v1.2): pending proofs FAIL until the calendar upgrades.
The substrate's background upgrade job (§5.4) shortens the pending window from "as long as the verifier waits" to "as long as the substrate's polling interval × calendar latency."
8. Implementation status
v1.1 ships:
- OTS submission and calendar response storage.
- Anchor record format with the schema registry from
/anchor/v1. receipt-verifier --require-anchor(OTS-presence only).
v1.2 closes the gap to Bitcoin-anchored timestamps verifiable offline:
- Full OTS binary parser.
- Bitcoin block-header verification.
- OTS upgrade job (re-fetch pending).
receipt-verifier --require-bitcoin-anchor.- Multi-rail anchor support.
- Anchor revocation / replacement.
Eclipse-attack mitigation deferred to v2.
9. References
/anchor/v1— v1.1 OTS submission flow- OpenTimestamps protocol — opcode reference
- BIP 9 — block-header version bits
- BIP 113 — median-time-past
- RFC 3161 — Internet X.509 PKI Time-Stamp Protocol (TSA rail)
- Esplora API — recommended SPV source
/interop/scitt/v1§4.2 —deos_anchorsfield on SCITT Receipts (anchor rails carry into the SCITT profile identically)- RFC 3552 — security considerations format (§7)
10. Maintainer
DEOS Computing — design questions and conformance reports to github.com/DEOS-Computing.
License: CC BY 4.0 (text), Apache-2.0 (reference code).