package core import ( "context" "errors" "time" ) type Ledger struct { entryStore EntryStore referenceStore ReferenceStore } func NewLedger(entryStore EntryStore, referenceStore ReferenceStore) (*Ledger, error) { return &Ledger{ entryStore: entryStore, referenceStore: referenceStore, }, nil } func (l *Ledger) Append(ctx context.Context, payload []byte) error { currentHeadEntryID, ok, err := l.referenceStore.Get(ctx, "HEAD") if err != nil { return err } if !ok { l.referenceStore.Set(ctx, "HEAD", EntryID{}) currentHeadEntryID = EntryID{} } entryTime := time.Now() entryID := ComputeEntryID(currentHeadEntryID, entryTime, payload) entry := Entry{ EntryID: entryID, Previous: currentHeadEntryID, Timestamp: entryTime, Payload: payload, } if ComputeEntryID(entry.Previous, entry.Timestamp, entry.Payload) != entryID { panic("EntryID hash mismatch fuckup") } err = l.entryStore.Store(ctx, entry) if err != nil { return err } err = l.referenceStore.Set(ctx, "HEAD", entry.EntryID) if err != nil { return err } return nil } func (l *Ledger) AppendTo(ctx context.Context, prevEntryID EntryID, payload []byte) error { entryTime := time.Now() entryID := ComputeEntryID(prevEntryID, entryTime, payload) entry := Entry{ EntryID: entryID, Previous: prevEntryID, Timestamp: entryTime, Payload: payload, } if ComputeEntryID(entry.Previous, entry.Timestamp, entry.Payload) != entryID { panic("EntryID hash mismatch fuckup") } err := l.entryStore.Store(ctx, entry) if err != nil { return err } return nil } func (l *Ledger) Get(ctx context.Context) (Entry, error) { headEntryID, ok, err := l.referenceStore.Get(ctx, "HEAD") if err != nil { return Entry{}, err } if !ok { return Entry{}, errors.New("HEAD not set") } entry, err := l.entryStore.Load(ctx, headEntryID) if err != nil { return Entry{}, err } return entry, nil } func (l *Ledger) GetFromReference(ctx context.Context, reference string) (Entry, error) { referenceEntryID, ok, err := l.referenceStore.Get(ctx, reference) if err != nil { return Entry{}, err } if !ok { return Entry{}, errors.New("HEAD not set") } entry, err := l.entryStore.Load(ctx, referenceEntryID) if err != nil { return Entry{}, err } return entry, nil } func (l *Ledger) SetHead(ctx context.Context, entryID EntryID) error { return l.referenceStore.Set(ctx, "HEAD", entryID) } func (l *Ledger) SetReference(ctx context.Context, reference string, entryID EntryID) error { return l.referenceStore.Set(ctx, reference, entryID) } func (l *Ledger) RemoveReference(ctx context.Context, reference string) error { return l.referenceStore.Delete(ctx, reference) }