Skip to content

UIP-2: Optional Auxiliary Metdata for Attestation #66

@lightsing

Description

@lightsing

UIP-2: Optional Auxiliary Metadata for Attestation

Field Value
UIP 2
Title Optional Auxiliary Metadata for Attestation
Author @lightsing
Status Draft
Type Standards Track
Created 2026-03-18

Abstract

This proposal introduces a backwards-compatible mechanism to append optional auxiliary metadata to existing attestation types.

Motivation

The original OpenTimestamps (OTS) SDK behavior retains pending attestations alongside upgraded Bitcoin attestations by merging them via a FORK operation. While straightforward, this approach makes it difficult for machines to deterministically verify if a timestamp is fully upgraded. The only way to verify is to attempt another upgrade and check if the attestation structure changes, which relies heavily on the liveness of the calendar server and adds implementation complexity.

Conversely, the UniversalTimestamps SDK's default behavior purges pending attestations upon a successful upgrade (though a keep_pending option is available to mirror OTS behavior). This design allows machines to easily determine if a timestamp is fully self-contained (i.e., no pending attestations remain). However, purging results in the loss of contextual information—specifically, the identity of the calendar server that assisted in creating the attestation.

Appending optional auxiliary metadata to attestations resolves this dilemma. It allows the protocol to retain non-authoritative contextual information (such as the calendar server URI) while maintaining a clean, easily verifiable, and self-contained attestation tree.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

1. Trailing Optional Codec Extension

The trailing optional encoding is defined by the following rules:

  1. If an optional field is the last declared field of a struct, it MUST be treated as a trailing optional field.
  2. During deserialization, if there is unconsumed data remaining in the buffer and a trailing optional field is defined, the decoder MUST attempt to decode the remaining data as the trailing optional field.
  3. The trailing optional encoding is recursive; a trailing optional field MAY contain nested trailing optional fields.

Invalid Example:
The following struct is invalid because optional_field_1 is not a trailing field. Currently, there is no generic optional field codec defined in the OTS specification to handle length or presence boundaries for non-trailing fields.

struct Example {
    mandatory_field: u64,
    optional_field_1: Option<String>, // Invalid: Cannot determine boundary
    optional_field_2: Option<u32>,
}

Valid Example:
The following struct is valid because nested trailing optional fields are strictly allowed under rule 3.

struct Example {
    chain_id: u64,
    metadata: Option<Metadata>, // Valid: Trailing optional
}

struct Metadata {
    height: u64,
    tx: Option<B256>, // Valid: Nested trailing optional
}

2. The URI Codec Specification

The original URI specification was defined implicitly as part of the PendingAttestation. This proposal extracts and standardizes it as a standalone URI type.

pub struct URI<'a>(pub Cow<'a, str>);

The maximum allowed length is defined as MAX_URI_LEN = 1000.

The URI type is a strict subset of the URI regex defined in RFC 3986 Appendix B: Parsing a URI Reference with a Regular Expression. A valid URI:

  • MUST contain only ASCII characters.
  • MUST be an absolute URI.
  • MUST contain a scheme.
  • MUST NOT contain a query string.
  • MUST have a length less than or equal to MAX_URI_LEN.

Consequently, a valid URI MUST strictly match the following PCRE2 regular expression: ^([a-zA-Z][\w+\-.]*):\/\/([\w\-.:\[\]]*)([\/\w\-.:%~]*)$ and its length MUST be <= MAX_URI_LEN.

3. Changes to BitcoinAttestation

The BitcoinAttestation specification is extended to include optional metadata.

From:

pub struct BitcoinAttestation {
    pub height: u32,
}

To:

pub struct BitcoinAttestation<'a> {
    pub height: u32,
    pub metadata: Option<BitcoinAttestationMetadata<'a>>
}

pub struct BitcoinAttestationMetadata<'a> {
   pub calendar: URI<'a>,
}

4. Changes to EASAttestation

The EASAttestation specification is extended.

From:

pub struct EASAttestation {
    pub chain: Chain,
    pub uid: B256,
}

To:

pub struct EASAttestation<'a> {
    pub chain: Chain,
    pub uid: B256,
    pub metadata: Option<EASAttestationMetadata<'a>>
}

pub struct EASAttestationMetadata<'a> {
   pub calendar: URI<'a>,
   pub tx: Option<TxHash>,
}

5. Changes to EASTimestamped

The EASTimestamped specification is extended.

From:

pub struct EASTimestamped {
    pub chain: Chain,
}

To:

pub struct EASTimestamped<'a> {
    pub chain: Chain,
    pub metadata: Option<EASTimestampedMetadata<'a>>
}

pub struct EASTimestampedMetadata<'a> {
   pub calendar: URI<'a>,
   pub tx: Option<TxHash>,
}

6. Changes to PendingAttestation

The PendingAttestation structure is updated to utilize the new strict URI type.

From:

pub struct PendingAttestation<'a> {
    pub uri: Cow<'a, str>,
}

To:

pub struct PendingAttestation<'a> {
    pub uri: URI<'a>,
}

7. Upgrade Procedure Behavior

Any compliant implementation executing an upgrade operation MUST retain the calendar server's URI upon a successful upgrade. The implementation MUST populate the metadata.calendar field of the newly generated attestation (e.g., BitcoinAttestation, EASAttestation) with this URI.

Implementations SHOULD subsequently purge the original PendingAttestation from the proof to maintain a clean and self-contained timestamp structure, relying instead on the newly appended trailing metadata for provenance.

8. Verify Procedure Behavior

The metadata introduced in this proposal is inherently auxiliary and non-authoritative. Any compliant implementation MUST attempt to actively verify this information before displaying it to the user as factual provenance.

Verifying Calendar Server (calendar):
To verify a calendar server URI embedded in the metadata, the implementation SHOULD initiate an upgrade network request to the specified calendar server to confirm that the server actually attests to the commitment.

Verifying Transaction Hash (tx):
For metadata containing a transaction hash (tx), such as within EASAttestationMetadata or EASTimestampedMetadata, the implementation SHOULD verify the transaction against the respective blockchain network (chain). The implementation SHOULD fetch the transaction receipt and mathematically confirm that it successfully emitted the corresponding on-chain event (e.g., the Attested event for the Ethereum Attestation Service) matching the attestation's core data (such as the uid).

Fallback Behavior:
If active verification is not possible (e.g., the client is operating in an offline environment, the calendar server is unresponsive, or the RPC node for the chain is inaccessible) or if the verification fails (e.g., the transaction receipt does not contain the expected event), the implementation MUST explicitly warn the user through the UI/CLI. The warning MUST clearly indicate that the displayed contextual information (such as the calendar server identity or the transaction hash) is auxiliary, unverified, and cannot be cryptographically trusted.

Backwards Compatibility

This proposal introduces both structural extensions and codec-level breaking changes relative to the original OpenTimestamps specification.

1. URI Codec Incompatibility

The adoption of the strictly defined URI type (Section 2) replaces the legacy PendingAttestation URI validation logic. This introduces a bidirectional incompatibility at the codec layer:

  • Tightening (Stricter Structure): The legacy OTS specification merely enforced a character whitelist ([a-zA-Z0-9.-_//:]) and a length limit, which technically allowed malformed or meaningless strings (e.g., invalid_string or ://missing_scheme). Implementations of UIP-2 MUST reject these structurally invalid URIs. A new decoder parsing an old, malformed .ots file will fail.
  • Relaxation (Expanded Character Set): To comply with RFC 3986 and support modern web infrastructure, the new URI regex explicitly permits characters previously forbidden by the legacy whitelist, such as [ and ] (for IPv6 addresses like [::1]), + (for schemes), % (for URL encoding), and ~. A legacy decoder attempting to parse a UIP-2 generated file containing these new characters in the URI will throw a decoding error.

Practical Impact Mitigation: While theoretically incompatible, the practical impact is negligible. Historically, all known and compliant OpenTimestamps calendar servers have exclusively emitted well-formed, absolute HTTP/HTTPS URIs that fit perfectly within the intersection of both the old and new specifications (e.g., https://a.btc.calendar.org). Therefore, this strictness acts as a necessary protocol sanitation and modernization without disrupting existing real-world timestamp files.

2. Trailing Optional Codec Compatibility

The forward compatibility of the trailing optional metadata mechanism (Sections 3, 4, 5) depends entirely on how strictly the legacy decoder handles the deserialization buffer boundaries:

  • Incompatible with Strict EOF Implementations (e.g., python-opentimestamps): Legacy decoders that explicitly enforce an End-Of-File (EOF) check on the attestation payload will fail to parse UIP-2 attestations. For example, the original OpenTimestamps Python SDK calls payload_ctx.assert_eof() after extracting known fields. When it encounters the unconsumed trailing metadata bytes introduced by this proposal, it will throw a fatal deserialization error, rendering the entire proof unreadable.
  • Compatible with Lenient Implementations (e.g., legacy uts): Legacy decoders that do not strictly enforce payload EOF checks will remain forward-compatible. For instance, older versions of the uts Rust implementation read the payload into a byte slice, decode the expected fixed-length fields, and implicitly discard any remaining bytes in the slice. These implementations will naturally ignore the trailing metadata without throwing errors.

Operational Impact: Because strict EOF checks exist in the wild (notably in the reference Python implementation), appending trailing metadata constitutes a structural breaking change for the legacy OTS ecosystem. Applications interacting with UIP-2 extended attestations MUST upgrade their parsers to either explicitly support the new metadata structures or safely ignore unconsumed trailing bytes at the payload level.

Metadata

Metadata

Assignees

Labels

UIP Status: DraftUTS Improvement Proposal in Draft stageUIP Types: Standards TrackUTS Improvement Proposals Types: Standards Track

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions