Files
opentimestamps/ots/instruction.go
2025-04-11 13:31:02 +02:00

95 lines
2.6 KiB
Go

package ots
import (
"fmt"
"slices"
"strings"
"git.intruders.space/public/opentimestamps/varn"
)
// a Instruction can be an operation like "append" or "prepend" (this will be the case when .Operation != nil)
// or an attestation (when .Attestation != nil).
// It will have a non-nil .Argument whenever the operation requires an argument.
type Instruction struct {
*Operation
Argument []byte
*Attestation
}
func GetCommonPrefixIndex(s1 []Instruction, s2 []Instruction) int {
n := min(len(s1), len(s2))
for i := 0; i < n; i++ {
if CompareInstructions(s1[i], s2[i]) != 0 {
return i
}
}
return n
}
// CompareInstructions returns negative if a<b, 0 if a=b and positive if a>b.
// It considers an operation smaller than an attestation, a pending attestation smaller than a Bitcoin attestation.
// It orders operations by their tag byte and then by their argument.
func CompareInstructions(a, b Instruction) int {
if a.Operation != nil {
if b.Attestation != nil {
// a is an operation but b is an attestation, a is bigger
return +1
}
if a.Operation == b.Operation {
// if both are the same operation sort by the argument
return slices.Compare(a.Argument, b.Argument)
}
// sort by the operation
if a.Operation.Tag < b.Operation.Tag {
return -1
} else if a.Operation.Tag > b.Operation.Tag {
return 1
} else {
return 0
}
} else if a.Attestation != nil && b.Attestation == nil {
// a is an attestation but b is not, b is bigger
return -1
} else if a.Attestation != nil && b.Attestation != nil {
// both are attestations
if a.Attestation.BitcoinBlockHeight == 0 && b.Attestation.BitcoinBlockHeight == 0 {
// none are bitcoin attestations
return strings.Compare(a.Attestation.CalendarServerURL, b.Attestation.CalendarServerURL)
}
if a.Attestation.BitcoinBlockHeight != 0 && b.Attestation.BitcoinBlockHeight != 0 {
// both are bitcoin attestations
return int(b.Attestation.BitcoinBlockHeight - a.Attestation.BitcoinBlockHeight)
}
// one is bitcoin and the other is not -- compare by bitcoin block,
// but reverse the result since the one with 0 should not be considered bigger
return -1 * int(b.Attestation.BitcoinBlockHeight-a.Attestation.BitcoinBlockHeight)
} else {
// this shouldn't happen
return 0
}
}
func ReadInstruction(buf varn.Buffer, tag byte) (*Instruction, error) {
op, ok := Tags[tag]
if !ok {
return nil, fmt.Errorf("unknown tag %v", tag)
}
inst := Instruction{
Operation: op,
}
if op.Binary {
val, err := buf.ReadVarBytes()
if err != nil {
return nil, fmt.Errorf("error reading argument: %w", err)
}
inst.Argument = val
}
return &inst, nil
}