Skip to content

[AI assisted] feat(witness): OpenTimestamps witness profile (4.C)#21

Open
scourtney-godaddy wants to merge 2 commits into
feat/equivalence-link-eventfrom
feat/opentimestamps-witness
Open

[AI assisted] feat(witness): OpenTimestamps witness profile (4.C)#21
scourtney-godaddy wants to merge 2 commits into
feat/equivalence-link-eventfrom
feat/opentimestamps-witness

Conversation

@scourtney-godaddy
Copy link
Copy Markdown

BLUF. Implements ANS-4 witness profile 4.C against OpenTimestamps' Bitcoin-anchored calendars. A deployment running this witness publishes an OTS proof per TL checkpoint that any verifier can validate against a Bitcoin node, anchoring TL state in a system outside the TL operator's control.

What lands

internal/port/witness.go defines the Witness interface and the WitnessAttestation struct that any backend returns. The interface stays narrow: a checkpoint goes in, a backend-specific proof comes out under a profile identifier the verifier uses to pick the right validation procedure. JSON-serializable so a TL audit endpoint can round-trip an attestation without losing fidelity.

internal/adapter/witness/opentimestamps/witness.go is the concrete OpenTimestamps backend. Profile() returns "4.C-opentimestamps". Attest(ctx, checkpoint) SHA-256s the checkpoint, POSTs the digest to https://btc.calendar.opentimestamps.org/digest (overridable via WithCalendarURL), and wraps the calendar's binary OTS proof in a WitnessAttestation. Upgrade(ctx, pending) POSTs a previously-returned pending proof back to the calendar to swap its calendar commitment for a finalized Bitcoin attestation; a 404 means "no Bitcoin block yet" and returns the input unchanged so a background refresh loop can retry.

The split between Attest and Upgrade matches OpenTimestamps' own pattern: producers call Attest at checkpoint time and persist the pending proof; a background loop calls Upgrade on a schedule (typically hourly) until the proof finalizes, then replaces the persisted bytes with the upgraded form. Verifiers run the persisted bytes through the OpenTimestamps reference CLI or any SPV-aware verifier; the witness package itself does no verification.

Tests

internal/adapter/witness/opentimestamps/witness_test.go covers the happy path against an httptest.Server, the empty-checkpoint guard, calendar 5xx, calendar empty body, context cancellation mid-fetch, the upgrade happy path, the 404 case where the calendar treats the input as still pending, upgrade 5xx, the empty-pending guard, the default calendar URL, the trailing-slash strip on base URLs, and a compile-time interface check that *Witness still satisfies port.Witness.

Operational notes

The default calendar is the public OpenTimestamps server. A production deployment should run its own OTS calendar instance and point WithCalendarURL at it; the public calendar is fine for the demo stack and integration tests but should not be a dependency in a regulated path. Switching is a one-line change at construction time.

Test plan

  • go build ./... clean
  • go test ./internal/adapter/witness/... clean
  • Real call against https://btc.calendar.opentimestamps.org/digest returns a non-empty OTS blob
  • Upgrade returns the input unchanged the first ~10 minutes (no Bitcoin block yet) and returns finalized bytes once a block lands

🤖 Generated with Claude Code

Adds the second witness profile (after the production-side
4.A Hedera) so the witness-pluggable architecture is demonstrable
in the LF reference. Bitcoin-anchored timestamps via the public
OpenTimestamps calendar — no infrastructure cost, no operator
credentials, runs on a laptop in the demo stack.

Two new packages:

internal/port/witness.go
  Defines port.Witness and port.WitnessAttestation. The contract is
  intentionally narrow: a checkpoint goes in, a backend-specific
  external proof comes out. Verifiers pick the matching profile to
  validate ExternalProof.

internal/adapter/witness/opentimestamps/witness.go
  Concrete Witness against an OTS calendar (default
  https://btc.calendar.opentimestamps.org). Hashes the checkpoint,
  POSTs the digest, returns the calendar's binary OTS proof bytes
  in WitnessAttestation.ExternalProof. Includes Upgrade for the
  pending → finalized transition (404 returns input unchanged so
  callers can retry; non-404 errors propagate).

Verification side intentionally not included: validating an OTS
proof requires an SPV-aware Bitcoin verifier or the off-the-shelf
ots CLI. A reference verifier would double the package size for
marginal LF demonstration value; out-of-band verification is the
documented path. The Profile() / ProfileID strings let downstream
verifiers route to the right validation procedure.

Tests cover the calendar HTTP contract (happy path, 5xx, empty
response, context cancellation), the upgrade flow (happy, 404
returns input unchanged, 5xx errors propagate, empty pending
rejected), and the configuration escape hatches (WithCalendarURL,
WithHTTPClient, WithClock, default URL, trailing slash).

The TL service does not yet consume Witness; binding witnesses into
the checkpoint flow is a separate piece of operator wiring. This PR
ships the contract and one concrete profile so deployments can
compose them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@scourtney-godaddy scourtney-godaddy added the AI assisted Pull request created with AI assistance label May 17, 2026
Default was set to btc.calendar.opentimestamps.org, which is not a
real public calendar host (DNS does not resolve). The OTS project
runs three public calendars: alice.btc.calendar.opentimestamps.org,
bob.btc.calendar.opentimestamps.org, and finney.calendar.eternitywall.com.
Default now points at alice; bob and finney are documented as
operator-rotation alternatives, with the multi-calendar aggregation
the OTS reference client does called out as a future amendment.

Adds a build-tagged live test (go test -tags=live) that hits the real
calendar and asserts a non-empty proof comes back.

Verified: a live POST of a SHA-256 digest to
https://alice.btc.calendar.opentimestamps.org/digest returns a 207-byte
OTS proof with the expected magic bytes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI assisted Pull request created with AI assistance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant