From 57e7c94f36a9d23b7b14d675fba7a17dfc5f4050 Mon Sep 17 00:00:00 2001 From: Phillip Michelsen Date: Mon, 9 Feb 2026 09:52:20 +0000 Subject: [PATCH] WIP Implementing core/ledger --- cmd/chron-cli/main.go | 42 +++++++++++++++++++ core/interfaces.go | 23 +++++----- core/ledger.go | 97 ++++++++++++++++++++++++++++++++++++++++++- core/types.go | 36 +++++++++++++--- go.mod | 5 +++ go.sum | 4 ++ 6 files changed, 190 insertions(+), 17 deletions(-) diff --git a/cmd/chron-cli/main.go b/cmd/chron-cli/main.go index da29a2c..d03499b 100644 --- a/cmd/chron-cli/main.go +++ b/cmd/chron-cli/main.go @@ -1,4 +1,46 @@ package main +import ( + "fmt" + "time" + + "git.michelsen.id/chron/core" + "github.com/google/uuid" + "lukechampine.com/blake3" +) + func main() { + payload := []byte{0, 1, 0, 1} + payloadDigest := blake3.Sum256(payload) + + entryID, err := uuid.NewV7() + entryIDBytes, err := entryID.MarshalBinary() + if err != nil { + return + } + ledgerID, err := uuid.NewV7() + ledgerIDBytes, err := ledgerID.MarshalBinary() + if err != nil { + return + } + payloadID, err := uuid.NewV7() + payloadIDBytes, err := payloadID.MarshalBinary() + if err != nil { + return + } + + entry := core.Entry{ + EntryID: core.EntryID(entryIDBytes), + LedgerID: core.LedgerID(ledgerIDBytes), + Seq: 1, + Timestamp: time.Now(), + PayloadID: core.PayloadID(payloadIDBytes), + PayloadDigest: payloadDigest, + } + + entryHash := core.HashEntry(entry) + entry.EntryHash = entryHash + + fmt.Printf("Entry: %+v\n", entry) + fmt.Printf("EntryHash: %x\n", entryHash) } diff --git a/core/interfaces.go b/core/interfaces.go index 39cd9e3..84db750 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -2,24 +2,27 @@ package core import ( "context" - "io" ) type EntryStore interface { - Append(ctx context.Context, entry Entry) (EntryID, error) - - Redact(ctx context.Context, entryID EntryID) error - + Append(ctx context.Context, entry Entry) error Get(ctx context.Context, entryID EntryID) (Entry, error) - - ScanForwards(ctx context.Context, entryID EntryID) ([]EntryID, error) - ScanBackwards(ctx context.Context, entryID EntryID) ([]EntryID, error) + GetBySeq(ctx context.Context, ledgerID LedgerID, seq uint64) (Entry, error) + Delete(ctx context.Context, entryID EntryID) error Head(ctx context.Context, ledgerID LedgerID) (EntryID, error) - IsEmpty(ctx context.Context, ledgerID LedgerID) bool + HeadSeq(context.Context, LedgerID) (uint64, error) + Tail(ctx context.Context, ledgerID LedgerID) (EntryID, error) + Iterator(ctx context.Context, ledgerLedgerID, startSeq uint64) (EntryIterator, error) +} + +type EntryIterator interface { + Next() bool + Entry() Entry } type PayloadStore interface { - Put(ctx context.Context, reader io.Reader) (PayloadID, error) + Put(ctx context.Context, payload []byte) (PayloadID, error) + Get(ctx context.Context, payloadID PayloadID) ([]byte, error) Delete(ctx context.Context, payloadID PayloadID) error } diff --git a/core/ledger.go b/core/ledger.go index 5ebbb29..72891fe 100644 --- a/core/ledger.go +++ b/core/ledger.go @@ -1,12 +1,107 @@ package core +import ( + "context" + "time" + + "github.com/google/uuid" + "lukechampine.com/blake3" +) + type Ledger struct { + LedgerID LedgerID + entryStore EntryStore payloadStore PayloadStore - LedgerID LedgerID + hashChaining bool } func NewLedger(entryStore EntryStore, payloadStore PayloadStore) (*Ledger, error) { + id, err := uuid.NewV7() + if err != nil { + return nil, err + } + + idByes, err := id.MarshalBinary() + if err != nil { + return nil, err + } + + return &Ledger{ + LedgerID: LedgerID(idByes), + entryStore: entryStore, + payloadStore: payloadStore, + }, nil +} + +func (l *Ledger) ID() LedgerID { + return l.LedgerID +} + +func (l *Ledger) Head() (Entry, error) { + panic("unimplemented") +} + +func (l *Ledger) Append(ctx context.Context, payload []byte) (EntryID, error) { + payloadDigest := blake3.Sum256(payload) + payloadID, err := l.payloadStore.Put(ctx, payload) + if err != nil { + return EntryID{}, err + } + + entryID, err := uuid.NewV7() + entryIDBytes, err := entryID.MarshalBinary() + if err != nil { + return EntryID{}, err + } + + head, err := l.Head() + if err != nil { + return EntryID{}, err + } + + entry := Entry{ + EntryID: EntryID(entryIDBytes), + LedgerID: l.LedgerID, + Seq: head.Seq + 1, + Timestamp: time.Now(), + PayloadID: payloadID, + PayloadDigest: payloadDigest, + } + + entryHash := HashEntry(entry) + entry.EntryHash = entryHash + + err = l.entryStore.Append(ctx, entry) + if err != nil { + return EntryID{}, err + } + + return EntryID(entryIDBytes), nil +} + +func (l *Ledger) Get(ctx context.Context, seq uint64) (Entry, []byte, error) { + entry, err := l.entryStore.GetBySeq(ctx, l.LedgerID, seq) + if err != nil { + return Entry{}, nil, err + } + payload, err := l.payloadStore.Get(ctx, entry.PayloadID) + if err != nil { + return Entry{}, nil, err + } + + return entry, payload, nil +} + +func (l *Ledger) GetEntry(ctx context.Context, seq uint64) (Entry, error) { + panic("unimplemented") +} + +func (l *Ledger) GetPayload(ctx context.Context, seq uint64) ([]byte, error) { + panic("unimplemented") +} + +func (l *Ledger) Iter(ctx context.Context, startSeq uint64) (EntryIterator, error) { panic("unimplemented") } diff --git a/core/types.go b/core/types.go index 8eedce2..030b7a0 100644 --- a/core/types.go +++ b/core/types.go @@ -1,15 +1,17 @@ package core import ( + "encoding/binary" "time" + + "lukechampine.com/blake3" ) type LedgerID [16]byte - type EntryID [16]byte type PayloadID [16]byte -type Hash [32]byte +type Hash32 [32]byte type Entry struct { EntryID EntryID @@ -19,9 +21,31 @@ type Entry struct { Timestamp time.Time - Nonce [16]byte - Commitment Hash + PayloadID PayloadID + PayloadDigest Hash32 - PreviousHash Hash - EntryHash Hash + PreviousHash Hash32 + EntryHash Hash32 +} + +func HashEntry(e Entry) Hash32 { + b := make([]byte, 0, 128) + + b = append(b, "chron.entry.v1"...) + b = append(b, e.LedgerID[:]...) + + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], e.Seq) + b = append(b, buf[:]...) + + binary.LittleEndian.PutUint64(buf[:], uint64(e.Timestamp.UTC().UnixNano())) + b = append(b, buf[:]...) + + b = append(b, e.EntryID[:]...) + b = append(b, e.PayloadID[:]...) + b = append(b, e.PayloadDigest[:]...) + b = append(b, e.PreviousHash[:]...) + + sum := blake3.Sum256(b) + return Hash32(sum) } diff --git a/go.mod b/go.mod index 7e7fe7f..7e74eca 100644 --- a/go.mod +++ b/go.mod @@ -3,3 +3,8 @@ module git.michelsen.id/chron go 1.25.5 require github.com/google/uuid v1.6.0 + +require ( + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + lukechampine.com/blake3 v1.4.1 // indirect +) diff --git a/go.sum b/go.sum index 7790d7c..6d581f3 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=