refactor
This commit is contained in:
203
README.md
203
README.md
@@ -1,80 +1,175 @@
|
||||
# opentimestamps
|
||||
|
||||
Interact with calendar servers, create and verify OTS attestations.
|
||||
A fork of [github.com/nbd-wtf/opentimestamps](https://github.com/nbd-wtf/opentimestamps) that lets you interact with calendar servers, create and verify OTS attestations.
|
||||
|
||||
# How to use
|
||||
|
||||
Full documentation at https://pkg.go.dev/github.com/nbd-wtf/opentimestamps. See some commented pseudocode below (you probably should not try to run it as it is).
|
||||
Here's an example of how to use the library to create a timestamp, attempt to upgrade it periodically, and display information about it:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/nbd-wtf/opentimestamps"
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
func main () {
|
||||
// create a timestamp at a specific calendar server
|
||||
hash := sha256.Sum256([]byte{1,2,3,4,5,6})
|
||||
seq, _ := opentimestamps.Stamp(context.Background(), "https://alice.btc.calendar.opentimestamps.org/", hash)
|
||||
"git.intruders.space/public/opentimestamps"
|
||||
"git.intruders.space/public/opentimestamps/ots"
|
||||
)
|
||||
|
||||
// you can just call UpgradeSequence() to get the upgraded sequence (or an error if not yet available)
|
||||
upgradedSeq, err := opentimestamps.UpgradeSequence(context.Background(), seq, hash[:])
|
||||
if err != nil {
|
||||
fmt.Println("wait more")
|
||||
}
|
||||
func main() {
|
||||
// Read a file to timestamp
|
||||
fileData, err := os.ReadFile("document.txt")
|
||||
if err != nil {
|
||||
fmt.Println("Error reading file:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// a File is a struct that represents the content of an .ots file, which contains the initial digest and any number of sequences
|
||||
file := File{
|
||||
Digest: hash,
|
||||
Sequences: []Sequence{seq},
|
||||
}
|
||||
// Calculate the digest
|
||||
digest := sha256.Sum256(fileData)
|
||||
fmt.Printf("File digest: %x\n", digest)
|
||||
|
||||
// it can be written to disk
|
||||
os.WriteFile("file.ots", file.SerializeToFile(), 0644)
|
||||
// Define calendar servers
|
||||
calendars := []string{
|
||||
"https://alice.btc.calendar.opentimestamps.org",
|
||||
"https://bob.btc.calendar.opentimestamps.org",
|
||||
"https://finney.calendar.eternitywall.com",
|
||||
}
|
||||
|
||||
// or printed in human-readable format
|
||||
fmt.Println(file.Human())
|
||||
// Create a timestamp using each calendar
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// sequences are always composed of a bunch of operation instructions -- these can be, for example, "append", "prepend", "sha256"
|
||||
fmt.Println(seq[0].Operation.Name) // "append"
|
||||
fmt.Println(seq[1].Operation.Name) // "sha256"
|
||||
fmt.Println(seq[2].Operation.Name) // "prepend"
|
||||
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)
|
||||
}
|
||||
|
||||
// "prepend" and "append" are "binary", i.e. they take an argument
|
||||
fmt.Println(hex.EncodeToString(seq[2].Argument)) // "c40fe258f9b828a0b5a7"
|
||||
if len(sequences) == 0 {
|
||||
fmt.Println("Failed to create any timestamps")
|
||||
return
|
||||
}
|
||||
|
||||
// all these instructions can be executed in order, starting from the initial hash
|
||||
result := seq.Compute(hash) // this is the value we send to the calendar server in order to get the upgraded sequence
|
||||
finalResult := upgradedSeq.Compute(hash) // this should be the merkle root of a bitcoin block if this sequence is upgraded
|
||||
// Create the timestamp file
|
||||
file := &ots.File{
|
||||
Digest: digest[:],
|
||||
Sequences: sequences,
|
||||
}
|
||||
|
||||
// each sequence always ends in an "attestation"
|
||||
// it can be either a pending attestation, i.e. a reference to a calendar server from which we will upgrade this sequence later
|
||||
fmt.Println(seq[len(seq)-1].Attestation.CalendarServerURL) // "https://alice.btc.calendar.opentimestamps.org/"
|
||||
// or it can be a reference to a bitcoin block, the merkle root of which we will check against the result of Compute() for verifying
|
||||
fmt.Println(upgradedSeq[len(upgradedSeq)-1].Attestation.BitcoinBlockHeight) // 810041
|
||||
// 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")
|
||||
|
||||
// speaking of verifying, this is how we do it:
|
||||
// first we need some source of bitcoin blocks,
|
||||
var bitcoin opentimestamps.Bitcoin
|
||||
if useLocallyRunningBitcoindNode {
|
||||
// it can be either a locally running bitcoind node
|
||||
bitcoin, _ = opentimestamps.NewBitcoindInterface(rpcclient.ConnConfig{
|
||||
User: "nakamoto",
|
||||
Pass: "mumbojumbo",
|
||||
HTTPPostMode: true,
|
||||
})
|
||||
} else {
|
||||
// or an esplora HTTP endpoint
|
||||
bitcoin = opentimestamps.NewEsploraClient("https://blockstream.info/api")
|
||||
}
|
||||
// Display initial timestamp info
|
||||
fmt.Println("\nInitial timestamp info:")
|
||||
fmt.Println(file.Human(false))
|
||||
|
||||
// then we pass that to a sequence
|
||||
if err := upgradedSeq.Verify(bitcoin, hash); err == nil {
|
||||
fmt.Println("it works!")
|
||||
}
|
||||
// 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))
|
||||
}
|
||||
```
|
||||
|
||||
You can also take a look at [`ots`](https://github.com/fiatjaf/ots), a simple CLI to OpenTimestamps which is basically a wrapper over this library.
|
||||
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`](https://github.com/fiatjaf/ots) CLI which is another implementation based on the same concepts.
|
||||
|
||||
# License
|
||||
|
||||
|
||||
Reference in New Issue
Block a user