205 lines
8.1 KiB
Markdown
205 lines
8.1 KiB
Markdown
# OpenTimestamps Integration Plan
|
|
|
|
## Why OpenTimestamps?
|
|
|
|
The NIP-QR migration event establishes a link between a Nostr identity and post-quantum keys. This link must be provably created **before** quantum computers exist. If an attacker later breaks secp256k1 via Shor's algorithm, they could forge a fraudulent NIP-QR event with a different set of PQ keys. Without timestamping, clients cannot distinguish the real event from a forged one.
|
|
|
|
OpenTimestamps anchors the event to a Bitcoin block, proving it existed at a specific time. Re-mining historical Bitcoin blocks is infeasible even with a quantum computer (Grover's algorithm gives only a quadratic speedup on mining, which doesn't help retroactively alter past blocks).
|
|
|
|
## How OpenTimestamps Works
|
|
|
|
1. **Submit a hash** to an OpenTimestamps calendar server (e.g., `https://alice.btc.calendar.opentimestamps.org`)
|
|
2. The calendar server aggregates hashes from multiple users into a Merkle tree
|
|
3. The calendar server commits the Merkle root to a Bitcoin transaction (OP_RETURN)
|
|
4. The server returns a **pending .ots file** — a proof file that contains the Merkle path but no Bitcoin confirmation yet
|
|
5. **Wait for the Bitcoin transaction to confirm** (typically 1-3 blocks, ~10-30 minutes)
|
|
6. The .ots file can then be **upgraded** to include the Bitcoin confirmation proof
|
|
7. The final .ots file can be **verified** against the Bitcoin blockchain
|
|
|
|
## NIP-03: OpenTimestamps Attestations for Events
|
|
|
|
NIP-03 defines `kind: 1040` — an event that contains an OTS proof for another event:
|
|
|
|
```json
|
|
{
|
|
"kind": 1040,
|
|
"tags": [
|
|
["e", "<target-event-id>", "<relay-url>"],
|
|
["k", "<target-event-kind>"]
|
|
],
|
|
"content": "<base64-encoded OTS file data>"
|
|
}
|
|
```
|
|
|
|
- The OTS proof MUST prove the referenced event's `id` (which is `SHA-256(serialized event)`)
|
|
- The content MUST be the full content of an `.ots` file with at least one Bitcoin attestation
|
|
- NIP-03 is marked `unrecommended` but is the only standard for OTS in Nostr
|
|
|
|
## The Plan
|
|
|
|
### Step 1: Submit the NIP-QR event hash to OpenTimestamps
|
|
|
|
After the NIP-QR event is signed (step 4 of our flow), before or after publishing to relays:
|
|
|
|
```
|
|
POST https://alice.btc.calendar.opentimestamps.org/timestamp
|
|
Content-Type: application/x-www-form-urlencoded
|
|
Body: <raw 32-byte SHA-256 hash of the event id>
|
|
```
|
|
|
|
The event `id` IS the SHA-256 hash (it's `SHA-256(serialized event)` in hex). We need to:
|
|
1. Convert the event `id` (hex string) to raw 32 bytes
|
|
2. POST those bytes to the calendar server
|
|
3. Receive the .ots file (binary) in the response
|
|
|
|
### Step 2: Save the pending .ots file
|
|
|
|
The response from the calendar server is a binary .ots file (pending proof). Store this:
|
|
- In browser memory (for immediate use)
|
|
- Offer the user a download link (so they can keep it)
|
|
|
|
### Step 3: Wait for Bitcoin confirmation
|
|
|
|
The .ots proof is initially "pending" — it contains the Merkle path but no Bitcoin block confirmation. The user needs to wait for the calendar server to commit to Bitcoin (typically 1-3 blocks).
|
|
|
|
Options:
|
|
- **Option A**: Show the user a "pending" status and a link to upgrade later
|
|
- **Option B**: Poll the calendar server's upgrade endpoint periodically
|
|
- **Option C**: Use a WebSocket or long-poll to wait for confirmation
|
|
|
|
For the demo, **Option A** is simplest: show the pending .ots file, explain it needs time to confirm, and provide instructions for upgrading later.
|
|
|
|
### Step 4: Upgrade the .ots file (later)
|
|
|
|
The pending .ots file can be upgraded by submitting it to the calendar server:
|
|
|
|
```
|
|
POST https://alice.btc.calendar.opentimestamps.org
|
|
Content-Type: application/x-www-form-urlencoded
|
|
Body: <raw .ots file bytes>
|
|
```
|
|
|
|
The server returns the upgraded .ots file with Bitcoin confirmation proof.
|
|
|
|
For the demo, this can be a manual step the user does later (or we can poll automatically).
|
|
|
|
### Step 5: Publish the NIP-03 event (kind 1040)
|
|
|
|
Once the .ots file is confirmed (has a Bitcoin attestation), publish a `kind: 1040` event:
|
|
|
|
```json
|
|
{
|
|
"kind": 1040,
|
|
"pubkey": "<user's pubkey>",
|
|
"tags": [
|
|
["e", "<NIP-QR event id>", "<relay-url>"],
|
|
["k", "30078"]
|
|
],
|
|
"content": "<base64-encoded .ots file>"
|
|
}
|
|
```
|
|
|
|
This event is signed by the user's secp256k1 key (via `window.nostr.signEvent()`) and published to relays.
|
|
|
|
### Step 6: Verify the OTS proof
|
|
|
|
The verification page (`verify.html`) can also verify the OTS proof:
|
|
1. Find the kind 1040 event referencing the NIP-QR event
|
|
2. Extract the .ots file from the content (base64-decode)
|
|
3. Verify the .ots proof against the Bitcoin blockchain
|
|
4. This proves the NIP-QR event existed at the time of the Bitcoin block
|
|
|
|
## Implementation Details
|
|
|
|
### API Endpoints
|
|
|
|
| Purpose | URL | Method |
|
|
|---|---|---|
|
|
| Submit hash for timestamping | `https://alice.btc.calendar.opentimestamps.org/timestamp` | POST (raw bytes) |
|
|
| Upgrade pending .ots | `https://alice.btc.calendar.opentimestamps.org` | POST (raw .ots bytes) |
|
|
| Verify .ots proof | Client-side verification (no server needed) | — |
|
|
|
|
Multiple calendar servers can be used for redundancy:
|
|
- `https://alice.btc.calendar.opentimestamps.org`
|
|
- `https://bob.btc.calendar.opentimestamps.org`
|
|
- `https://btc.calendar.catallaxy.com`
|
|
|
|
### Browser Implementation
|
|
|
|
The OpenTimestamps API uses raw binary POST requests, which can be done with `fetch()`:
|
|
|
|
```javascript
|
|
// Submit event hash for timestamping
|
|
async function timestampEvent(eventIdHex) {
|
|
const hashBytes = hexToBytes(eventIdHex); // 32 bytes
|
|
const response = await fetch('https://alice.btc.calendar.opentimestamps.org/timestamp', {
|
|
method: 'POST',
|
|
body: hashBytes
|
|
});
|
|
const otsFile = await response.arrayBuffer(); // binary .ots file
|
|
return new Uint8Array(otsFile);
|
|
}
|
|
```
|
|
|
|
### UI Changes
|
|
|
|
Add a new step to the checklist:
|
|
1. Sign in with your Nostr identity
|
|
2. Generate a quantum-safe seed phrase
|
|
3. Derive post-quantum keys from seed
|
|
4. Sign the NIP-QR migration event
|
|
5. Publish event to relays
|
|
6. **Timestamp the event with OpenTimestamps** (new)
|
|
7. Verify the event signatures (link to verify.html)
|
|
|
|
After publishing to relays (step 5), the page automatically:
|
|
1. Submits the event hash to the OpenTimestamps calendar
|
|
2. Receives the pending .ots file
|
|
3. Shows the .ots file (base64) and a download link
|
|
4. Explains that the proof needs Bitcoin confirmation (10-30 minutes)
|
|
5. Offers to check/upgrade the proof later
|
|
|
|
### OTS Verification
|
|
|
|
For verifying .ots files in the browser, we can use the `javascript-opentimestamps` library:
|
|
- npm: `javascript-opentimestamps`
|
|
- GitHub: https://github.com/opentimestamps/javascript-opentimestamps
|
|
|
|
This library can verify .ots files against a Bitcoin blockchain API (e.g., Blockstream's esplora API at `https://blockstream.info/api`).
|
|
|
|
## Updated Checklist
|
|
|
|
1. Sign in with your Nostr identity
|
|
2. Generate a quantum-safe seed phrase
|
|
3. Derive post-quantum keys from seed
|
|
4. Sign the NIP-QR migration event
|
|
5. Publish event to relays
|
|
6. Timestamp the event with OpenTimestamps
|
|
7. Verify the event signatures (link to verify.html)
|
|
|
|
## Updated Flow
|
|
|
|
```
|
|
Sign in → Generate seed → Derive PQ keys → Sign event → Publish to relays
|
|
→ Submit hash to OpenTimestamps → Receive pending .ots
|
|
→ (wait for Bitcoin confirmation)
|
|
→ Upgrade .ots → Publish kind 1040 event → Done
|
|
```
|
|
|
|
## What to Build
|
|
|
|
1. **`timestampEvent(eventIdHex)`** function — submits hash to OTS calendar, returns .ots file
|
|
2. **`upgradeOts(otsBytes)`** function — upgrades pending .ots to confirmed
|
|
3. **UI for step 6** — shows OTS status, pending .ots file, download link, upgrade button
|
|
4. **Publish kind 1040 event** — after .ots is confirmed, sign and publish the NIP-03 event
|
|
5. **OTS verification in verify.html** — verify .ots proof against Bitcoin blockchain
|
|
6. **Install `javascript-opentimestamps`** — for client-side .ots verification
|
|
|
|
## Considerations
|
|
|
|
- **CORS**: The OpenTimestamps calendar servers support CORS, so browser requests should work
|
|
- **Time**: Bitcoin confirmation takes 10-30 minutes — the user may need to come back later
|
|
- **Multiple calendars**: For redundancy, submit to multiple calendar servers
|
|
- **NIP-03 unrecommended status**: We use it anyway because it's the only standard, and the concept is sound
|
|
- **The event id IS the hash**: Nostr event ids are SHA-256 hashes, so we timestamp the id directly
|