Browse Source

Refactor database methods: simplify `SaveEvent` signature, enhance logging, and introduce deletion check logic with `CheckForDeleted`.

main
mleku 4 months ago
parent
commit
b08e94807b
No known key found for this signature in database
  1. 17
      app/handle-event.go
  2. 5
      go.mod
  3. 3
      main.go
  4. 5
      pkg/database/database.go
  5. 2
      pkg/database/export_test.go
  6. 2
      pkg/database/fetch-event-by-serial_test.go
  7. 2
      pkg/database/get-serial-by-id_test.go
  8. 2
      pkg/database/get-serials-by-range_test.go
  9. 2
      pkg/database/import.go
  10. 10
      pkg/database/migrations.go
  11. 6
      pkg/database/query-events-multiple-param-replaceable_test.go
  12. 20
      pkg/database/query-events_test.go
  13. 2
      pkg/database/query-for-created-at_test.go
  14. 225
      pkg/database/query-for-deleted.go
  15. 2
      pkg/database/query-for-ids_test.go
  16. 2
      pkg/database/query-for-kinds-authors-tags_test.go
  17. 2
      pkg/database/query-for-kinds-authors_test.go
  18. 2
      pkg/database/query-for-kinds-tags_test.go
  19. 2
      pkg/database/query-for-kinds_test.go
  20. 2
      pkg/database/query-for-serials_test.go
  21. 2
      pkg/database/query-for-tags_test.go
  22. 99
      pkg/database/save-event.go
  23. 10
      pkg/database/save-event_test.go
  24. 1
      pkg/encoders/go.mod
  25. 3
      pkg/encoders/reason/reason.go
  26. 4
      pkg/interfaces/store/store_interface.go

17
app/handle-event.go

@ -3,6 +3,7 @@ package app
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"encoders.orly/envelopes/eventenvelope" "encoders.orly/envelopes/eventenvelope"
"lol.mleku.dev/chk" "lol.mleku.dev/chk"
@ -53,8 +54,22 @@ func (l *Listener) HandleEvent(c context.Context, msg []byte) (
} }
return return
} }
// check if the event was deleted
//
// todo: the list of admin pubkeys should go in the second parameter when it
// is implemented to enable admins to delete events of other users.
if err = l.CheckForDeleted(env.E, nil); err != nil {
if strings.HasPrefix(err.Error(), "blocked:") {
errStr := err.Error()[len("blocked: "):len(err.Error())]
if err = Ok.Error(
l, env, errStr,
); chk.E(err) {
return
}
}
}
// store the event // store the event
if _, _, err = l.SaveEvent(c, env.E, false, nil); chk.E(err) { if _, _, err = l.SaveEvent(c, env.E); chk.E(err) {
return return
} }
l.publishers.Deliver(env.E) l.publishers.Deliver(env.E)

5
go.mod

@ -7,8 +7,10 @@ require (
encoders.orly v0.0.0-00010101000000-000000000000 encoders.orly v0.0.0-00010101000000-000000000000
github.com/adrg/xdg v0.5.3 github.com/adrg/xdg v0.5.3
github.com/coder/websocket v1.8.13 github.com/coder/websocket v1.8.13
github.com/dgraph-io/badger/v4 v4.8.0
github.com/pkg/profile v1.7.0 github.com/pkg/profile v1.7.0
go-simpler.org/env v0.12.0 go-simpler.org/env v0.12.0
interfaces.orly v0.0.0-00010101000000-000000000000
lol.mleku.dev v1.0.2 lol.mleku.dev v1.0.2
protocol.orly v0.0.0-00010101000000-000000000000 protocol.orly v0.0.0-00010101000000-000000000000
utils.orly v0.0.0-00010101000000-000000000000 utils.orly v0.0.0-00010101000000-000000000000
@ -18,7 +20,6 @@ require (
crypto.orly v0.0.0-00010101000000-000000000000 // indirect crypto.orly v0.0.0-00010101000000-000000000000 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/badger/v4 v4.8.0 // indirect
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.18.0 // indirect github.com/fatih/color v1.18.0 // indirect
@ -43,7 +44,7 @@ require (
golang.org/x/net v0.41.0 // indirect golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.35.0 // indirect golang.org/x/sys v0.35.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
interfaces.orly v0.0.0-00010101000000-000000000000 // indirect lukechampine.com/frand v1.5.1 // indirect
) )
replace ( replace (

3
main.go

@ -36,8 +36,11 @@ func main() {
case <-sigs: case <-sigs:
fmt.Printf("\r") fmt.Printf("\r")
cancel() cancel()
chk.E(db.Close())
return
case <-quit: case <-quit:
cancel() cancel()
chk.E(db.Close())
return return
} }
} }

