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 ab. // 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 }