From 0130725321562e2061b990bb31250ccaa9594295 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Thu, 17 Apr 2025 00:36:33 -0300 Subject: [PATCH] sdk finally compiles. --- sdk/addressable_loader.go | 18 ++++++++------ sdk/feeds.go | 12 +++++---- sdk/input.go | 20 +++++---------- sdk/list.go | 35 +++++++++++++-------------- sdk/lists_event.go | 8 +++--- sdk/lists_profile.go | 15 ++++++------ sdk/lists_relay.go | 14 +++++------ sdk/lists_topics.go | 8 +++--- sdk/metadata.go | 17 ++++++------- sdk/outbox.go | 4 +-- sdk/replaceable_loader.go | 20 +++++++-------- sdk/search.go | 4 ++- sdk/set.go | 30 ++++++++++++----------- sdk/specific_event.go | 34 ++++++++++++-------------- sdk/system.go | 51 +++++++++++++++++++-------------------- 15 files changed, 141 insertions(+), 149 deletions(-) diff --git a/sdk/addressable_loader.go b/sdk/addressable_loader.go index 478304a..2bebce5 100644 --- a/sdk/addressable_loader.go +++ b/sdk/addressable_loader.go @@ -21,16 +21,16 @@ const ( ) func (sys *System) initializeAddressableDataloaders() { - sys.addressableLoaders = make([]*dataloader.Loader[nostr.PubKey, []*nostr.Event], 4) + sys.addressableLoaders = make([]*dataloader.Loader[nostr.PubKey, []nostr.Event], 4) sys.addressableLoaders[kind_30000] = sys.createAddressableDataloader(30000) sys.addressableLoaders[kind_30002] = sys.createAddressableDataloader(30002) sys.addressableLoaders[kind_30015] = sys.createAddressableDataloader(30015) sys.addressableLoaders[kind_30030] = sys.createAddressableDataloader(30030) } -func (sys *System) createAddressableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, []*nostr.Event] { +func (sys *System) createAddressableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, []nostr.Event] { return dataloader.NewBatchedLoader( - func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[[]*nostr.Event] { + func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[[]nostr.Event] { return sys.batchLoadAddressableEvents(ctxs, kind, pubkeys) }, dataloader.Options{ @@ -44,9 +44,9 @@ func (sys *System) batchLoadAddressableEvents( ctxs []context.Context, kind uint16, pubkeys []nostr.PubKey, -) map[nostr.PubKey]dataloader.Result[[]*nostr.Event] { +) map[nostr.PubKey]dataloader.Result[[]nostr.Event] { batchSize := len(pubkeys) - results := make(map[nostr.PubKey]dataloader.Result[[]*nostr.Event], batchSize) + results := make(map[nostr.PubKey]dataloader.Result[[]nostr.Event], batchSize) relayFilter := make([]nostr.DirectedFilter, 0, max(3, batchSize*2)) relayFilterIndex := make(map[string]int, max(3, batchSize*2)) @@ -103,7 +103,9 @@ func (sys *System) batchLoadAddressableEvents( wg.Wait() // query all relays with the prepared filters - multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter) + multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter, nostr.SubscriptionOptions{ + Label: "loadaddrs", + }) nextEvent: for { select { @@ -115,7 +117,7 @@ nextEvent: events := results[ie.PubKey].Data if events == nil { // no events found, so just add this and end - results[ie.PubKey] = dataloader.Result[[]*nostr.Event]{Data: []*nostr.Event{ie.Event}} + results[ie.PubKey] = dataloader.Result[[]nostr.Event]{Data: []nostr.Event{ie.Event}} continue nextEvent } @@ -136,7 +138,7 @@ nextEvent: } events = append(events, ie.Event) - results[ie.PubKey] = dataloader.Result[[]*nostr.Event]{Data: events} + results[ie.PubKey] = dataloader.Result[[]nostr.Event]{Data: events} case <-aggregatedContext.Done(): return results } diff --git a/sdk/feeds.go b/sdk/feeds.go index 4d88c97..9dd906a 100644 --- a/sdk/feeds.go +++ b/sdk/feeds.go @@ -105,9 +105,9 @@ func (sys *System) FetchFeedPage( kinds []uint16, until nostr.Timestamp, totalLimit int, -) ([]*nostr.Event, error) { +) ([]nostr.Event, error) { limitPerKey := PerQueryLimitInBatch(totalLimit, len(pubkeys)) - events := make([]*nostr.Event, 0, len(pubkeys)*limitPerKey) + events := make([]nostr.Event, 0, len(pubkeys)*limitPerKey) wg := sync.WaitGroup{} wg.Add(len(pubkeys)) @@ -153,8 +153,10 @@ func (sys *System) FetchFeedPage( fUntil := oldestTimestamp + 1 filter.Until = &fUntil filter.Since = nil - for ie := range sys.Pool.FetchMany(ctx, relays, filter, nostr.WithLabel("feedpage")) { - sys.StoreRelay.Publish(ctx, *ie.Event) + for ie := range sys.Pool.FetchMany(ctx, relays, filter, nostr.SubscriptionOptions{ + Label: "feedpage", + }) { + sys.Publisher.Publish(ctx, ie.Event) // we shouldn't need this check here, but against rogue relays we'll do it if ie.Event.CreatedAt < oldestTimestamp { @@ -173,7 +175,7 @@ func (sys *System) FetchFeedPage( } wg.Wait() - slices.SortFunc(events, nostr.CompareEventPtrReverse) + slices.SortFunc(events, nostr.CompareEventReverse) return events, nil } diff --git a/sdk/input.go b/sdk/input.go index c4c08e7..b01e600 100644 --- a/sdk/input.go +++ b/sdk/input.go @@ -2,7 +2,6 @@ package sdk import ( "context" - "encoding/hex" "fiatjaf.com/nostr" "fiatjaf.com/nostr/nip05" @@ -12,18 +11,15 @@ import ( // InputToProfile turns any npub/nprofile/hex/nip05 input into a ProfilePointer (or nil). func InputToProfile(ctx context.Context, input string) *nostr.ProfilePointer { // handle if it is a hex string - if len(input) == 64 { - if _, err := hex.DecodeString(input); err == nil { - return &nostr.ProfilePointer{PublicKey: input} - } + if pk, err := nostr.PubKeyFromHex(input); err == nil { + return &nostr.ProfilePointer{PublicKey: pk} } // handle nip19 codes, if that's the case prefix, data, _ := nip19.Decode(input) switch prefix { case "npub": - input = data.(string) - return &nostr.ProfilePointer{PublicKey: input} + return &nostr.ProfilePointer{PublicKey: data.(nostr.PubKey)} case "nprofile": pp := data.(nostr.ProfilePointer) return &pp @@ -41,19 +37,15 @@ func InputToProfile(ctx context.Context, input string) *nostr.ProfilePointer { // InputToEventPointer turns any note/nevent/hex input into a EventPointer (or nil). func InputToEventPointer(input string) *nostr.EventPointer { // handle if it is a hex string - if len(input) == 64 { - if _, err := hex.DecodeString(input); err == nil { - return &nostr.EventPointer{ID: input} - } + if id, err := nostr.IDFromHex(input); err == nil { + return &nostr.EventPointer{ID: id} } // handle nip19 codes, if that's the case prefix, data, _ := nip19.Decode(input) switch prefix { case "note": - if input, ok := data.(string); ok { - return &nostr.EventPointer{ID: input} - } + return &nostr.EventPointer{ID: data.([32]byte)} case "nevent": if ep, ok := data.(nostr.EventPointer); ok { return &ep diff --git a/sdk/list.go b/sdk/list.go index 2219c71..60c2753 100644 --- a/sdk/list.go +++ b/sdk/list.go @@ -10,15 +10,15 @@ import ( "fiatjaf.com/nostr/sdk/cache" ) -type GenericList[I TagItemWithValue] struct { +type GenericList[V comparable, I TagItemWithValue[V]] struct { PubKey nostr.PubKey `json:"-"` // must always be set otherwise things will break Event *nostr.Event `json:"-"` // may be empty if a contact list event wasn't found Items []I } -type TagItemWithValue interface { - Value() any +type TagItemWithValue[V comparable] interface { + Value() V } var ( @@ -26,15 +26,15 @@ var ( valueWasJustCached = [60]bool{} ) -func fetchGenericList[I TagItemWithValue]( +func fetchGenericList[V comparable, I TagItemWithValue[V]]( sys *System, ctx context.Context, pubkey nostr.PubKey, actualKind uint16, replaceableIndex replaceableIndex, parseTag func(nostr.Tag) (I, bool), - cache cache.Cache32[GenericList[I]], -) (fl GenericList[I], fromInternal bool) { + cache cache.Cache32[GenericList[V, I]], +) (fl GenericList[V, I], fromInternal bool) { // we have 60 mutexes, so we can load up to 60 lists at the same time, but if we do the same exact // call that will do it only once, the subsequent ones will wait for a result to be cached // and then return it from cache -- 13 is an arbitrary index for the pubkey @@ -55,13 +55,12 @@ func fetchGenericList[I TagItemWithValue]( return v, true } - v := GenericList[I]{PubKey: pubkey} + v := GenericList[V, I]{PubKey: pubkey} - events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}) - if len(events) != 0 { + for evt := range sys.Store.QueryEvents(nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}) { // ok, we found something locally - items := parseItemsFromEventTags(events[0], parseTag) - v.Event = events[0] + items := parseItemsFromEventTags(evt, parseTag) + v.Event = &evt v.Items = items // but if we haven't tried fetching from the network recently we should do it @@ -100,30 +99,30 @@ func fetchGenericList[I TagItemWithValue]( return v, false } -func tryFetchListFromNetwork[I TagItemWithValue]( +func tryFetchListFromNetwork[V comparable, I TagItemWithValue[V]]( ctx context.Context, sys *System, pubkey nostr.PubKey, replaceableIndex replaceableIndex, parseTag func(nostr.Tag) (I, bool), -) *GenericList[I] { +) *GenericList[V, I] { evt, err := sys.replaceableLoaders[replaceableIndex].Load(ctx, pubkey) if err != nil { return nil } - v := &GenericList[I]{ + v := &GenericList[V, I]{ PubKey: pubkey, - Event: evt, + Event: &evt, Items: parseItemsFromEventTags(evt, parseTag), } - sys.StoreRelay.Publish(ctx, *evt) + sys.Publisher.Publish(ctx, evt) return v } -func parseItemsFromEventTags[I TagItemWithValue]( - evt *nostr.Event, +func parseItemsFromEventTags[V comparable, I TagItemWithValue[V]]( + evt nostr.Event, parseTag func(nostr.Tag) (I, bool), ) []I { result := make([]I, 0, len(evt.Tags)) diff --git a/sdk/lists_event.go b/sdk/lists_event.go index 4624b49..a04a90c 100644 --- a/sdk/lists_event.go +++ b/sdk/lists_event.go @@ -11,18 +11,18 @@ type EventRef struct{ nostr.Pointer } func (e EventRef) Value() string { return e.Pointer.AsTagReference() } -func (sys *System) FetchBookmarkList(ctx context.Context, pubkey string) GenericList[EventRef] { +func (sys *System) FetchBookmarkList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, EventRef] { if sys.BookmarkListCache == nil { - sys.BookmarkListCache = cache_memory.New[GenericList[EventRef]](1000) + sys.BookmarkListCache = cache_memory.New[GenericList[string, EventRef]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10003, kind_10003, parseEventRef, sys.BookmarkListCache) return ml } -func (sys *System) FetchPinList(ctx context.Context, pubkey string) GenericList[EventRef] { +func (sys *System) FetchPinList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, EventRef] { if sys.PinListCache == nil { - sys.PinListCache = cache_memory.New[GenericList[EventRef]](1000) + sys.PinListCache = cache_memory.New[GenericList[string, EventRef]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10001, kind_10001, parseEventRef, sys.PinListCache) diff --git a/sdk/lists_profile.go b/sdk/lists_profile.go index ad10e66..3da5650 100644 --- a/sdk/lists_profile.go +++ b/sdk/lists_profile.go @@ -17,27 +17,27 @@ type ProfileRef struct { func (f ProfileRef) Value() nostr.PubKey { return f.Pubkey } -func (sys *System) FetchFollowList(ctx context.Context, pubkey nostr.PubKey) GenericList[ProfileRef] { +func (sys *System) FetchFollowList(ctx context.Context, pubkey nostr.PubKey) GenericList[nostr.PubKey, ProfileRef] { if sys.FollowListCache == nil { - sys.FollowListCache = cache_memory.New[GenericList[ProfileRef]](1000) + sys.FollowListCache = cache_memory.New[GenericList[nostr.PubKey, ProfileRef]](1000) } fl, _ := fetchGenericList(sys, ctx, pubkey, 3, kind_3, parseProfileRef, sys.FollowListCache) return fl } -func (sys *System) FetchMuteList(ctx context.Context, pubkey nostr.PubKey) GenericList[ProfileRef] { +func (sys *System) FetchMuteList(ctx context.Context, pubkey nostr.PubKey) GenericList[nostr.PubKey, ProfileRef] { if sys.MuteListCache == nil { - sys.MuteListCache = cache_memory.New[GenericList[ProfileRef]](1000) + sys.MuteListCache = cache_memory.New[GenericList[nostr.PubKey, ProfileRef]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10000, kind_10000, parseProfileRef, sys.MuteListCache) return ml } -func (sys *System) FetchFollowSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[ProfileRef] { +func (sys *System) FetchFollowSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[nostr.PubKey, ProfileRef] { if sys.FollowSetsCache == nil { - sys.FollowSetsCache = cache_memory.New[GenericSets[ProfileRef]](1000) + sys.FollowSetsCache = cache_memory.New[GenericSets[nostr.PubKey, ProfileRef]](1000) } ml, _ := fetchGenericSets(sys, ctx, pubkey, 30000, kind_30000, parseProfileRef, sys.FollowSetsCache) @@ -52,11 +52,10 @@ func parseProfileRef(tag nostr.Tag) (fw ProfileRef, ok bool) { return fw, false } - pubkey, err := nostr.PubKeyFromHex(fw.Pubkey) + pubkey, err := nostr.PubKeyFromHex(tag[1]) if err != nil { return fw, false } - fw.Pubkey = pubkey if len(tag) > 2 { diff --git a/sdk/lists_relay.go b/sdk/lists_relay.go index 68fd70f..be00849 100644 --- a/sdk/lists_relay.go +++ b/sdk/lists_relay.go @@ -19,32 +19,32 @@ type RelayURL string func (r RelayURL) Value() string { return string(r) } -func (sys *System) FetchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[Relay] { +func (sys *System) FetchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, Relay] { ml, _ := fetchGenericList(sys, ctx, pubkey, 10002, kind_10002, parseRelayFromKind10002, sys.RelayListCache) return ml } -func (sys *System) FetchBlockedRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[RelayURL] { +func (sys *System) FetchBlockedRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, RelayURL] { if sys.BlockedRelayListCache == nil { - sys.BlockedRelayListCache = cache_memory.New[GenericList[RelayURL]](1000) + sys.BlockedRelayListCache = cache_memory.New[GenericList[string, RelayURL]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10006, kind_10006, parseRelayURL, sys.BlockedRelayListCache) return ml } -func (sys *System) FetchSearchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[RelayURL] { +func (sys *System) FetchSearchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, RelayURL] { if sys.SearchRelayListCache == nil { - sys.SearchRelayListCache = cache_memory.New[GenericList[RelayURL]](1000) + sys.SearchRelayListCache = cache_memory.New[GenericList[string, RelayURL]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10007, kind_10007, parseRelayURL, sys.SearchRelayListCache) return ml } -func (sys *System) FetchRelaySets(ctx context.Context, pubkey nostr.PubKey) GenericSets[RelayURL] { +func (sys *System) FetchRelaySets(ctx context.Context, pubkey nostr.PubKey) GenericSets[string, RelayURL] { if sys.RelaySetsCache == nil { - sys.RelaySetsCache = cache_memory.New[GenericSets[RelayURL]](1000) + sys.RelaySetsCache = cache_memory.New[GenericSets[string, RelayURL]](1000) } ml, _ := fetchGenericSets(sys, ctx, pubkey, 30002, kind_30002, parseRelayURL, sys.RelaySetsCache) diff --git a/sdk/lists_topics.go b/sdk/lists_topics.go index 16ffd69..f72135e 100644 --- a/sdk/lists_topics.go +++ b/sdk/lists_topics.go @@ -11,18 +11,18 @@ type Topic string func (r Topic) Value() string { return string(r) } -func (sys *System) FetchTopicList(ctx context.Context, pubkey nostr.PubKey) GenericList[Topic] { +func (sys *System) FetchTopicList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, Topic] { if sys.TopicListCache == nil { - sys.TopicListCache = cache_memory.New[GenericList[Topic]](1000) + sys.TopicListCache = cache_memory.New[GenericList[string, Topic]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10015, kind_10015, parseTopicString, sys.TopicListCache) return ml } -func (sys *System) FetchTopicSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[Topic] { +func (sys *System) FetchTopicSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[string, Topic] { if sys.TopicSetsCache == nil { - sys.TopicSetsCache = cache_memory.New[GenericSets[Topic]](1000) + sys.TopicSetsCache = cache_memory.New[GenericSets[string, Topic]](1000) } ml, _ := fetchGenericSets(sys, ctx, pubkey, 30015, kind_30015, parseTopicString, sys.TopicSetsCache) diff --git a/sdk/metadata.go b/sdk/metadata.go index 8190729..0f5c542 100644 --- a/sdk/metadata.go +++ b/sdk/metadata.go @@ -110,12 +110,11 @@ func (sys *System) FetchProfileMetadata(ctx context.Context, pubkey nostr.PubKey pm.PubKey = pubkey - res, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []int{0}, Authors: []string{pubkey}}) - if len(res) != 0 { + for evt := range sys.Store.QueryEvents(nostr.Filter{Kinds: []uint16{0}, Authors: []nostr.PubKey{pubkey}}) { // ok, we found something locally - pm, _ = ParseMetadata(res[0]) + pm, _ = ParseMetadata(evt) pm.PubKey = pubkey - pm.Event = res[0] + pm.Event = &evt // but if we haven't tried fetching from the network recently we should do it lastFetchKey := makeLastFetchKey(0, pubkey) @@ -151,7 +150,7 @@ func (sys *System) FetchProfileMetadata(ctx context.Context, pubkey nostr.PubKey return pm } -func (sys *System) tryFetchMetadataFromNetwork(ctx context.Context, pubkey string) *ProfileMetadata { +func (sys *System) tryFetchMetadataFromNetwork(ctx context.Context, pubkey nostr.PubKey) *ProfileMetadata { evt, err := sys.replaceableLoaders[kind_0].Load(ctx, pubkey) if err != nil { return nil @@ -163,15 +162,15 @@ func (sys *System) tryFetchMetadataFromNetwork(ctx context.Context, pubkey strin } pm.PubKey = pubkey - pm.Event = evt - sys.StoreRelay.Publish(ctx, *evt) + pm.Event = &evt + sys.Publisher.Publish(ctx, evt) sys.MetadataCache.SetWithTTL(pubkey, pm, time.Hour*6) return &pm } // ParseMetadata parses a kind 0 event into a ProfileMetadata struct. // Returns an error if the event is not kind 0 or if the content is not valid JSON. -func ParseMetadata(event *nostr.Event) (meta ProfileMetadata, err error) { +func ParseMetadata(event nostr.Event) (meta ProfileMetadata, err error) { if event.Kind != 0 { err = fmt.Errorf("event %s is kind %d, not 0", event.ID, event.Kind) } else if er := json.Unmarshal([]byte(event.Content), &meta); er != nil { @@ -183,6 +182,6 @@ func ParseMetadata(event *nostr.Event) (meta ProfileMetadata, err error) { } meta.PubKey = event.PubKey - meta.Event = event + meta.Event = &event return meta, err } diff --git a/sdk/outbox.go b/sdk/outbox.go index 8d6e498..933db45 100644 --- a/sdk/outbox.go +++ b/sdk/outbox.go @@ -49,7 +49,7 @@ func (sys *System) FetchOutboxRelays(ctx context.Context, pubkey nostr.PubKey, n // FetchWriteRelays just reads relays from a kind:10002, that's the only canonical place where a user reveals // the relays they intend to receive notifications from. -func (sys *System) FetchInboxRelays(ctx context.Context, pubkey string, n int) []string { +func (sys *System) FetchInboxRelays(ctx context.Context, pubkey nostr.PubKey, n int) []string { rl := sys.FetchRelayList(ctx, pubkey) if len(rl.Items) == 0 || len(rl.Items) > 10 { return []string{"wss://relay.damus.io", "wss://nos.lol"} @@ -70,7 +70,7 @@ func (sys *System) FetchInboxRelays(ctx context.Context, pubkey string, n int) [ // // Use FetchWriteRelays when deciding where to publish on behalf of a user, but FetchOutboxRelays when deciding // from where to read notes authored by other users. -func (sys *System) FetchWriteRelays(ctx context.Context, pubkey string) []string { +func (sys *System) FetchWriteRelays(ctx context.Context, pubkey nostr.PubKey) []string { rl := sys.FetchRelayList(ctx, pubkey) relays := make([]string, 0, 7) diff --git a/sdk/replaceable_loader.go b/sdk/replaceable_loader.go index 9bd8129..9144b75 100644 --- a/sdk/replaceable_loader.go +++ b/sdk/replaceable_loader.go @@ -33,7 +33,7 @@ const ( type EventResult dataloader.Result[*nostr.Event] func (sys *System) initializeReplaceableDataloaders() { - sys.replaceableLoaders = make([]*dataloader.Loader[nostr.PubKey, *nostr.Event], 12) + sys.replaceableLoaders = make([]*dataloader.Loader[nostr.PubKey, nostr.Event], 12) sys.replaceableLoaders[kind_0] = sys.createReplaceableDataloader(0) sys.replaceableLoaders[kind_3] = sys.createReplaceableDataloader(3) sys.replaceableLoaders[kind_10000] = sys.createReplaceableDataloader(10000) @@ -48,9 +48,9 @@ func (sys *System) initializeReplaceableDataloaders() { sys.replaceableLoaders[kind_10030] = sys.createReplaceableDataloader(10030) } -func (sys *System) createReplaceableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, *nostr.Event] { +func (sys *System) createReplaceableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, nostr.Event] { return dataloader.NewBatchedLoader( - func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[*nostr.Event] { + func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[nostr.Event] { return sys.batchLoadReplaceableEvents(ctxs, kind, pubkeys) }, dataloader.Options{ @@ -64,9 +64,9 @@ func (sys *System) batchLoadReplaceableEvents( ctxs []context.Context, kind uint16, pubkeys []nostr.PubKey, -) map[nostr.PubKey]dataloader.Result[*nostr.Event] { +) map[nostr.PubKey]dataloader.Result[nostr.Event] { batchSize := len(pubkeys) - results := make(map[nostr.PubKey]dataloader.Result[*nostr.Event], batchSize) + results := make(map[nostr.PubKey]dataloader.Result[nostr.Event], batchSize) relayFilter := make([]nostr.DirectedFilter, 0, max(3, batchSize*2)) relayFilterIndex := make(map[string]int, max(3, batchSize*2)) @@ -121,9 +121,9 @@ func (sys *System) batchLoadReplaceableEvents( // query all relays with the prepared filters wg.Wait() - multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter, - nostr.WithLabel("repl~"+strconv.Itoa(int(kind))), - ) + multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter, nostr.SubscriptionOptions{ + Label: "repl~" + strconv.Itoa(int(kind)), + }) for { select { case ie, more := <-multiSubs: @@ -132,8 +132,8 @@ func (sys *System) batchLoadReplaceableEvents( } // insert this event at the desired position - if val, ok := results[ie.PubKey]; !ok || val.Data == nil || val.Data.CreatedAt < ie.CreatedAt { - results[ie.PubKey] = dataloader.Result[*nostr.Event]{Data: ie.Event} + if val, ok := results[ie.PubKey]; !ok || val.Data.ID == nostr.ZeroID || val.Data.CreatedAt < ie.CreatedAt { + results[ie.PubKey] = dataloader.Result[nostr.Event]{Data: ie.Event} } case <-aggregatedContext.Done(): return results diff --git a/sdk/search.go b/sdk/search.go index ef09435..86af040 100644 --- a/sdk/search.go +++ b/sdk/search.go @@ -13,7 +13,9 @@ func (sys *System) SearchUsers(ctx context.Context, query string) []ProfileMetad for ie := range sys.Pool.FetchMany(ctx, sys.UserSearchRelays.URLs, nostr.Filter{ Search: query, Limit: limit, - }, nostr.WithLabel("search")) { + }, nostr.SubscriptionOptions{ + Label: "search", + }) { m, _ := ParseMetadata(ie.Event) profiles = append(profiles, m) } diff --git a/sdk/set.go b/sdk/set.go index b401f4d..692e449 100644 --- a/sdk/set.go +++ b/sdk/set.go @@ -11,22 +11,22 @@ import ( // this is similar to list.go and inherits code from that. -type GenericSets[I TagItemWithValue] struct { - PubKey nostr.PubKey `json:"-"` - Events []*nostr.Event `json:"-"` +type GenericSets[V comparable, I TagItemWithValue[V]] struct { + PubKey nostr.PubKey `json:"-"` + Events []nostr.Event `json:"-"` Sets map[string][]I } -func fetchGenericSets[I TagItemWithValue]( +func fetchGenericSets[V comparable, I TagItemWithValue[V]]( sys *System, ctx context.Context, pubkey nostr.PubKey, actualKind uint16, addressableIndex addressableIndex, parseTag func(nostr.Tag) (I, bool), - cache cache.Cache32[GenericSets[I]], -) (fl GenericSets[I], fromInternal bool) { + cache cache.Cache32[GenericSets[V, I]], +) (fl GenericSets[V, I], fromInternal bool) { n := pubkey[7] lockIdx := (uint16(n) + actualKind) % 60 genericListMutexes[lockIdx].Lock() @@ -44,9 +44,11 @@ func fetchGenericSets[I TagItemWithValue]( return v, true } - v := GenericSets[I]{PubKey: pubkey} + v := GenericSets[V, I]{PubKey: pubkey} - events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}) + events := slices.Collect( + sys.Store.QueryEvents(nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}), + ) if len(events) != 0 { // ok, we found something locally sets := parseSetsFromEvents(events, parseTag) @@ -89,31 +91,31 @@ func fetchGenericSets[I TagItemWithValue]( return v, false } -func tryFetchSetsFromNetwork[I TagItemWithValue]( +func tryFetchSetsFromNetwork[V comparable, I TagItemWithValue[V]]( ctx context.Context, sys *System, pubkey nostr.PubKey, addressableIndex addressableIndex, parseTag func(nostr.Tag) (I, bool), -) *GenericSets[I] { +) *GenericSets[V, I] { events, err := sys.addressableLoaders[addressableIndex].Load(ctx, pubkey) if err != nil { return nil } - v := &GenericSets[I]{ + v := &GenericSets[V, I]{ PubKey: pubkey, Events: events, Sets: parseSetsFromEvents(events, parseTag), } for _, evt := range events { - sys.StoreRelay.Publish(ctx, *evt) + sys.Publisher.Publish(ctx, evt) } return v } -func parseSetsFromEvents[I TagItemWithValue]( - events []*nostr.Event, +func parseSetsFromEvents[V comparable, I TagItemWithValue[V]]( + events []nostr.Event, parseTag func(nostr.Tag) (I, bool), ) map[string][]I { sets := make(map[string][]I, len(events)) diff --git a/sdk/specific_event.go b/sdk/specific_event.go index 919aecb..c565947 100644 --- a/sdk/specific_event.go +++ b/sdk/specific_event.go @@ -39,13 +39,13 @@ func (sys *System) FetchSpecificEventFromInput( case "naddr": pointer = data.(nostr.EntityPointer) case "note": - pointer = nostr.EventPointer{ID: data.(string)} + pointer = nostr.EventPointer{ID: data.([32]byte)} default: return nil, nil, fmt.Errorf("invalid code '%s'", input) } } else { - if nostr.IsValid32ByteHex(input) { - pointer = nostr.EventPointer{ID: input} + if id, err := nostr.IDFromHex(input); err == nil { + pointer = nostr.EventPointer{ID: id} } else { return nil, nil, fmt.Errorf("failed to decode '%s': %w", input, err) } @@ -66,7 +66,7 @@ func (sys *System) FetchSpecificEvent( priorityRelays := make([]string, 0, 8) var filter nostr.Filter - author := "" + var author nostr.PubKey relays := make([]string, 0, 10) fallback := make([]string, 0, 10) successRelays = make([]string, 0, 10) @@ -74,7 +74,7 @@ func (sys *System) FetchSpecificEvent( switch v := pointer.(type) { case nostr.EventPointer: author = v.Author - filter.IDs = []string{v.ID} + filter.IDs = []nostr.ID{v.ID} relays = append(relays, v.Relays...) relays = appendUnique(relays, sys.FallbackRelays.Next()) fallback = append(fallback, sys.JustIDRelays.URLs...) @@ -82,9 +82,9 @@ func (sys *System) FetchSpecificEvent( priorityRelays = append(priorityRelays, v.Relays...) case nostr.EntityPointer: author = v.PublicKey - filter.Authors = []string{v.PublicKey} + filter.Authors = []nostr.PubKey{v.PublicKey} filter.Tags = nostr.TagMap{"d": []string{v.Identifier}} - filter.Kinds = []int{v.Kind} + filter.Kinds = []uint16{v.Kind} relays = append(relays, v.Relays...) relays = appendUnique(relays, sys.FallbackRelays.Next()) fallback = append(fallback, sys.FallbackRelays.Next(), sys.FallbackRelays.Next()) @@ -93,13 +93,12 @@ func (sys *System) FetchSpecificEvent( // try to fetch in our internal eventstore first if !params.SkipLocalStore { - if res, _ := sys.StoreRelay.QuerySync(ctx, filter); len(res) != 0 { - evt := res[0] - return evt, nil, nil + for evt := range sys.Store.QueryEvents(filter) { + return &evt, nil, nil } } - if author != "" { + if author != nostr.ZeroPK { // fetch relays for author authorRelays := sys.FetchOutboxRelays(ctx, author, 3) @@ -141,19 +140,16 @@ attempts: countdown := 6.0 subManyCtx := ctx - for ie := range sys.Pool.FetchMany( - subManyCtx, - attempt.relays, - filter, - nostr.WithLabel(attempt.label), - ) { + for ie := range sys.Pool.FetchMany(subManyCtx, attempt.relays, filter, nostr.SubscriptionOptions{ + Label: attempt.label, + }) { fetchProfileOnce.Do(func() { go sys.FetchProfileMetadata(ctx, ie.PubKey) }) successRelays = append(successRelays, ie.Relay.URL) if result == nil || ie.CreatedAt > result.CreatedAt { - result = ie.Event + result = &ie.Event } if !attempt.slowWithRelays { @@ -170,7 +166,7 @@ attempts: // save stuff in cache and in internal store if !params.SkipLocalStore { - sys.StoreRelay.Publish(ctx, *result) + sys.Publisher.Publish(ctx, *result) } // put priority relays first so they get used in nevent and nprofile diff --git a/sdk/system.go b/sdk/system.go index b88af83..0f3872f 100644 --- a/sdk/system.go +++ b/sdk/system.go @@ -1,10 +1,11 @@ package sdk import ( - "context" "math/rand/v2" "fiatjaf.com/nostr" + "fiatjaf.com/nostr/eventstore" + "fiatjaf.com/nostr/eventstore/nullstore" "fiatjaf.com/nostr/eventstore/wrappers" "fiatjaf.com/nostr/sdk/cache" cache_memory "fiatjaf.com/nostr/sdk/cache/memory" @@ -13,8 +14,6 @@ import ( "fiatjaf.com/nostr/sdk/hints/memoryh" "fiatjaf.com/nostr/sdk/kvstore" kvstore_memory "fiatjaf.com/nostr/sdk/kvstore/memory" - "fiatjaf.com/nostr/eventstore" - "fiatjaf.com/nostr/eventstore/nullstore" ) // System represents the core functionality of the SDK, providing access to @@ -30,17 +29,17 @@ import ( type System struct { KVStore kvstore.KVStore MetadataCache cache.Cache32[ProfileMetadata] - RelayListCache cache.Cache32[GenericList[Relay]] - FollowListCache cache.Cache32[GenericList[ProfileRef]] - MuteListCache cache.Cache32[GenericList[ProfileRef]] - BookmarkListCache cache.Cache32[GenericList[EventRef]] - PinListCache cache.Cache32[GenericList[EventRef]] - BlockedRelayListCache cache.Cache32[GenericList[RelayURL]] - SearchRelayListCache cache.Cache32[GenericList[RelayURL]] - TopicListCache cache.Cache32[GenericList[Topic]] - RelaySetsCache cache.Cache32[GenericSets[RelayURL]] - FollowSetsCache cache.Cache32[GenericSets[ProfileRef]] - TopicSetsCache cache.Cache32[GenericSets[Topic]] + RelayListCache cache.Cache32[GenericList[string, Relay]] + FollowListCache cache.Cache32[GenericList[nostr.PubKey, ProfileRef]] + MuteListCache cache.Cache32[GenericList[nostr.PubKey, ProfileRef]] + BookmarkListCache cache.Cache32[GenericList[string, EventRef]] + PinListCache cache.Cache32[GenericList[string, EventRef]] + BlockedRelayListCache cache.Cache32[GenericList[string, RelayURL]] + SearchRelayListCache cache.Cache32[GenericList[string, RelayURL]] + TopicListCache cache.Cache32[GenericList[string, Topic]] + RelaySetsCache cache.Cache32[GenericSets[string, RelayURL]] + FollowSetsCache cache.Cache32[GenericSets[nostr.PubKey, ProfileRef]] + TopicSetsCache cache.Cache32[GenericSets[string, Topic]] Hints hints.HintsDB Pool *nostr.Pool RelayListRelays *RelayStream @@ -54,8 +53,8 @@ type System struct { Publisher wrappers.StorePublisher - replaceableLoaders []*dataloader.Loader[nostr.PubKey, *nostr.Event] - addressableLoaders []*dataloader.Loader[nostr.PubKey, []*nostr.Event] + replaceableLoaders []*dataloader.Loader[nostr.PubKey, nostr.Event] + addressableLoaders []*dataloader.Loader[nostr.PubKey, []nostr.Event] } // SystemModifier is a function that modifies a System instance. @@ -119,12 +118,12 @@ func NewSystem(mods ...SystemModifier) *System { Hints: memoryh.NewHintDB(), } - sys.Pool = nostr.NewPool(context.Background(), - nostr.WithAuthorKindQueryMiddleware(sys.TrackQueryAttempts), - nostr.WithEventMiddleware(sys.TrackEventHintsAndRelays), - nostr.WithDuplicateMiddleware(sys.TrackEventRelaysD), - nostr.WithPenaltyBox(), - ) + sys.Pool = nostr.NewPool(nostr.PoolOptions{ + AuthorKindQueryMiddleware: sys.TrackQueryAttempts, + EventMiddleware: sys.TrackEventHintsAndRelays, + DuplicateMiddleware: sys.TrackEventRelaysD, + PenaltyBox: true, + }) for _, mod := range mods { mod(sys) @@ -134,14 +133,14 @@ func NewSystem(mods ...SystemModifier) *System { sys.MetadataCache = cache_memory.New[ProfileMetadata](8000) } if sys.RelayListCache == nil { - sys.RelayListCache = cache_memory.New[GenericList[Relay]](8000) + sys.RelayListCache = cache_memory.New[GenericList[string, Relay]](8000) } if sys.Store == nil { sys.Store = &nullstore.NullStore{} sys.Store.Init() } - sys.StoreRelay = eventstore.RelayWrapper{Store: sys.Store} + sys.Publisher = wrappers.StorePublisher{Store: sys.Store} sys.initializeReplaceableDataloaders() sys.initializeAddressableDataloaders() @@ -223,14 +222,14 @@ func WithStore(store eventstore.Store) SystemModifier { } // WithRelayListCache returns a SystemModifier that sets the RelayListCache. -func WithRelayListCache(cache cache.Cache32[GenericList[Relay]]) SystemModifier { +func WithRelayListCache(cache cache.Cache32[GenericList[string, Relay]]) SystemModifier { return func(sys *System) { sys.RelayListCache = cache } } // WithFollowListCache returns a SystemModifier that sets the FollowListCache. -func WithFollowListCache(cache cache.Cache32[GenericList[ProfileRef]]) SystemModifier { +func WithFollowListCache(cache cache.Cache32[GenericList[nostr.PubKey, ProfileRef]]) SystemModifier { return func(sys *System) { sys.FollowListCache = cache }