# Construction steps Every step below is reproducible. Values marked `<...>` are filled at publication time; all other values were verified during construction and can be re-derived. ## 0. Clean-room conditions - Sources used: (1) the published draft, retrieved from `https://www.ietf.org/archive/id/draft-fassbender-scitt-time-anchor-03.txt`; (2) the OpenTimestamps release pinned in Section 5.2.1 of the profile. Nothing else was consulted. The Anchoring Specification v1.3 is a normative reference of the profile, but no producer-side decision required consulting it beyond what the profile restates; the public mailing-list fallback was not needed. - No communication with the document's author during implementation. - All code written from scratch for this exercise. Format knowledge was obtained by reading the pinned release's source as the format specification, which the profile directs (Section 5.2.1: it "must be read in order to implement this profile, not run"). ## 1. Pinned release verification pip download opentimestamps==0.4.5 --no-deps --no-binary :all: sha256sum opentimestamps-0.4.5.tar.gz Result: `56726ccde97fb67f336a7f237ce36808e5593c3089d68d900b1c83d0ebf9dcfa` — exact match with Section 5.2.1. The package content of the GitHub tag `python-opentimestamps-v0.4.5` was additionally compared against the sdist and found identical (`diff -r` on the package directory). ## 2. Implementation modules (SHA-256) e17d3217f7842ec82fdecfc8c53e62caf2ae191bbb140ef57796b726f6787fba ots_wire.py 70c83b074679343b953bc04b4051ebfd046571088ed9f58e0bf78b375af67561 anchor.py f91be556b4f2975369f944e89c0c29014c61649b12639c30dcf565dd40c680df node_rpc.py 54c6821b9d7431c8da03ff44ae474ae32b3264c99a4e0539cc1b6882f3517c62 calendar_client.py 96105a2685d3b0f90b8b27c3d10b508103dda14893b06040954499c9197c6c7d upgrade.py b513e774683ecf1cc9d1f111526f40486c6a4d792eb6ed15221e5b6afae2c3dd verify.py 31438ea3c1e2f1105a1dd2c24334af012f831afc9c98c1f3e53039f6fe50585e tests_offline.py 6d352c1f40fef48c352ba6e0f99dc425ab1bbe6f660056fea2d8a5e35c81d6ed anchor_run.py 3c799e2d1525889f0ffb15f50067ee05f74bcc2907007adedb8ddd2bdb71d3af verify_run.py Every module carries an SPDX MIT identifier and an authorship header; the full licence text is in `LICENSE`. What each implements, with the profile section it answers to: | Module | Profile reference | |---|---| | `ots_wire.py` | OTS-DESERIALIZE / OTS-SERIALIZE / OTS-REPLAY (Section 1.4); wire format per pinned [OTS] release, cited file:line in the module header | | `anchor.py` | CONSTRUCT-ANCHOR (Appendix D.1), BATCH-ANCHOR (Appendix D.3) | | `calendar_client.py` | Calendar Server protocol, imported from [OTS] calendar.py:47-100 as the profile directs (Appendix D, preamble) | | `node_rpc.py` | FETCH-BLOCK-HEADER-BY-HEIGHT, CONFIRMATIONS-ON (Section 1.4), against a full node operated by the implementer — satisfying the header-source independence requirement directly (Appendix D.2) | | `upgrade.py` | UPGRADE-PENDING-PROOFS (Appendix D.2) including the promotion gates 2d–2f: per-branch header comparison and MIN_CONFIRMATIONS = 6 before promoting (Sections 3.1 step 9, 6.11) | | `verify.py` | VERIFY-ANCHOR, all eleven steps (Section 3.1); returns exactly one of valid / invalid / unverifiable (Section 3.3) | Replay is restricted to append / prepend / SHA-256 (Section 1.4, OTS-REPLAY); a branch containing any other operation is left unevaluated, never treated as failing. ## 3. Offline validation against Appendix E `tests_offline.py` (run with the draft as argument), 21 checks, all passing on the production machine: - Appendix E.1/E.2 recomputed with plain SHA-256 (route 1 of E.7). - All four E.5 proof files: byte length 1877, file digests as printed, parse, attested height 957289, replay of the 82 recorded operations from the artifact hash to the internal-byte-order Merkle root printed in E.3, and byte-identical re-serialisation (round-trip). - BATCH-ANCHOR, fed the E.1 nonces, reproduces the E.1 leaves and the E.2 root, and the operations it records for vector 1 are identical to the first six operations of the E.5 proof produced by the author's deployment — two independent producers recording the same sequence. **This is the central interoperability result of this deliverable; see README.md, "Interoperability result".** - Batch sizes 1..9 checked against an independent recomputation of the aggregation, exercising the odd-element promotion rule. - VERIFY-ANCHOR boundary behaviour: k=6 valid, k=5 unverifiable, missing header unverifiable, mismatched root invalid, mutated artifact invalid (Appendix E.6). - D.2 gate behaviour: k=5 not promoted; k=6 promoted with height and block_time recorded; node unavailable leaves the proof pending. ## 4. Verifier closed against the live chain printf 'draft-fassbender-scitt-time-anchor-03 vector 1' > v1.txt python3 verify_run.py v1.txt vector1.ots Result on ``: `valid`, block-height binding 957289, RWCP 2026-07-09T10:02:59Z (matching Appendix E.3), confirmations ``. Headers and confirmation depth were obtained from a Bitcoin full node operated by the implementer; per Appendix C of the profile, no verifier described there is currently in a position to return `valid`. ## 5. Anchoring run (producer side) printf '' > artifact.txt sha256sum artifact.txt # python3 anchor_run.py artifact.txt Submission to public OpenTimestamps calendars (alice, bob, finney) per the protocol of [OTS] calendar.py:47-76. The artifact hash was blinded with a fresh 16-byte nonce before aggregation (Appendix D.3, step 2). ## 6. Upgrade with promotion gates python3 upgrade.py state.json # run periodically The proof was promoted to `anchored` only after this implementation itself (a) replayed the proof and compared the resulting digest against the Merkle root of the block header at the attested height, retrieved from the implementer's own node (Appendix D.2, step 2e), and (b) established at least 6 confirmations at that height (step 2f; Sections 3.1 step 9, 6.11). Promotion occurred on ``: height ``, k = ``. ## 7. Final verification python3 verify_run.py artifact.txt artifact.txt.ots Result: `valid`; block-height binding ``; RWCP `` (informative). Full session log in `anchoring-session.log`.