Files
opentimestamps/README.md
2025-04-11 13:31:02 +02:00

5.0 KiB

opentimestamps

A fork of github.com/nbd-wtf/opentimestamps that lets you interact with calendar servers, create and verify OTS attestations.

How to use

Here's an example of how to use the library to create a timestamp, attempt to upgrade it periodically, and display information about it:

package main

import (
	"context"
	"crypto/sha256"
	"fmt"
	"os"
	"time"

	"git.intruders.space/public/opentimestamps"
	"git.intruders.space/public/opentimestamps/ots"
)

func main() {
	// Read a file to timestamp
	fileData, err := os.ReadFile("document.txt")
	if err != nil {
		fmt.Println("Error reading file:", err)
		return
	}

	// Calculate the digest
	digest := sha256.Sum256(fileData)
	fmt.Printf("File digest: %x\n", digest)

	// Define calendar servers
	calendars := []string{
		"https://alice.btc.calendar.opentimestamps.org",
		"https://bob.btc.calendar.opentimestamps.org",
		"https://finney.calendar.eternitywall.com",
	}

	// Create a timestamp using each calendar
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	var sequences []ots.Sequence
	for _, calendarURL := range calendars {
		fmt.Printf("Submitting to %s...\n", calendarURL)
		seq, err := opentimestamps.Stamp(ctx, calendarURL, digest)
		if err != nil {
			fmt.Printf("Failed to submit to %s: %v\n", calendarURL, err)
			continue
		}
		fmt.Printf("Submission to %s successful\n", calendarURL)
		sequences = append(sequences, seq)
	}

	if len(sequences) == 0 {
		fmt.Println("Failed to create any timestamps")
		return
	}

	// Create the timestamp file
	file := &ots.File{
		Digest:    digest[:],
		Sequences: sequences,
	}

	// Save the OTS file
	otsData := file.SerializeToFile()
	if err := os.WriteFile("document.txt.ots", otsData, 0644); err != nil {
		fmt.Println("Failed to save OTS file:", err)
		return
	}
	fmt.Println("Timestamp file created successfully")

	// Display initial timestamp info
	fmt.Println("\nInitial timestamp info:")
	fmt.Println(file.Human(false))

	// Attempt to upgrade the timestamp every 20 minutes
	fmt.Println("\nWill check for upgrades every 20 minutes...")

	maxAttempts := 12 // Try for about 4 hours (12 * 20 minutes)
	for attempt := 0; attempt < maxAttempts; attempt++ {
		if attempt > 0 {
			fmt.Printf("\nWaiting 20 minutes before next upgrade attempt (%d/%d)...\n", attempt+1, maxAttempts)
			time.Sleep(20 * time.Minute)
		}

		upgraded := false
		pendingSequences := file.GetPendingSequences()
		if len(pendingSequences) == 0 {
			fmt.Println("No pending sequences to upgrade")
			break
		}

		fmt.Printf("Attempting to upgrade %d pending sequences...\n", len(pendingSequences))

		upgradeCtx, upgradeCancel := context.WithTimeout(context.Background(), 30*time.Second)
		for _, seq := range pendingSequences {
			att := seq.GetAttestation()
			fmt.Printf("Trying to upgrade sequence from %s...\n", att.CalendarServerURL)

			upgradedSeq, err := opentimestamps.UpgradeSequence(upgradeCtx, seq, digest[:])
			if err != nil {
				fmt.Printf("Failed to upgrade sequence from %s: %v\n", att.CalendarServerURL, err)
				continue
			}

			// Replace the sequence in the file
			for i, origSeq := range file.Sequences {
				origAtt := origSeq.GetAttestation()
				if origAtt.CalendarServerURL == att.CalendarServerURL {
					file.Sequences[i] = upgradedSeq
					upgraded = true

					newAtt := upgradedSeq.GetAttestation()
					if newAtt.BitcoinBlockHeight > 0 {
						fmt.Printf("Sequence upgraded! Confirmed in Bitcoin block %d\n", newAtt.BitcoinBlockHeight)
					} else {
						fmt.Println("Sequence updated but still pending")
					}
					break
				}
			}
		}
		upgradeCancel()

		if upgraded {
			// Save the upgraded file
			otsData = file.SerializeToFile()
			if err := os.WriteFile("document.txt.ots", otsData, 0644); err != nil {
				fmt.Println("Failed to save upgraded OTS file:", err)
			} else {
				fmt.Println("Upgraded timestamp file saved")
			}
		}

		// If all sequences are confirmed, we're done
		if len(file.GetPendingSequences()) == 0 {
			fmt.Println("All sequences are now confirmed in the Bitcoin blockchain!")
			break
		}
	}

	// Final report
	fmt.Println("\nFinal timestamp status:")

	confirmedSeqs := file.GetBitcoinAttestedSequences()
	pendingSeqs := file.GetPendingSequences()

	fmt.Printf("Confirmed attestations: %d\n", len(confirmedSeqs))
	for _, seq := range confirmedSeqs {
		att := seq.GetAttestation()
		fmt.Printf("- Confirmed in Bitcoin block %d\n", att.BitcoinBlockHeight)
	}

	fmt.Printf("Pending attestations: %d\n", len(pendingSeqs))
	for _, seq := range pendingSeqs {
		att := seq.GetAttestation()
		fmt.Printf("- Still pending at %s\n", att.CalendarServerURL)
	}

	fmt.Println("\nDetailed timestamp info:")
	fmt.Println(file.Human(true))
}

This repository includes an ots command line tool which provides a convenient interface to the core library functionality. Run it without arguments to see usage information.

You can also take a look at the original ots CLI which is another implementation based on the same concepts.

License

Public Domain