2025-04-11 13:31:02 +02:00
2025-04-11 13:31:02 +02:00
2025-04-11 13:31:02 +02:00
2023-09-30 14:52:54 -03:00
2025-04-11 13:31:02 +02:00
2025-04-11 13:31:02 +02:00

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

Description
create, read, upgrade and verify opentimestamps proofs
Readme Unlicense 165 KiB
Languages
Go 100%