139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"time"
|
|
|
|
"git.intruders.space/public/opentimestamps"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
// Upgrade command flags
|
|
upgradeOutput string
|
|
upgradeTimeout time.Duration
|
|
upgradeDryRun bool
|
|
)
|
|
|
|
// upgradeCmd represents the upgrade command
|
|
var upgradeCmd = &cobra.Command{
|
|
Use: "upgrade [flags] <file.ots>",
|
|
Short: "Upgrade a timestamp",
|
|
Long: `Upgrade a timestamp by checking if pending attestations have been confirmed in the Bitcoin blockchain.
|
|
If confirmed, the timestamp will be updated with the Bitcoin block information.`,
|
|
Args: cobra.ExactArgs(1),
|
|
Run: runUpgradeCmd,
|
|
}
|
|
|
|
func init() {
|
|
// Local flags for the upgrade command
|
|
upgradeCmd.Flags().StringVarP(&upgradeOutput, "output", "o", "", "Output filename (default: overwrites the input file)")
|
|
upgradeCmd.Flags().DurationVar(&upgradeTimeout, "timeout", 30*time.Second, "Timeout for calendar server connections")
|
|
upgradeCmd.Flags().BoolVar(&upgradeDryRun, "dry-run", false, "Don't write output file, just check if upgrade is possible")
|
|
}
|
|
|
|
func runUpgradeCmd(cmd *cobra.Command, args []string) {
|
|
inputPath := args[0]
|
|
|
|
// Determine output file path
|
|
outputPath := upgradeOutput
|
|
if outputPath == "" {
|
|
outputPath = inputPath
|
|
}
|
|
|
|
// Read and parse the OTS file
|
|
otsData, err := os.ReadFile(inputPath)
|
|
if err != nil {
|
|
slog.Error("Failed to read OTS file", "file", inputPath, "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
timestampFile, err := opentimestamps.ReadFromFile(otsData)
|
|
if err != nil {
|
|
slog.Error("Failed to parse OTS file", "file", inputPath, "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Get pending sequences to upgrade
|
|
pendingSequences := timestampFile.GetPendingSequences()
|
|
if len(pendingSequences) == 0 {
|
|
slog.Info("No pending timestamps found, file is already fully upgraded", "file", inputPath)
|
|
os.Exit(0)
|
|
}
|
|
|
|
slog.Info("Found pending timestamps", "count", len(pendingSequences))
|
|
|
|
// Create context with timeout
|
|
ctx, cancel := context.WithTimeout(context.Background(), upgradeTimeout)
|
|
defer cancel()
|
|
|
|
// Try to upgrade each pending sequence
|
|
upgradedCount := 0
|
|
for i, seq := range pendingSequences {
|
|
att := seq.GetAttestation()
|
|
slog.Info("Attempting to upgrade timestamp",
|
|
"index", i+1,
|
|
"calendar", att.CalendarServerURL)
|
|
|
|
upgraded, err := opentimestamps.UpgradeSequence(ctx, seq, timestampFile.Digest)
|
|
if err != nil {
|
|
slog.Warn("Upgrade failed",
|
|
"calendar", att.CalendarServerURL,
|
|
"error", err)
|
|
continue
|
|
}
|
|
|
|
// Replace the pending sequence with the upgraded one
|
|
for j, origSeq := range timestampFile.Sequences {
|
|
if origSeq[len(origSeq)-1].Attestation != nil &&
|
|
origSeq[len(origSeq)-1].Attestation.CalendarServerURL == att.CalendarServerURL {
|
|
timestampFile.Sequences[j] = upgraded
|
|
break
|
|
}
|
|
}
|
|
|
|
upgradedCount++
|
|
|
|
newAtt := upgraded.GetAttestation()
|
|
if newAtt.BitcoinBlockHeight > 0 {
|
|
slog.Info("Timestamp upgraded successfully",
|
|
"calendar", att.CalendarServerURL,
|
|
"block", newAtt.BitcoinBlockHeight)
|
|
} else {
|
|
slog.Info("Timestamp replaced but still pending", "calendar", att.CalendarServerURL)
|
|
}
|
|
}
|
|
|
|
if upgradedCount == 0 {
|
|
slog.Warn("No timestamps could be upgraded at this time. Try again later.", "file", inputPath)
|
|
if !upgradeDryRun {
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
// In dry run mode, don't write the file
|
|
if upgradeDryRun {
|
|
slog.Info("Dry run completed", "upgraded", upgradedCount, "total", len(pendingSequences))
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Write the updated OTS file
|
|
newOtsData := timestampFile.SerializeToFile()
|
|
err = os.WriteFile(outputPath, newOtsData, 0644)
|
|
if err != nil {
|
|
slog.Error("Failed to write updated OTS file", "file", outputPath, "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
slog.Info("Timestamp file upgraded successfully",
|
|
"file", outputPath,
|
|
"upgraded", upgradedCount,
|
|
"total", len(pendingSequences))
|
|
|
|
// Print human-readable representation
|
|
slog.Debug("Updated timestamp details", "info", timestampFile.Human(false))
|
|
}
|