Files

68 lines
2.1 KiB
Go

package domain
import (
"crypto/sha256"
"errors"
"fmt"
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
)
var (
ErrInvalidEventSequence = errors.New("event sequence must be nonzero")
ErrEventSequenceGap = errors.New("event sequence gap")
ErrConflictingDuplicate = errors.New("conflicting duplicate event")
ErrMissingDuplicate = errors.New("duplicate event has no durable fingerprint")
ErrInvalidTruncationRange = errors.New("invalid output truncation range")
)
type EventFingerprint [sha256.Size]byte
func FingerprintBytes(value []byte) EventFingerprint { return sha256.Sum256(value) }
type EventSequenceDecision uint8
const (
EventSequenceInvalid EventSequenceDecision = iota
EventSequenceAppend
EventSequenceDuplicate
)
// ClassifyEventSequence admits exactly the next event, or an immutable replay
// whose stored fingerprint is supplied. Wire event-sequence gaps are never
// repaired by inventing events.
func ClassifyEventSequence(last, incoming uint64, stored *EventFingerprint, candidate EventFingerprint) (EventSequenceDecision, error) {
if incoming == 0 {
return EventSequenceInvalid, ErrInvalidEventSequence
}
if last != ^uint64(0) && incoming == last+1 {
return EventSequenceAppend, nil
}
if incoming > last {
return EventSequenceInvalid, fmt.Errorf("%w: have %d, received %d", ErrEventSequenceGap, last, incoming)
}
if stored == nil {
return EventSequenceInvalid, ErrMissingDuplicate
}
if *stored != candidate {
return EventSequenceInvalid, ErrConflictingDuplicate
}
return EventSequenceDuplicate, nil
}
// ValidateOutputTruncation checks the two allowed forms: both range endpoints
// absent for pre-sequencing client loss, or a complete nonempty retained range.
func ValidateOutputTruncation(value *rvboxv1.OutputTruncation) error {
if value == nil {
return ErrInvalidTruncationRange
}
first, last := value.FirstRemovedEventSeq, value.LastRemovedEventSeq
if first == nil && last == nil {
return nil
}
if first == nil || last == nil || *first == 0 || *last == 0 || *first > *last {
return ErrInvalidTruncationRange
}
return nil
}