Something went wrong. Try again.
An ATProto Powered Reputation Network
Something went wrong. Try again.
5.8 kB · 193 lines
Go
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194package storage
import ( "context" "database/sql" "errors" "slices" "time"
"provenance/internal/domain" "provenance/internal/storage/db")
func (s *SQLite) graphRows(ctx context.Context, q *db.Queries, tag string, mode domain.Mode, limit int) ([]domain.Vouch, error) { fallback, all := int64(0), int64(0) if mode == domain.Fallback { fallback = 1 } if domain.SoftAll(s.softMode) { all = 1 } rows, err := q.Graph(ctx, db.GraphParams{Tag: tag, Fallback: fallback, MaxRows: int64(limit)}) if err != nil { return nil, err } result := vouchRows(rows) if s.softMode == domain.SoftDisabled || mode == domain.Tagged { return result, nil } soft, err := q.SoftGraph(ctx, db.SoftGraphParams{Tag: tag, Fallback: fallback, AllAuthors: all, MaxRows: int64(limit)}) if err != nil { return nil, err } result = appendSoftRows(result, soft) if !domain.SoftLists(s.softMode) { return result, nil } lists, err := q.ListGraph(ctx, db.ListGraphParams{Tag: tag, Fallback: fallback, AllAuthors: all, MaxRows: int64(limit)}) return appendListRows(result, lists), err}
func appendSoftRows(result []domain.Vouch, rows []db.SoftEdge) []domain.Vouch { // Convert into the final buffer instead of a second full edge slice. result = slices.Grow(result, len(rows)) for _, row := range rows { result = append(result, domain.Vouch{Author: row.Author, Subject: row.Subject, Score: row.Score, Source: row.Source, RecordKey: row.RecordKey, CreatedAt: row.CreatedAt}) } return result}
func (s *SQLite) SoftReady(ctx context.Context, did string) (bool, error) { a, err := s.queries.SoftAccount(ctx, did) if errors.Is(err, sql.ErrNoRows) { return false, nil } if err != nil || !domain.SoftLists(s.softMode) || a.BootstrapUs == 0 { return a.BootstrapUs != 0, err } _, err = s.queries.ListBootstrap(ctx, did) if errors.Is(err, sql.ErrNoRows) { return false, nil } return err == nil, err}
// Keep replay watermarks and account state separate from Provenance participation.func (s *SQLite) applySoft(ctx context.Context, q *db.Queries, change domain.Change) error { if s.softMode == domain.SoftDisabled { return nil } if !domain.SoftAll(s.softMode) { if _, err := q.Participant(ctx, change.DID); errors.Is(err, sql.ErrNoRows) { return nil } else if err != nil { return err } } if err := joinSoft(ctx, q, change.DID); err != nil { return err } a, err := q.SoftAccount(ctx, change.DID) if err != nil { return err } if change.TimeUS <= a.BootstrapUs { return nil } if change.Kind == domain.AccountChange { if oldEvent(change, a.AccountUs) { return nil } active := int64(0) if change.Active { active = 1 } if !change.Active && change.Status == domain.DeletedAccount { if err := q.ClearSoft(ctx, change.DID); err != nil { return err } if err := q.ClearSoftVersions(ctx, change.DID); err != nil { return err } return q.SoftDeleted(ctx, db.SoftDeletedParams{Did: change.DID, AccountUs: change.TimeUS, BootstrapUs: change.TimeUS}) } return q.SoftAccountState(ctx, db.SoftAccountStateParams{Did: change.DID, Active: active, AccountUs: change.TimeUS}) } if change.Kind != domain.RecordChange || !domain.SoftSource(change.Source) { return nil } if change.RecordKey == "" { return errors.New("soft record key is required") } version, err := q.SoftVersion(ctx, db.SoftVersionParams{Author: change.DID, Source: change.Source, RecordKey: change.RecordKey}) if err != nil && !errors.Is(err, sql.ErrNoRows) { return err } if oldEvent(change, version) { return nil } return applySoftRecord(ctx, q, change)}
// Preserve path invalidation for every accepted record, including unchanged values.func applySoftRecord(ctx context.Context, q *db.Queries, change domain.Change) error { if err := validateSoftChange(change); err != nil { return err }
if err := q.DeleteSoft(ctx, db.DeleteSoftParams{Author: change.DID, Source: change.Source, RecordKey: change.RecordKey}); err != nil { return err } if v := change.Vouch; v != nil { if err := putSoft(ctx, q, *v); err != nil { return err } } return q.SetSoftVersion(ctx, db.SetSoftVersionParams{Author: change.DID, Source: change.Source, RecordKey: change.RecordKey, TimeUs: change.TimeUS})}
func validateSoftChange(change domain.Change) error { if v := change.Vouch; v != nil { if v.Author != change.DID || v.Source != change.Source || v.RecordKey != change.RecordKey { return errors.New("soft record does not match its repository or source") } if err := domain.ValidateVouch(*v); err != nil { return err } }
return nil}
func replaceSoft(ctx context.Context, q *db.Queries, did string, vouches []domain.Vouch, at time.Time) error { if err := joinSoft(ctx, q, did); err != nil { return err } previous, err := q.SoftRecords(ctx, did) if err != nil { return err } keys := make(map[[2]string]struct{}, len(vouches)) for _, v := range vouches { if !domain.SoftSource(v.Source) { continue } if v.Author != did { return errors.New("soft snapshot author does not match repository") } if err := domain.ValidateVouch(v); err != nil { return err } key := [2]string{v.Source, v.RecordKey} if _, exists := keys[key]; exists { return errors.New("duplicate soft snapshot record") } keys[key] = struct{}{} if err := putSoft(ctx, q, v); err != nil { return err } } for _, v := range previous { if _, exists := keys[[2]string{v.Source, v.RecordKey}]; exists { continue } if err := q.DeleteSoft(ctx, db.DeleteSoftParams{Author: did, Source: v.Source, RecordKey: v.RecordKey}); err != nil { return err } } if err := q.ClearSoftVersions(ctx, did); err != nil { return err } return q.SoftBootstrap(ctx, db.SoftBootstrapParams{Did: did, AccountUs: at.UnixMicro(), BootstrapUs: at.UnixMicro()})}