- Update nostr dependency to v1.0.11 with new types package - Add IDFixed(), PubFixed(), IDHex(), PubHex() methods to IdPkTs - Add EventRef type: 80-byte stack-allocated event reference - Add ToEventRef()/ToIdPkTs() conversion methods - Update tests to use IDHex() instead of hex.Enc(r.Id) EventRef provides: - Copy-on-assignment semantics (arrays vs slices) - Zero heap allocations for event reference passing - Type-safe fixed-size fields (EventID, Pubkey) Files modified: - go.mod, go.sum: Update nostr to v1.0.11 - pkg/interfaces/store/store_interface.go: Add methods and EventRef type - pkg/interfaces/store/store_interface_test.go: New test file - pkg/database/binary_tag_filter_test.go: Use IDHex() - pkg/neo4j/fetch-event_test.go: Use IDHex(), PubHex() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
224 lines
5.9 KiB
Go
224 lines
5.9 KiB
Go
// Package store is an interface and ancillary helpers and types for defining a
|
|
// series of API elements for abstracting the event storage from the
|
|
// implementation.
|
|
//
|
|
// It is composed so that the top-level interface can be
|
|
// partially implemented if need be.
|
|
package store
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
|
|
"next.orly.dev/app/config"
|
|
"next.orly.dev/pkg/database/indexes/types"
|
|
"git.mleku.dev/mleku/nostr/encoders/event"
|
|
"git.mleku.dev/mleku/nostr/encoders/filter"
|
|
"git.mleku.dev/mleku/nostr/encoders/tag"
|
|
ntypes "git.mleku.dev/mleku/nostr/types"
|
|
)
|
|
|
|
// I am a type for a persistence layer for nostr events handled by a relay.
|
|
type I interface {
|
|
Pather
|
|
io.Closer
|
|
Pather
|
|
Wiper
|
|
Querier
|
|
Querent
|
|
Deleter
|
|
Saver
|
|
Importer
|
|
Exporter
|
|
Syncer
|
|
LogLeveler
|
|
EventIdSerialer
|
|
Initer
|
|
SerialByIder
|
|
}
|
|
|
|
type Initer interface {
|
|
Init(path string) (err error)
|
|
}
|
|
|
|
type Pather interface {
|
|
// Path returns the directory of the database.
|
|
Path() (s string)
|
|
}
|
|
|
|
type Wiper interface {
|
|
// Wipe deletes everything in the database.
|
|
Wipe() (err error)
|
|
}
|
|
|
|
type Querent interface {
|
|
// QueryEvents is invoked upon a client's REQ as described in NIP-01. It
|
|
// returns the matching events in reverse chronological order in a slice.
|
|
QueryEvents(c context.Context, f *filter.F) (evs event.S, err error)
|
|
}
|
|
|
|
type Accountant interface {
|
|
EventCount() (count uint64, err error)
|
|
}
|
|
|
|
// IdPkTs holds event reference data with slice fields for backward compatibility.
|
|
// For new code preferring stack-allocated, copy-on-assignment semantics,
|
|
// use the IDFixed() and PubFixed() methods or convert to EventRef.
|
|
type IdPkTs struct {
|
|
Id []byte
|
|
Pub []byte
|
|
Ts int64
|
|
Ser uint64
|
|
}
|
|
|
|
// IDFixed returns the event ID as a fixed-size array (stack-allocated, copied on assignment).
|
|
func (i *IdPkTs) IDFixed() ntypes.EventID {
|
|
return ntypes.EventIDFromBytes(i.Id)
|
|
}
|
|
|
|
// PubFixed returns the pubkey as a fixed-size array (stack-allocated, copied on assignment).
|
|
func (i *IdPkTs) PubFixed() ntypes.Pubkey {
|
|
return ntypes.PubkeyFromBytes(i.Pub)
|
|
}
|
|
|
|
// IDHex returns the event ID as a lowercase hex string.
|
|
func (i *IdPkTs) IDHex() string {
|
|
return ntypes.EventIDFromBytes(i.Id).Hex()
|
|
}
|
|
|
|
// PubHex returns the pubkey as a lowercase hex string.
|
|
func (i *IdPkTs) PubHex() string {
|
|
return ntypes.PubkeyFromBytes(i.Pub).Hex()
|
|
}
|
|
|
|
// ToEventRef converts IdPkTs to an EventRef (fully stack-allocated).
|
|
func (i *IdPkTs) ToEventRef() EventRef {
|
|
return NewEventRef(i.Id, i.Pub, i.Ts, i.Ser)
|
|
}
|
|
|
|
// EventRef is a stack-friendly event reference using fixed-size arrays.
|
|
// Total size: 80 bytes (32+32+8+8), fits in a cache line, copies stay on stack.
|
|
// Use this type when you need safe, immutable event references.
|
|
type EventRef struct {
|
|
id ntypes.EventID // 32 bytes
|
|
pub ntypes.Pubkey // 32 bytes
|
|
ts int64 // 8 bytes
|
|
ser uint64 // 8 bytes
|
|
}
|
|
|
|
// NewEventRef creates an EventRef from byte slices.
|
|
// The slices are copied into fixed-size arrays.
|
|
func NewEventRef(id, pub []byte, ts int64, ser uint64) EventRef {
|
|
return EventRef{
|
|
id: ntypes.EventIDFromBytes(id),
|
|
pub: ntypes.PubkeyFromBytes(pub),
|
|
ts: ts,
|
|
ser: ser,
|
|
}
|
|
}
|
|
|
|
// ID returns the event ID (copy, stays on stack).
|
|
func (r EventRef) ID() ntypes.EventID { return r.id }
|
|
|
|
// Pub returns the pubkey (copy, stays on stack).
|
|
func (r EventRef) Pub() ntypes.Pubkey { return r.pub }
|
|
|
|
// Ts returns the timestamp.
|
|
func (r EventRef) Ts() int64 { return r.ts }
|
|
|
|
// Ser returns the serial number.
|
|
func (r EventRef) Ser() uint64 { return r.ser }
|
|
|
|
// IDHex returns the event ID as lowercase hex.
|
|
func (r EventRef) IDHex() string { return r.id.Hex() }
|
|
|
|
// PubHex returns the pubkey as lowercase hex.
|
|
func (r EventRef) PubHex() string { return r.pub.Hex() }
|
|
|
|
// IDSlice returns a slice view of the ID (shares memory, use carefully).
|
|
func (r *EventRef) IDSlice() []byte { return r.id.Bytes() }
|
|
|
|
// PubSlice returns a slice view of the pubkey (shares memory, use carefully).
|
|
func (r *EventRef) PubSlice() []byte { return r.pub.Bytes() }
|
|
|
|
// ToIdPkTs converts EventRef to IdPkTs for backward compatibility.
|
|
// Note: This allocates new slices.
|
|
func (r EventRef) ToIdPkTs() *IdPkTs {
|
|
return &IdPkTs{
|
|
Id: r.id.Copy(),
|
|
Pub: r.pub.Copy(),
|
|
Ts: r.ts,
|
|
Ser: r.ser,
|
|
}
|
|
}
|
|
|
|
type Querier interface {
|
|
QueryForIds(c context.Context, f *filter.F) (evs []*IdPkTs, err error)
|
|
}
|
|
|
|
type GetIdsWriter interface {
|
|
FetchIds(c context.Context, evIds *tag.T, out io.Writer) (err error)
|
|
}
|
|
|
|
type Deleter interface {
|
|
// DeleteEvent is used to handle deletion events, as per NIP-09.
|
|
DeleteEvent(c context.Context, ev []byte) (err error)
|
|
}
|
|
|
|
type Saver interface {
|
|
// SaveEvent is called once relay.AcceptEvent reports true. The owners
|
|
// parameter is for designating admins whose delete by e tag events apply
|
|
// the same as author's own.
|
|
SaveEvent(c context.Context, ev *event.E) (replaced bool, err error)
|
|
}
|
|
|
|
type Importer interface {
|
|
// Import reads in a stream of line-structured JSON the events to save into
|
|
// the store.
|
|
Import(r io.Reader)
|
|
}
|
|
|
|
type Exporter interface {
|
|
// Export writes a stream of line structured JSON of all events in the
|
|
// store.
|
|
//
|
|
// If pubkeys are present, only those with these pubkeys in the `pubkey`
|
|
// field and in `p` tags will be included.
|
|
Export(c context.Context, w io.Writer, pubkeys ...[]byte)
|
|
}
|
|
|
|
type Rescanner interface {
|
|
// Rescan triggers the regeneration of indexes of the database to enable old
|
|
// records to be found with new indexes.
|
|
Rescan() (err error)
|
|
}
|
|
|
|
type Syncer interface {
|
|
// Sync signals the event store to flush its buffers.
|
|
Sync() (err error)
|
|
}
|
|
|
|
type Configuration struct {
|
|
BlockList []string `json:"block_list" doc:"list of IP addresses that will be ignored"`
|
|
}
|
|
|
|
type Configurationer interface {
|
|
GetConfiguration() (c config.C, err error)
|
|
SetConfiguration(c config.C) (err error)
|
|
}
|
|
|
|
type LogLeveler interface {
|
|
SetLogLevel(level string)
|
|
}
|
|
|
|
type EventIdSerialer interface {
|
|
EventIdsBySerial(start uint64, count int) (
|
|
evs [][]byte,
|
|
err error,
|
|
)
|
|
}
|
|
|
|
type SerialByIder interface {
|
|
GetSerialById(id []byte) (ser *types.Uint40, err error)
|
|
}
|