5
pkg/database/database.go

@ -79,8 +79,8 @@ func New(
case <-d.ctx.Done(): case <-d.ctx.Done():
} }
d.cancel() d.cancel()
d.seq.Release() // d.seq.Release()
d.DB.Close() // d.DB.Close()
}() }()
return return
} }
@ -131,5 +131,6 @@ func (d *D) Close() (err error) {
return return
} }
} }
log.I.F("%s: database closed", d.dataDir)
return return
} }

2
pkg/database/export_test.go

@ -55,7 +55,7 @@ func TestExport(t *testing.T) {
} }
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event: %v", err) t.Fatalf("Failed to save event: %v", err)
} }

2
pkg/database/fetch-event-by-serial_test.go

@ -58,7 +58,7 @@ func TestFetchEventBySerial(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/get-serial-by-id_test.go

@ -54,7 +54,7 @@ func TestGetSerialById(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/get-serials-by-range_test.go

@ -61,7 +61,7 @@ func TestGetSerialsByRange(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/import.go

@ -57,7 +57,7 @@ func (d *D) Import(rr io.Reader) {
continue continue
} }
if _, _, err = d.SaveEvent(d.ctx, ev, false, nil); err != nil { if _, _, err = d.SaveEvent(d.ctx, ev); err != nil {
continue continue
} }

10
pkg/database/migrations.go

@ -14,11 +14,10 @@ import (
) )
const ( const (
currentVersion uint32 = 0 currentVersion uint32 = 1
) )
func (d *D) RunMigrations() { func (d *D) RunMigrations() {
log.I.F("running migrations...")
var err error var err error
var dbVersion uint32 var dbVersion uint32
// first find the current version tag if any // first find the current version tag if any
@ -48,6 +47,7 @@ func (d *D) RunMigrations() {
); chk.E(err) { ); chk.E(err) {
return return
} }
log.I.F("found version tag: %d", ver.Get())
dbVersion = ver.Get() dbVersion = ver.Get()
} }
return return
@ -62,9 +62,13 @@ func (d *D) RunMigrations() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
vv := new(types.Uint32) vv := new(types.Uint32)
vv.Set(currentVersion) vv.Set(currentVersion)
log.I.S(vv)
if err = indexes.VersionEnc(vv).MarshalWrite(buf); chk.E(err) { if err = indexes.VersionEnc(vv).MarshalWrite(buf); chk.E(err) {
return return
} }
if err = txn.Set(buf.Bytes(), nil); chk.E(err) {
return
}
return return
}, },
); chk.E(err) { ); chk.E(err) {
@ -72,10 +76,10 @@ func (d *D) RunMigrations() {
} }
} }
if dbVersion < 1 { if dbVersion < 1 {
log.I.F("migrating to version 1...")
// the first migration is expiration tags // the first migration is expiration tags
d.UpdateExpirationTags() d.UpdateExpirationTags()
} }
log.I.F("migrations complete")
} }
func (d *D) UpdateExpirationTags() { func (d *D) UpdateExpirationTags() {

6
pkg/database/query-events-multiple-param-replaceable_test.go

@ -43,7 +43,7 @@ func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
baseEvent.Sign(sign) baseEvent.Sign(sign)
// Save the base parameterized replaceable event // Save the base parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, baseEvent, false, nil); err != nil { if _, _, err := db.SaveEvent(ctx, baseEvent); err != nil {
t.Fatalf("Failed to save base parameterized replaceable event: %v", err) t.Fatalf("Failed to save base parameterized replaceable event: %v", err)
} }
@ -61,7 +61,7 @@ func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
newerEvent.Sign(sign) newerEvent.Sign(sign)
// Save the newer parameterized replaceable event // Save the newer parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, newerEvent, false, nil); err != nil { if _, _, err := db.SaveEvent(ctx, newerEvent); err != nil {
t.Fatalf( t.Fatalf(
"Failed to save newer parameterized replaceable event: %v", err, "Failed to save newer parameterized replaceable event: %v", err,
) )
@ -81,7 +81,7 @@ func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
newestEvent.Sign(sign) newestEvent.Sign(sign)
// Save the newest parameterized replaceable event // Save the newest parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, newestEvent, false, nil); err != nil { if _, _, err := db.SaveEvent(ctx, newestEvent); err != nil {
t.Fatalf( t.Fatalf(
"Failed to save newest parameterized replaceable event: %v", err, "Failed to save newest parameterized replaceable event: %v", err,
) )

20
pkg/database/query-events_test.go

@ -62,7 +62,7 @@ func setupTestDB(t *testing.T) (
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }
@ -202,9 +202,7 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
replaceableEvent.Tags = tag.NewS() replaceableEvent.Tags = tag.NewS()
replaceableEvent.Sign(sign) replaceableEvent.Sign(sign)
// Save the replaceable event // Save the replaceable event
if _, _, err := db.SaveEvent( if _, _, err := db.SaveEvent(ctx, replaceableEvent); err != nil {
ctx, replaceableEvent, false, nil,
); err != nil {
t.Fatalf("Failed to save replaceable event: %v", err) t.Fatalf("Failed to save replaceable event: %v", err)
} }
@ -217,7 +215,7 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
newerEvent.Tags = tag.NewS() newerEvent.Tags = tag.NewS()
newerEvent.Sign(sign) newerEvent.Sign(sign)
// Save the newer event // Save the newer event
if _, _, err := db.SaveEvent(ctx, newerEvent, false, nil); err != nil { if _, _, err := db.SaveEvent(ctx, newerEvent); err != nil {
t.Fatalf("Failed to save newer event: %v", err) t.Fatalf("Failed to save newer event: %v", err)
} }
@ -294,7 +292,7 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
) )
// Save the deletion event // Save the deletion event
if _, _, err = db.SaveEvent(ctx, deletionEvent, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, deletionEvent); err != nil {
t.Fatalf("Failed to save deletion event: %v", err) t.Fatalf("Failed to save deletion event: %v", err)
} }
@ -379,7 +377,7 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
paramEvent.Sign(sign) paramEvent.Sign(sign)
// Save the parameterized replaceable event // Save the parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, paramEvent, false, nil); err != nil { if _, _, err := db.SaveEvent(ctx, paramEvent); err != nil {
t.Fatalf("Failed to save parameterized replaceable event: %v", err) t.Fatalf("Failed to save parameterized replaceable event: %v", err)
} }
@ -405,9 +403,7 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
paramDeletionEvent.Sign(sign) paramDeletionEvent.Sign(sign)
// Save the parameterized deletion event // Save the parameterized deletion event
if _, _, err := db.SaveEvent( if _, _, err := db.SaveEvent(ctx, paramDeletionEvent); err != nil {
ctx, paramDeletionEvent, false, nil,
); err != nil {
t.Fatalf("Failed to save parameterized deletion event: %v", err) t.Fatalf("Failed to save parameterized deletion event: %v", err)
} }
@ -440,9 +436,7 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
paramDeletionEvent2.Sign(sign) paramDeletionEvent2.Sign(sign)
// Save the parameterized deletion event with e-tag // Save the parameterized deletion event with e-tag
if _, _, err := db.SaveEvent( if _, _, err := db.SaveEvent(ctx, paramDeletionEvent2); err != nil {
ctx, paramDeletionEvent2, false, nil,
); err != nil {
t.Fatalf( t.Fatalf(
"Failed to save parameterized deletion event with e-tag: %v", err, "Failed to save parameterized deletion event with e-tag: %v", err,
) )

2
pkg/database/query-for-created-at_test.go

@ -58,7 +58,7 @@ func TestQueryForCreatedAt(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

225
pkg/database/query-for-deleted.go

@ -0,0 +1,225 @@
package database
import (
"fmt"
"sort"
"database.orly/indexes/types"
"encoders.orly/event"
"encoders.orly/filter"
"encoders.orly/hex"
"encoders.orly/kind"
"encoders.orly/tag"
"encoders.orly/tag/atag"
"interfaces.orly/store"
"lol.mleku.dev/chk"
"lol.mleku.dev/errorf"
)
// CheckForDeleted checks if the event is deleted, and returns an error with
// prefix "blocked:" if it is. This function also allows designating admin
// pubkeys that also may delete the event, normally only the author is allowed
// to delete an event.
func (d *D) CheckForDeleted(ev *event.E, admins [][]byte) (err error) {
keys := append([][]byte{ev.Pubkey}, admins...)
authors := tag.NewFromBytesSlice(keys...)
// if the event is addressable, check for a deletion event with the same
// kind/pubkey/dtag
if kind.IsParameterizedReplaceable(ev.Kind) {
var idxs []Range
// construct a tag
t := ev.Tags.GetFirst([]byte("d"))
a := atag.T{
Kind: kind.New(ev.Kind),
PubKey: ev.Pubkey,
DTag: t.Value(),
}
at := a.Marshal(nil)
if idxs, err = GetIndexesFromFilter(
&filter.F{
Authors: authors,
Kinds: kind.NewS(kind.Deletion),
Tags: tag.NewS(tag.NewFromAny("#a", at)),
},
); chk.E(err) {
return
}
var sers types.Uint40s
for _, idx := range idxs {
var s types.Uint40s
if s, err = d.GetSerialsByRange(idx); chk.E(err) {
return
}
sers = append(sers, s...)
}
if len(sers) > 0 {
// there can be multiple of these because the author/kind/tag is a
// stable value but refers to any event from the author, of the
// kind, with the identifier. so we need to fetch the full ID index
// to get the timestamp and ensure that the event post-dates it.
// otherwise, it should be rejected.
var idPkTss []*store.IdPkTs
var tmp []*store.IdPkTs
if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
return
}
idPkTss = append(idPkTss, tmp...)
// sort by timestamp, so the first is the newest, which the event
// must be newer to not be deleted.
sort.Slice(
idPkTss, func(i, j int) bool {
return idPkTss[i].Ts > idPkTss[j].Ts
},
)
if ev.CreatedAt < idPkTss[0].Ts {
err = errorf.E(
"blocked: %0x was deleted by address %s because it is older than the delete: event: %d delete: %d",
ev.ID, at, ev.CreatedAt, idPkTss[0].Ts,
)
return
}
return
}
return
}
// if the event is replaceable, check if there is a deletion event newer
// than the event, it must specify the same kind/pubkey. this type of delete
// only has the k tag to specify the kind, it can be what an author would
// use, as the author is part of the replaceable event specification.
if kind.IsReplaceable(ev.Kind) {
var idxs []Range
if idxs, err = GetIndexesFromFilter(
&filter.F{
Authors: tag.NewFromBytesSlice(ev.Pubkey),
Kinds: kind.NewS(kind.Deletion),
Tags: tag.NewS(
tag.NewFromAny("#k", fmt.Sprint(ev.Kind)),
),
},
); chk.E(err) {
return
}
var sers types.Uint40s
for _, idx := range idxs {
var s types.Uint40s
if s, err = d.GetSerialsByRange(idx); chk.E(err) {
return
}
sers = append(sers, s...)
}
if len(sers) > 0 {
var idPkTss []*store.IdPkTs
var tmp []*store.IdPkTs
if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
return
}
idPkTss = append(idPkTss, tmp...)
// sort by timestamp, so the first is the newest, which the event
// must be newer to not be deleted.
sort.Slice(
idPkTss, func(i, j int) bool {
return idPkTss[i].Ts > idPkTss[j].Ts
},
)
if ev.CreatedAt < idPkTss[0].Ts {
err = errorf.E(
"blocked: %0x was deleted: the event is older than the delete event %0x: event: %d delete: %d",
ev.ID, idPkTss[0].Id, ev.CreatedAt, idPkTss[0].Ts,
)
return
}
}
// this type of delete can also use an a tag to specify kind and
// author, which would be required for admin deletes
idxs = nil
// construct a tag
a := atag.T{
Kind: kind.New(ev.Kind),
PubKey: ev.Pubkey,
}
at := a.Marshal(nil)
if idxs, err = GetIndexesFromFilter(
&filter.F{
Authors: authors,
Kinds: kind.NewS(kind.Deletion),
Tags: tag.NewS(tag.NewFromAny("#a", at)),
},
); chk.E(err) {
return
}
sers = nil
for _, idx := range idxs {
var s types.Uint40s
if s, err = d.GetSerialsByRange(idx); chk.E(err) {
return
}
sers = append(sers, s...)
}
if len(sers) > 0 {
var idPkTss []*store.IdPkTs
var tmp []*store.IdPkTs
if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
return
}
idPkTss = append(idPkTss, tmp...)
// sort by timestamp, so the first is the newest
sort.Slice(
idPkTss, func(i, j int) bool {
return idPkTss[i].Ts > idPkTss[j].Ts
},
)
if ev.CreatedAt < idPkTss[0].Ts {
err = errorf.E(
"blocked: %0x was deleted by address %s: event is older than the delete: event: %d delete: %d",
ev.ID, at, idPkTss[0].Id, ev.CreatedAt, idPkTss[0].Ts,
)
return
}
}
}
// otherwise we check for a delete by event id
var idxs []Range
if idxs, err = GetIndexesFromFilter(
&filter.F{
Authors: authors,
Kinds: kind.NewS(kind.Deletion),
Tags: tag.NewS(
tag.NewFromAny("#e", hex.Enc(ev.ID)),
),
},
); chk.E(err) {
return
}
var sers types.Uint40s
for _, idx := range idxs {
var s types.Uint40s
if s, err = d.GetSerialsByRange(idx); chk.E(err) {
return
}
sers = append(sers, s...)
}
if len(sers) > 0 {
var idPkTss []*store.IdPkTs
var tmp []*store.IdPkTs
if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
return
}
idPkTss = append(idPkTss, tmp...)
// sort by timestamp, so the first is the newest
sort.Slice(
idPkTss, func(i, j int) bool {
return idPkTss[i].Ts > idPkTss[j].Ts
},
)
if ev.CreatedAt < idPkTss[0].Ts {
err = errorf.E(
"blocked: %0x was deleted because it is older than the delete: event: %d delete: %d",
ev.ID, ev.CreatedAt, idPkTss[0].Ts,
)
return
}
return
}
return
}

2
pkg/database/query-for-ids_test.go

@ -60,7 +60,7 @@ func TestQueryForIds(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds-authors-tags_test.go

@ -59,7 +59,7 @@ func TestQueryForKindsAuthorsTags(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds-authors_test.go

@ -59,7 +59,7 @@ func TestQueryForKindsAuthors(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds-tags_test.go

@ -59,7 +59,7 @@ func TestQueryForKindsTags(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds_test.go

@ -58,7 +58,7 @@ func TestQueryForKinds(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-serials_test.go

@ -61,7 +61,7 @@ func TestQueryForSerials(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-tags_test.go

@ -58,7 +58,7 @@ func TestQueryForTags(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

99
pkg/database/save-event.go

@ -3,121 +3,24 @@ package database
import ( import (
"bytes" "bytes"
"context" "context"
"sort"
"database.orly/indexes" "database.orly/indexes"
"database.orly/indexes/types" "database.orly/indexes/types"
"encoders.orly/event" "encoders.orly/event"
"encoders.orly/filter"
"encoders.orly/kind"
"encoders.orly/tag"
"encoders.orly/tag/atag"
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
"interfaces.orly/store"
"lol.mleku.dev/chk" "lol.mleku.dev/chk"
"lol.mleku.dev/errorf" "lol.mleku.dev/errorf"
"lol.mleku.dev/log" "lol.mleku.dev/log"
) )
// SaveEvent saves an event to the database, generating all the necessary indexes. // SaveEvent saves an event to the database, generating all the necessary indexes.
func (d *D) SaveEvent( func (d *D) SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error) {
c context.Context, ev *event.E, noVerify bool, owners [][]byte,
) (kc, vc int, err error) {
if !noVerify {
// check if the event already exists // check if the event already exists
var ser *types.Uint40 var ser *types.Uint40
if ser, err = d.GetSerialById(ev.ID); err == nil && ser != nil { if ser, err = d.GetSerialById(ev.ID); err == nil && ser != nil {
err = errorf.E("event already exists: %0x", ev.ID) err = errorf.E("event already exists: %0x", ev.ID)
return return
} }
}
// check if an existing delete event references this event submission
if kind.IsParameterizedReplaceable(ev.Kind) {
var idxs []Range
// construct a tag
t := ev.Tags.GetFirst([]byte("d"))
a := atag.T{
Kind: kind.New(ev.Kind),
PubKey: ev.Pubkey,
DTag: t.Value(),
}
at := a.Marshal(nil)
if idxs, err = GetIndexesFromFilter(
&filter.F{
Authors: tag.NewFromBytesSlice(ev.Pubkey),
Kinds: kind.NewS(kind.Deletion),
Tags: tag.NewS(tag.NewFromAny("#a", at)),
},
); chk.E(err) {
return
}
var sers types.Uint40s
for _, idx := range idxs {
var s types.Uint40s
if s, err = d.GetSerialsByRange(idx); chk.E(err) {
return
}
sers = append(sers, s...)
}
if len(sers) > 0 {
// there can be multiple of these because the author/kind/tag is a
// stable value but refers to any event from the author, of the
// kind, with the identifier. so we need to fetch the full ID index
// to get the timestamp and ensure that the event post-dates it.
// otherwise, it should be rejected.
var idPkTss []*store.IdPkTs
var tmp []*store.IdPkTs
if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
return
}
idPkTss = append(idPkTss, tmp...)
// sort by timestamp, so the first is the newest
sort.Slice(
idPkTss, func(i, j int) bool {
return idPkTss[i].Ts > idPkTss[j].Ts
},
)
if ev.CreatedAt < idPkTss[0].Ts {
err = errorf.E(
"blocked: %0x was deleted by address %s because it is older than the delete: event: %d delete: %d",
ev.ID, at, ev.CreatedAt, idPkTss[0].Ts,
)
return
}
return
}
} else {
var idxs []Range
keys := [][]byte{ev.Pubkey}
for _, owner := range owners {
keys = append(keys, owner)
}
if idxs, err = GetIndexesFromFilter(
&filter.F{
Authors: tag.NewFromBytesSlice(keys...),
Kinds: kind.NewS(kind.Deletion),
Tags: tag.NewS(tag.NewFromAny("#e", ev.ID)),
},
); chk.E(err) {
return
}
var sers types.Uint40s
for _, idx := range idxs {
var s types.Uint40s
if s, err = d.GetSerialsByRange(idx); chk.E(err) {
return
}
sers = append(sers, s...)
}
if len(sers) > 0 {
// really there can only be one of these; the chances of an idhash
// collision are basically zero in practice, at least, one in a
// billion or more anyway, more than a human is going to create.
err = errorf.E("blocked: event %0x deleted by event ID", ev.ID)
return
}
}
// Get the next sequence number for the event // Get the next sequence number for the event
var serial uint64 var serial uint64
if serial, err = d.seq.Next(); chk.E(err) { if serial, err = d.seq.Next(); chk.E(err) {

10
pkg/database/save-event_test.go

@ -65,7 +65,7 @@ func TestSaveEvents(t *testing.T) {
// Save the event to the database // Save the event to the database
var k, v int var k, v int
if k, v, err = db.SaveEvent(ctx, ev, false, nil); err != nil { if k, v, err = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }
kc += k kc += k
@ -125,7 +125,7 @@ func TestDeletionEventWithETagRejection(t *testing.T) {
regularEvent.Sign(sign) regularEvent.Sign(sign)
// Save the regular event // Save the regular event
if _, _, err := db.SaveEvent(ctx, regularEvent, false, nil); err != nil { if _, _, err := db.SaveEvent(ctx, regularEvent); err != nil {
t.Fatalf("Failed to save regular event: %v", err) t.Fatalf("Failed to save regular event: %v", err)
} }
@ -151,7 +151,7 @@ func TestDeletionEventWithETagRejection(t *testing.T) {
err = errorf.E("deletion events referencing other events with 'e' tag are not allowed") err = errorf.E("deletion events referencing other events with 'e' tag are not allowed")
} else { } else {
// Try to save the deletion event // Try to save the deletion event
_, _, err = db.SaveEvent(ctx, deletionEvent, false, nil) _, _, err = db.SaveEvent(ctx, deletionEvent)
} }
if err == nil { if err == nil {
@ -204,12 +204,12 @@ func TestSaveExistingEvent(t *testing.T) {
ev.Sign(sign) ev.Sign(sign)
// Save the event for the first time // Save the event for the first time
if _, _, err := db.SaveEvent(ctx, ev, false, nil); err != nil { if _, _, err := db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event: %v", err) t.Fatalf("Failed to save event: %v", err)
} }
// Try to save the same event again, it should be rejected // Try to save the same event again, it should be rejected
_, _, err = db.SaveEvent(ctx, ev, false, nil) _, _, err = db.SaveEvent(ctx, ev)
if err == nil { if err == nil {
t.Fatal("Expected error when saving an existing event, but got nil") t.Fatal("Expected error when saving an existing event, but got nil")
} }

1
pkg/encoders/go.mod

@ -26,6 +26,7 @@ require (
replace ( replace (
acl.orly => ../acl acl.orly => ../acl
crypto.orly => ../crypto crypto.orly => ../crypto
database.orly => ../database
encoders.orly => ../encoders encoders.orly => ../encoders
interfaces.orly => ../interfaces interfaces.orly => ../interfaces
next.orly.dev => ../../ next.orly.dev => ../../

3
pkg/encoders/reason/reason.go

@ -3,8 +3,6 @@ package reason
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"lol.mleku.dev/log"
) )
// R is the machine-readable prefix before the colon in an OK or CLOSED envelope message. // R is the machine-readable prefix before the colon in an OK or CLOSED envelope message.
@ -38,7 +36,6 @@ func (r R) IsPrefix(reason []byte) bool {
// F allows creation of a full R text with a printf style format. // F allows creation of a full R text with a printf style format.
func (r R) F(format string, params ...any) (o []byte) { func (r R) F(format string, params ...any) (o []byte) {
log.D.F(format, params...)
return Msg(r, format, params...) return Msg(r, format, params...)
} }

4
pkg/interfaces/store/store_interface.go

@ -84,9 +84,7 @@ type Saver interface {
// SaveEvent is called once relay.AcceptEvent reports true. The owners // SaveEvent is called once relay.AcceptEvent reports true. The owners
// parameter is for designating admins whose delete by e tag events apply // parameter is for designating admins whose delete by e tag events apply
// the same as author's own. // the same as author's own.
SaveEvent( SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error)
c context.Context, ev *event.E, noVerify bool, owners [][]byte,
) (kc, vc int, err error)
} }
type Importer interface { type Importer interface {

Loading…
Cancel
Save