From b4268d649c48d8505f96b6d87dd6529daccff3e6 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Mon, 14 Apr 2025 17:31:23 -0300 Subject: [PATCH] a bunch of [32]byte conversions. still more needed. --- README.md | 12 +- connection_options.go | 2 +- count_test.go | 2 +- envelopes.go | 40 ++-- envelopes_sonic.go | 70 +++--- envelopes_test.go | 2 +- eose_test.go | 2 +- event.go | 51 ++-- event_easyjson.go | 28 ++- event_test.go | 12 +- example/example.go | 4 +- filter.go | 51 ++-- filter_easyjson.go | 34 +-- filter_test.go | 44 ++-- go.mod | 2 +- helpers.go | 59 ++++- keyer/bunker.go | 4 +- keyer/encrypted.go | 6 +- keyer/lib.go | 10 +- keyer/manual.go | 2 +- keyer/plain.go | 4 +- keys.go | 41 +--- kinds.go | 272 +++++++++++----------- nip04/nip04_test.go | 2 +- nip05/nip05.go | 2 +- nip10/nip10.go | 2 +- nip11/fetch.go | 2 +- nip13/nip13.go | 2 +- nip13/nip13_test.go | 2 +- nip14/subject.go | 2 +- nip17/nip17.go | 4 +- nip19/nip19.go | 2 +- nip19/nip19_test.go | 2 +- nip22/nip22.go | 4 +- nip29/group.go | 2 +- nip29/nip29.go | 2 +- nip31/nip31.go | 2 +- nip34/patch.go | 2 +- nip34/repository.go | 2 +- nip34/state.go | 2 +- nip40/nip40.go | 2 +- nip42/nip42.go | 2 +- nip44/nip44_test.go | 2 +- nip45/hll_event.go | 2 +- nip45/hll_filter.go | 2 +- nip46/bunker_session.go | 6 +- nip46/client.go | 6 +- nip46/dynamic-signer.go | 6 +- nip46/nip46.go | 2 +- nip46/static-key-signer.go | 6 +- nip46/wellknownnostrjson.go | 2 +- nip52/calendar_event.go | 2 +- nip53/nip53.go | 2 +- nip59/nip59.go | 4 +- nip60/history.go | 2 +- nip60/lightning-swap.go | 2 +- nip60/pay.go | 4 +- nip60/receive.go | 4 +- nip60/send-external.go | 2 +- nip60/send.go | 4 +- nip60/swap.go | 2 +- nip60/token.go | 2 +- nip60/wallet.go | 2 +- nip60/wallet_test.go | 4 +- nip61/info.go | 2 +- nip61/nip61.go | 4 +- nip73/pointer.go | 2 +- nip77/envelopes.go | 2 +- nip77/example/example.go | 4 +- nip77/negentropy/encoding.go | 2 +- nip77/negentropy/fuzz_test.go | 6 +- nip77/negentropy/negentropy.go | 2 +- nip77/negentropy/storage/accumulator.go | 2 +- nip77/negentropy/storage/vector/vector.go | 6 +- nip77/negentropy/types.go | 2 +- nip77/negentropy/whatever_test.go | 6 +- nip77/nip77.go | 6 +- nip86/methods.go | 2 +- nip92/imeta.go | 2 +- nip92/imeta_test.go | 2 +- nip94/nip94.go | 2 +- nip96/nip96.go | 2 +- nip96/nip96_test.go | 2 +- nip96/types.go | 2 +- pointers.go | 67 +++--- pool.go | 103 ++++---- relay.go | 7 +- relay_js_test.go | 5 +- relay_test.go | 7 +- sdk/addressable_loader.go | 24 +- sdk/cache/interface.go | 8 +- sdk/cache/memory/cache.go | 38 +-- sdk/event_relays.go | 29 +-- sdk/feeds.go | 4 +- sdk/feeds_test.go | 8 +- sdk/hints/badgerh/db.go | 18 +- sdk/hints/badgerh/keys.go | 11 +- sdk/hints/interface.go | 10 +- sdk/hints/keys.go | 2 +- sdk/hints/memoryh/db.go | 4 +- sdk/hints/sqlh/db.go | 4 +- sdk/hints/test/libsql_test.go | 2 +- sdk/hints/test/mattnsqlite_test.go | 2 +- sdk/hints/test/memory_test.go | 2 +- sdk/hints/test/moderncsqlite_test.go | 2 +- sdk/hints/test/suite.go | 4 +- sdk/hints/test/wasmsqlite_test.go | 2 +- sdk/input.go | 6 +- sdk/kvstore/badger/store.go | 2 +- sdk/kvstore/lmdb/store.go | 2 +- sdk/kvstore/memory/store.go | 2 +- sdk/list.go | 23 +- sdk/lists_event.go | 38 +-- sdk/lists_profile.go | 26 ++- sdk/lists_relay.go | 30 ++- sdk/lists_topics.go | 18 +- sdk/loader_helpers.go | 7 +- sdk/metadata.go | 8 +- sdk/outbox.go | 9 +- sdk/replaceable_loader.go | 28 +-- sdk/sdk_test.go | 2 +- sdk/search.go | 2 +- sdk/set.go | 19 +- sdk/specific_event.go | 6 +- sdk/system.go | 24 +- sdk/tracker.go | 20 +- signature.go | 34 +-- signature_libsecp256k1.go | 37 +-- subscription.go | 20 +- subscription_test.go | 6 +- types.go | 68 ++++++ utils.go | 10 +- 132 files changed, 857 insertions(+), 879 deletions(-) create mode 100644 types.go diff --git a/README.md b/README.md index 4c7eb1f..df2b90d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Run Tests](https://github.com/nbd-wtf/go-nostr/actions/workflows/test.yml/badge.svg)](https://github.com/nbd-wtf/go-nostr/actions/workflows/test.yml) -[![Go Reference](https://pkg.go.dev/badge/github.com/nbd-wtf/go-nostr.svg)](https://pkg.go.dev/github.com/nbd-wtf/go-nostr) -[![Go Report Card](https://goreportcard.com/badge/github.com/nbd-wtf/go-nostr)](https://goreportcard.com/report/github.com/nbd-wtf/go-nostr) +[![Run Tests](https://fiatjaf.com/nostrlib/actions/workflows/test.yml/badge.svg)](https://fiatjaf.com/nostrlib/actions/workflows/test.yml) +[![Go Reference](https://pkg.go.dev/badge/fiatjaf.com/nostrlib.svg)](https://pkg.go.dev/fiatjaf.com/nostrlib) +[![Go Report Card](https://goreportcard.com/badge/fiatjaf.com/nostrlib)](https://goreportcard.com/report/fiatjaf.com/nostrlib) @@ -10,7 +10,7 @@ go-nostr A set of useful things for [Nostr](https://github.com/nostr-protocol/nostr)-related software. ```bash -go get github.com/nbd-wtf/go-nostr +go get fiatjaf.com/nostrlib ``` ### Generating a key @@ -21,8 +21,8 @@ package main import ( "fmt" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip19" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip19" ) func main() { diff --git a/connection_options.go b/connection_options.go index 69a8bf9..ada5885 100644 --- a/connection_options.go +++ b/connection_options.go @@ -13,7 +13,7 @@ import ( var defaultConnectionOptions = &ws.DialOptions{ CompressionMode: ws.CompressionContextTakeover, HTTPHeader: http.Header{ - textproto.CanonicalMIMEHeaderKey("User-Agent"): {"github.com/nbd-wtf/go-nostr"}, + textproto.CanonicalMIMEHeaderKey("User-Agent"): {"fiatjaf.com/nostrlib"}, }, } diff --git a/count_test.go b/count_test.go index dbcc4f9..d94d3fa 100644 --- a/count_test.go +++ b/count_test.go @@ -14,7 +14,7 @@ func TestCount(t *testing.T) { defer rl.Close() count, _, err := rl.Count(context.Background(), Filters{ - {Kinds: []int{KindFollowList}, Tags: TagMap{"p": []string{"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"}}}, + {Kinds: []uint16{KindFollowList}, Tags: TagMap{"p": []string{"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"}}}, }) assert.NoError(t, err) assert.Greater(t, count, int64(0)) diff --git a/envelopes.go b/envelopes.go index 3018862..725952a 100644 --- a/envelopes.go +++ b/envelopes.go @@ -124,7 +124,7 @@ func (v EventEnvelope) MarshalJSON() ([]byte, error) { // ReqEnvelope represents a REQ message. type ReqEnvelope struct { SubscriptionID string - Filters + Filter } func (_ ReqEnvelope) Label() string { return "REQ" } @@ -136,13 +136,8 @@ func (v *ReqEnvelope) FromJSON(data string) error { return fmt.Errorf("failed to decode REQ envelope: missing filters") } v.SubscriptionID = string(unsafe.Slice(unsafe.StringData(arr[1].Str), len(arr[1].Str))) - v.Filters = make(Filters, len(arr)-2) - f := 0 - for i := 2; i < len(arr); i++ { - if err := easyjson.Unmarshal(unsafe.Slice(unsafe.StringData(arr[i].Raw), len(arr[i].Raw)), &v.Filters[f]); err != nil { - return fmt.Errorf("%w -- on filter %d", err, f) - } - f++ + if err := easyjson.Unmarshal(unsafe.Slice(unsafe.StringData(arr[2].Raw), len(arr[2].Raw)), &v.Filter); err != nil { + return fmt.Errorf("on filter: %w", err) } return nil @@ -152,11 +147,8 @@ func (v ReqEnvelope) MarshalJSON() ([]byte, error) { w := jwriter.Writer{NoEscapeHTML: true} w.RawString(`["REQ","`) w.RawString(v.SubscriptionID) - w.RawString(`"`) - for _, filter := range v.Filters { - w.RawString(`,`) - filter.MarshalEasyJSON(&w) - } + w.RawString(`",`) + v.Filter.MarshalEasyJSON(&w) w.RawString(`]`) return w.BuildBytes() } @@ -198,15 +190,9 @@ func (v *CountEnvelope) FromJSON(data string) error { return nil } - f := 0 - for i := 2; i < len(arr); i++ { - item := unsafe.Slice(unsafe.StringData(arr[i].Raw), len(arr[i].Raw)) - - if err := easyjson.Unmarshal(item, &v.Filter); err != nil { - return fmt.Errorf("%w -- on filter %d", err, f) - } - - f++ + item := unsafe.Slice(unsafe.StringData(arr[2].Raw), len(arr[2].Raw)) + if err := easyjson.Unmarshal(item, &v.Filter); err != nil { + return fmt.Errorf("on filter: %w", err) } return nil @@ -216,7 +202,7 @@ func (v CountEnvelope) MarshalJSON() ([]byte, error) { w := jwriter.Writer{NoEscapeHTML: true} w.RawString(`["COUNT","`) w.RawString(v.SubscriptionID) - w.RawString(`",`) + w.RawString(`"`) if v.Count != nil { w.RawString(`{"count":`) w.RawString(strconv.FormatInt(*v.Count, 10)) @@ -357,7 +343,7 @@ func (v ClosedEnvelope) MarshalJSON() ([]byte, error) { // OKEnvelope represents an OK message. type OKEnvelope struct { - EventID string + EventID ID OK bool Reason string } @@ -374,7 +360,9 @@ func (v *OKEnvelope) FromJSON(data string) error { if len(arr) < 4 { return fmt.Errorf("failed to decode OK envelope: missing fields") } - v.EventID = string(unsafe.Slice(unsafe.StringData(arr[1].Str), len(arr[1].Str))) + if _, err := hex.Decode(v.EventID[:], []byte(arr[1].Str)); err != nil { + return err + } v.OK = arr[2].Raw == "true" v.Reason = string(unsafe.Slice(unsafe.StringData(arr[3].Str), len(arr[3].Str))) @@ -384,7 +372,7 @@ func (v *OKEnvelope) FromJSON(data string) error { func (v OKEnvelope) MarshalJSON() ([]byte, error) { w := jwriter.Writer{NoEscapeHTML: true} w.RawString(`["OK","`) - w.RawString(v.EventID) + w.RawString(hex.EncodeToString(v.EventID[:])) w.RawString(`",`) ok := "false" if v.OK { diff --git a/envelopes_sonic.go b/envelopes_sonic.go index 4cabcbc..a38cfb0 100644 --- a/envelopes_sonic.go +++ b/envelopes_sonic.go @@ -165,7 +165,6 @@ func (sv *sonicVisitor) OnArrayEnd() error { sv.mainEnvelope = sv.event case inReq: sv.mainEnvelope = sv.req - sv.smp.doneWithFilterSlice(sv.req.Filters) case inOk: sv.mainEnvelope = sv.ok case inEose: @@ -184,13 +183,13 @@ func (sv *sonicVisitor) OnArrayEnd() error { // filter object properties case inIds: sv.whereWeAre = inFilterObject - sv.smp.doneWithStringSlice(sv.currentFilter.IDs) + sv.smp.doneWithIDSlice(sv.currentFilter.IDs) case inAuthors: sv.whereWeAre = inFilterObject - sv.smp.doneWithStringSlice(sv.currentFilter.Authors) + sv.smp.doneWithPubKeySlice(sv.currentFilter.Authors) case inKinds: sv.whereWeAre = inFilterObject - sv.smp.doneWithIntSlice(sv.currentFilter.Kinds) + sv.smp.doneWithUint16Slice(sv.currentFilter.Kinds) case inAFilterTag: sv.currentFilter.Tags[sv.currentFilterTagName] = sv.currentFilterTagList sv.whereWeAre = inFilterObject @@ -268,13 +267,13 @@ func (sv *sonicVisitor) OnObjectKey(key string) error { sv.whereWeAre = inUntil case "ids": sv.whereWeAre = inIds - sv.currentFilter.IDs = sv.smp.reusableStringArray + sv.currentFilter.IDs = sv.smp.reusableIDArray case "authors": sv.whereWeAre = inAuthors - sv.currentFilter.Authors = sv.smp.reusableStringArray + sv.currentFilter.Authors = sv.smp.reusablePubKeyArray case "kinds": sv.whereWeAre = inKinds - sv.currentFilter.Kinds = sv.smp.reusableIntArray + sv.currentFilter.Kinds = sv.smp.reusableUint16Array case "search": sv.whereWeAre = inSearch case "count", "hll": @@ -316,7 +315,7 @@ func (sv *sonicVisitor) OnObjectEnd() error { sv.currentEvent = nil case inFilterObject: if sv.req != nil { - sv.req.Filters = append(sv.req.Filters, *sv.currentFilter) + sv.req.Filter = *sv.currentFilter sv.whereWeAre = inReq } else { sv.count.Filter = *sv.currentFilter @@ -335,6 +334,7 @@ func (sv *sonicVisitor) OnObjectEnd() error { func (sv *sonicVisitor) OnString(v string) error { // fmt.Println("***", "OnString", v, "==", sv.whereWeAre) + var err error switch sv.whereWeAre { case inEnvelope: switch v { @@ -342,7 +342,7 @@ func (sv *sonicVisitor) OnString(v string) error { sv.event = &EventEnvelope{} sv.whereWeAre = inEvent case "REQ": - sv.req = &ReqEnvelope{Filters: sv.smp.reusableFilterArray} + sv.req = &ReqEnvelope{} sv.whereWeAre = inReq case "OK": sv.ok = &OKEnvelope{} @@ -372,8 +372,11 @@ func (sv *sonicVisitor) OnString(v string) error { case inReq: sv.req.SubscriptionID = v case inOk: - if sv.ok.EventID == "" { - sv.ok.EventID = v + if sv.ok.EventID == [32]byte{} { + sv.ok.EventID, err = IDFromHex(v) + if err != nil { + return err + } } else { sv.ok.Reason = v } @@ -396,9 +399,17 @@ func (sv *sonicVisitor) OnString(v string) error { // filter object properties case inIds: - sv.currentFilter.IDs = append(sv.currentFilter.IDs, v) + id, err := IDFromHex(v) + if err != nil { + return err + } + sv.currentFilter.IDs = append(sv.currentFilter.IDs, id) case inAuthors: - sv.currentFilter.Authors = append(sv.currentFilter.Authors, v) + pk, err := PubKeyFromHex(v) + if err != nil { + return err + } + sv.currentFilter.Authors = append(sv.currentFilter.Authors, pk) case inSearch: sv.currentFilter.Search = v sv.whereWeAre = inFilterObject @@ -484,36 +495,27 @@ func (_ sonicVisitor) OnFloat64(v float64, n stdlibjson.Number) error { } type sonicMessageParser struct { - reusableFilterArray []Filter reusableTagArray []Tag + reusableIDArray []ID + reusablePubKeyArray []PubKey reusableStringArray []string - reusableIntArray []int + reusableUint16Array []uint16 } // NewMessageParser returns a sonicMessageParser object that is intended to be reused many times. // It is not goroutine-safe. func NewMessageParser() sonicMessageParser { return sonicMessageParser{ - reusableFilterArray: make([]Filter, 0, 1000), reusableTagArray: make([]Tag, 0, 10000), reusableStringArray: make([]string, 0, 10000), - reusableIntArray: make([]int, 0, 10000), + reusableIDArray: make([]ID, 0, 10000), + reusablePubKeyArray: make([]PubKey, 0, 10000), + reusableUint16Array: make([]uint16, 0, 10000), } } var NewSonicMessageParser = NewMessageParser -func (smp *sonicMessageParser) doneWithFilterSlice(slice []Filter) { - if unsafe.SliceData(smp.reusableFilterArray) == unsafe.SliceData(slice) { - smp.reusableFilterArray = slice[len(slice):] - } - - if cap(smp.reusableFilterArray) < 7 { - // create a new one - smp.reusableFilterArray = make([]Filter, 0, 1000) - } -} - func (smp *sonicMessageParser) doneWithTagSlice(slice []Tag) { if unsafe.SliceData(smp.reusableTagArray) == unsafe.SliceData(slice) { smp.reusableTagArray = slice[len(slice):] @@ -525,7 +527,7 @@ func (smp *sonicMessageParser) doneWithTagSlice(slice []Tag) { } } -func (smp *sonicMessageParser) doneWithStringSlice(slice []string) { +func (smp *sonicMessageParser) doneWithIDSlice(slice []string) { if unsafe.SliceData(smp.reusableStringArray) == unsafe.SliceData(slice) { smp.reusableStringArray = slice[len(slice):] } @@ -536,14 +538,14 @@ func (smp *sonicMessageParser) doneWithStringSlice(slice []string) { } } -func (smp *sonicMessageParser) doneWithIntSlice(slice []int) { - if unsafe.SliceData(smp.reusableIntArray) == unsafe.SliceData(slice) { - smp.reusableIntArray = slice[len(slice):] +func (smp *sonicMessageParser) doneWithUint16Slice(slice []uint16) { + if unsafe.SliceData(smp.reusableUint16Array) == unsafe.SliceData(slice) { + smp.reusableUint16Array = slice[len(slice):] } - if cap(smp.reusableIntArray) < 8 { + if cap(smp.reusableUint16Array) < 8 { // create a new one - smp.reusableIntArray = make([]int, 0, 10000) + smp.reusableUint16Array = make([]uint16, 0, 10000) } } diff --git a/envelopes_test.go b/envelopes_test.go index 01092a4..ece0fc0 100644 --- a/envelopes_test.go +++ b/envelopes_test.go @@ -99,7 +99,7 @@ func TestParseMessage(t *testing.T) { { Name: "REQ envelope", Message: `["REQ","million", {"kinds": [1]}, {"kinds": [30023 ], "#d": ["buteko", "batuke"]}]`, - ExpectedEnvelope: &ReqEnvelope{SubscriptionID: "million", Filters: Filters{{Kinds: []int{1}}, {Kinds: []int{30023}, Tags: TagMap{"d": []string{"buteko", "batuke"}}}}}, + ExpectedEnvelope: &ReqEnvelope{SubscriptionID: "million", Filters: Filters{{Kinds: []uint16{1}}, {Kinds: []uint16{30023}, Tags: TagMap{"d": []string{"buteko", "batuke"}}}}}, }, { Name: "CLOSE envelope", diff --git a/eose_test.go b/eose_test.go index 74d32df..788c356 100644 --- a/eose_test.go +++ b/eose_test.go @@ -13,7 +13,7 @@ func TestEOSEMadness(t *testing.T) { defer rl.Close() sub, err := rl.Subscribe(context.Background(), Filters{ - {Kinds: []int{KindTextNote}, Limit: 2}, + {Kinds: []uint16{KindTextNote}, Limit: 2}, }) assert.NoError(t, err) diff --git a/event.go b/event.go index 499b809..c7144e2 100644 --- a/event.go +++ b/event.go @@ -10,13 +10,13 @@ import ( // Event represents a Nostr event. type Event struct { - ID string - PubKey string + ID ID + PubKey PubKey CreatedAt Timestamp - Kind int + Kind uint16 Tags Tags Content string - Sig string + Sig [64]byte } func (evt Event) String() string { @@ -24,37 +24,14 @@ func (evt Event) String() string { return string(j) } -// GetID computes the event ID and returns it as a hex string. -func (evt *Event) GetID() string { - h := sha256.Sum256(evt.Serialize()) - return hex.EncodeToString(h[:]) +// GetID serializes and returns the event ID as a string. +func (evt *Event) GetID() ID { + return sha256.Sum256(evt.Serialize()) } // CheckID checks if the implied ID matches the given ID more efficiently. func (evt *Event) CheckID() bool { - if len(evt.ID) != 64 { - return false - } - - ser := make([]byte, 0, 100+len(evt.Content)+len(evt.Tags)*80) - ser = serializeEventInto(evt, ser) - h := sha256.Sum256(ser) - - const hextable = "0123456789abcdef" - - for i := 0; i < 32; i++ { - b := hextable[h[i]>>4] - if b != evt.ID[i*2] { - return false - } - - b = hextable[h[i]&0x0f] - if b != evt.ID[i*2+1] { - return false - } - } - - return true + return evt.GetID() == evt.ID } // Serialize outputs a byte array that can be hashed to produce the canonical event "id". @@ -68,13 +45,13 @@ func (evt *Event) Serialize() []byte { func serializeEventInto(evt *Event, dst []byte) []byte { // the header portion is easy to serialize // [0,"pubkey",created_at,kind,[ - dst = append(dst, "[0,\""...) - dst = append(dst, evt.PubKey...) - dst = append(dst, "\","...) + dst = append(dst, `[0,"`...) + dst = hex.AppendEncode(dst, evt.PubKey[:]) + dst = append(dst, `",`...) dst = append(dst, strconv.FormatInt(int64(evt.CreatedAt), 10)...) - dst = append(dst, ',') - dst = append(dst, strconv.Itoa(evt.Kind)...) - dst = append(dst, ',') + dst = append(dst, `,`...) + dst = append(dst, strconv.FormatUint(uint64(evt.Kind), 10)...) + dst = append(dst, `,`...) // tags dst = append(dst, '[') diff --git a/event_easyjson.go b/event_easyjson.go index a805222..e6b363a 100644 --- a/event_easyjson.go +++ b/event_easyjson.go @@ -1,6 +1,8 @@ package nostr import ( + "encoding/hex" + easyjson "github.com/mailru/easyjson" jlexer "github.com/mailru/easyjson/jlexer" jwriter "github.com/mailru/easyjson/jwriter" @@ -23,6 +25,7 @@ func easyjsonF642ad3eDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Event) return } in.Delim('{') + var reusableBuffer [64]byte for !in.IsDelim('}') { key := in.UnsafeFieldName(true) in.WantColon() @@ -33,13 +36,15 @@ func easyjsonF642ad3eDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Event) } switch key { case "id": - out.ID = in.String() + hex.Decode(reusableBuffer[:], []byte(in.String())) + copy(out.ID[:], reusableBuffer[0:32]) case "pubkey": - out.PubKey = in.String() + hex.Decode(reusableBuffer[:], []byte(in.String())) + copy(out.PubKey[:], reusableBuffer[0:32]) case "created_at": out.CreatedAt = Timestamp(in.Int64()) case "kind": - out.Kind = in.Int() + out.Kind = uint16(in.Int()) case "tags": if in.IsNull() { in.Skip() @@ -83,7 +88,8 @@ func easyjsonF642ad3eDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Event) case "content": out.Content = in.String() case "sig": - out.Sig = in.String() + hex.Decode(reusableBuffer[:], []byte(in.String())) + copy(out.Sig[:], reusableBuffer[0:64]) } in.WantComma() } @@ -100,20 +106,20 @@ func easyjsonF642ad3eEncodeGithubComNbdWtfGoNostr(out *jwriter.Writer, in Event) { const prefix string = "\"kind\":" out.RawString(prefix) - out.Int(in.Kind) + out.Int(int(in.Kind)) } { - if in.ID != "" { + if in.ID != [32]byte{} { const prefix string = ",\"id\":" out.RawString(prefix) - out.String(in.ID) + out.String(hex.EncodeToString(in.ID[:])) } } { - if in.PubKey != "" { + if in.PubKey != [32]byte{} { const prefix string = ",\"pubkey\":" out.RawString(prefix) - out.String(in.PubKey) + out.String(hex.EncodeToString(in.PubKey[:])) } } { @@ -146,10 +152,10 @@ func easyjsonF642ad3eEncodeGithubComNbdWtfGoNostr(out *jwriter.Writer, in Event) out.String(in.Content) } { - if in.Sig != "" { + if in.Sig != [64]byte{} { const prefix string = ",\"sig\":" out.RawString(prefix) - out.String(in.Sig) + out.String(hex.EncodeToString(in.Sig[:])) } } out.RawByte('}') diff --git a/event_test.go b/event_test.go index 01b882b..0a0e0a5 100644 --- a/event_test.go +++ b/event_test.go @@ -1,6 +1,7 @@ package nostr import ( + "encoding/hex" "fmt" "math/rand/v2" "testing" @@ -34,15 +35,18 @@ func TestEventParsingAndVerifying(t *testing.T) { } func TestEventSerialization(t *testing.T) { + sig := [64]byte{} + hex.Decode(sig[:], []byte("ed08d2dd5b0f7b6a3cdc74643d4adee3158ddede9cc848e8cd97630c097001acc2d052d2d3ec2b7ac4708b2314b797106d1b3c107322e61b5e5cc2116e099b79")) + events := []Event{ { - ID: "92570b321da503eac8014b23447301eb3d0bbdfbace0d11a4e4072e72bb7205d", - PubKey: "e9142f724955c5854de36324dab0434f97b15ec6b33464d56ebe491e3f559d1b", + ID: MustIDFromHex("92570b321da503eac8014b23447301eb3d0bbdfbace0d11a4e4072e72bb7205d"), + PubKey: MustPubKeyFromHex("e9142f724955c5854de36324dab0434f97b15ec6b33464d56ebe491e3f559d1b"), Kind: KindEncryptedDirectMessage, CreatedAt: Timestamp(1671028682), Tags: Tags{Tag{"p", "f8340b2bde651576b75af61aa26c80e13c65029f00f7f64004eece679bf7059f"}}, Content: "you say yes, I say no", - Sig: "ed08d2dd5b0f7b6a3cdc74643d4adee3158ddede9cc848e8cd97630c097001acc2d052d2d3ec2b7ac4708b2314b797106d1b3c107322e61b5e5cc2116e099b79", + Sig: sig, }, } @@ -73,7 +77,7 @@ func TestEventSerialization(t *testing.T) { } } -func mustSignEvent(t *testing.T, privkey string, event *Event) { +func mustSignEvent(t *testing.T, privkey [32]byte, event *Event) { t.Helper() if err := event.Sign(privkey); err != nil { t.Fatalf("event.Sign: %v", err) diff --git a/example/example.go b/example/example.go index e5826d1..5082c1b 100644 --- a/example/example.go +++ b/example/example.go @@ -9,8 +9,8 @@ import ( "time" jsoniter "github.com/json-iterator/go" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip19" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip19" ) func main() { diff --git a/filter.go b/filter.go index ed4b1f0..6d15c1d 100644 --- a/filter.go +++ b/filter.go @@ -6,12 +6,10 @@ import ( "github.com/mailru/easyjson" ) -type Filters []Filter - type Filter struct { - IDs []string - Kinds []int - Authors []string + IDs []ID + Kinds []uint16 + Authors []PubKey Tags TagMap Since *Timestamp Until *Timestamp @@ -24,29 +22,6 @@ type Filter struct { type TagMap map[string][]string -func (eff Filters) String() string { - j, _ := json.Marshal(eff) - return string(j) -} - -func (eff Filters) Match(event *Event) bool { - for _, filter := range eff { - if filter.Matches(event) { - return true - } - } - return false -} - -func (eff Filters) MatchIgnoringTimestampConstraints(event *Event) bool { - for _, filter := range eff { - if filter.MatchesIgnoringTimestampConstraints(event) { - return true - } - } - return false -} - func (ef Filter) String() string { j, _ := easyjson.Marshal(ef) return string(j) @@ -99,11 +74,11 @@ func FilterEqual(a Filter, b Filter) bool { return false } - if !similar(a.IDs, b.IDs) { + if !similarID(a.IDs, b.IDs) { return false } - if !similar(a.Authors, b.Authors) { + if !similarPublicKey(a.Authors, b.Authors) { return false } @@ -142,14 +117,26 @@ func FilterEqual(a Filter, b Filter) bool { func (ef Filter) Clone() Filter { clone := Filter{ - IDs: slices.Clone(ef.IDs), - Authors: slices.Clone(ef.Authors), Kinds: slices.Clone(ef.Kinds), Limit: ef.Limit, Search: ef.Search, LimitZero: ef.LimitZero, } + if ef.IDs != nil { + clone.IDs = make([]ID, len(ef.IDs)) + for i, src := range ef.IDs { + copy(clone.IDs[i][:], src[:]) + } + } + + if ef.Authors != nil { + clone.Authors = make([]PubKey, len(ef.Authors)) + for i, src := range ef.Authors { + copy(clone.Authors[i][:], src[:]) + } + } + if ef.Tags != nil { clone.Tags = make(TagMap, len(ef.Tags)) for k, v := range ef.Tags { diff --git a/filter_easyjson.go b/filter_easyjson.go index d980045..74755f7 100644 --- a/filter_easyjson.go +++ b/filter_easyjson.go @@ -1,6 +1,8 @@ package nostr import ( + "encoding/hex" + easyjson "github.com/mailru/easyjson" jlexer "github.com/mailru/easyjson/jlexer" jwriter "github.com/mailru/easyjson/jwriter" @@ -41,17 +43,17 @@ func easyjson4d398eaaDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Filter) in.Delim('[') if out.IDs == nil { if !in.IsDelim(']') { - out.IDs = make([]string, 0, 20) + out.IDs = make([]ID, 0, 20) } else { - out.IDs = []string{} + out.IDs = []ID{} } } else { out.IDs = (out.IDs)[:0] } for !in.IsDelim(']') { - var v1 string - v1 = string(in.String()) - out.IDs = append(out.IDs, v1) + id := [32]byte{} + hex.Decode(id[:], []byte(in.String())) + out.IDs = append(out.IDs, id) in.WantComma() } in.Delim(']') @@ -64,17 +66,15 @@ func easyjson4d398eaaDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Filter) in.Delim('[') if out.Kinds == nil { if !in.IsDelim(']') { - out.Kinds = make([]int, 0, 8) + out.Kinds = make([]uint16, 0, 8) } else { - out.Kinds = []int{} + out.Kinds = []uint16{} } } else { out.Kinds = (out.Kinds)[:0] } for !in.IsDelim(']') { - var v2 int - v2 = int(in.Int()) - out.Kinds = append(out.Kinds, v2) + out.Kinds = append(out.Kinds, uint16(in.Int())) in.WantComma() } in.Delim(']') @@ -87,17 +87,17 @@ func easyjson4d398eaaDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Filter) in.Delim('[') if out.Authors == nil { if !in.IsDelim(']') { - out.Authors = make([]string, 0, 40) + out.Authors = make([]PubKey, 0, 40) } else { - out.Authors = []string{} + out.Authors = []PubKey{} } } else { out.Authors = (out.Authors)[:0] } for !in.IsDelim(']') { - var v3 string - v3 = string(in.String()) - out.Authors = append(out.Authors, v3) + pk := [32]byte{} + hex.Decode(pk[:], []byte(in.String())) + out.Authors = append(out.Authors, pk) in.WantComma() } in.Delim(']') @@ -178,7 +178,7 @@ func easyjson4d398eaaEncodeGithubComNbdWtfGoNostr(out *jwriter.Writer, in Filter if v4 > 0 { out.RawByte(',') } - out.String(string(v5)) + out.String(hex.EncodeToString(v5[:])) } out.RawByte(']') } @@ -216,7 +216,7 @@ func easyjson4d398eaaEncodeGithubComNbdWtfGoNostr(out *jwriter.Writer, in Filter if v8 > 0 { out.RawByte(',') } - out.String(string(v9)) + out.String(hex.EncodeToString(v9[:])) } out.RawByte(']') } diff --git a/filter_test.go b/filter_test.go index 5979eff..d8f872f 100644 --- a/filter_test.go +++ b/filter_test.go @@ -28,7 +28,7 @@ func TestFilterUnmarshal(t *testing.T) { func TestFilterMarshal(t *testing.T) { until := Timestamp(12345678) filterj, err := json.Marshal(Filter{ - Kinds: []int{KindTextNote, KindRecommendServer, KindEncryptedDirectMessage}, + Kinds: []uint16{KindTextNote, KindRecommendServer, KindEncryptedDirectMessage}, Tags: TagMap{"fruit": {"banana", "mango"}}, Until: &until, }) @@ -60,7 +60,7 @@ func TestFilterUnmarshalWithLimitZero(t *testing.T) { func TestFilterMarshalWithLimitZero(t *testing.T) { until := Timestamp(12345678) filterj, err := json.Marshal(Filter{ - Kinds: []int{KindTextNote, KindRecommendServer, KindEncryptedDirectMessage}, + Kinds: []uint16{KindTextNote, KindRecommendServer, KindEncryptedDirectMessage}, Tags: TagMap{"fruit": {"banana", "mango"}}, Until: &until, LimitZero: true, @@ -83,50 +83,50 @@ func TestFilterMatchingLive(t *testing.T) { func TestFilterEquality(t *testing.T) { assert.True(t, FilterEqual( - Filter{Kinds: []int{KindEncryptedDirectMessage, KindDeletion}}, - Filter{Kinds: []int{KindEncryptedDirectMessage, KindDeletion}}, + Filter{Kinds: []uint16{KindEncryptedDirectMessage, KindDeletion}}, + Filter{Kinds: []uint16{KindEncryptedDirectMessage, KindDeletion}}, ), "kinds filters should be equal") assert.True(t, FilterEqual( - Filter{Kinds: []int{KindEncryptedDirectMessage, KindDeletion}, Tags: TagMap{"letter": {"a", "b"}}}, - Filter{Kinds: []int{KindEncryptedDirectMessage, KindDeletion}, Tags: TagMap{"letter": {"b", "a"}}}, + Filter{Kinds: []uint16{KindEncryptedDirectMessage, KindDeletion}, Tags: TagMap{"letter": {"a", "b"}}}, + Filter{Kinds: []uint16{KindEncryptedDirectMessage, KindDeletion}, Tags: TagMap{"letter": {"b", "a"}}}, ), "kind+tags filters should be equal") tm := Now() assert.True(t, FilterEqual( Filter{ - Kinds: []int{KindEncryptedDirectMessage, KindDeletion}, + Kinds: []uint16{KindEncryptedDirectMessage, KindDeletion}, Tags: TagMap{"letter": {"a", "b"}, "fruit": {"banana"}}, Since: &tm, - IDs: []string{"aaaa", "bbbb"}, + IDs: []ID{{'a', 'a'}, {'b', 'b'}}, }, Filter{ - Kinds: []int{KindDeletion, KindEncryptedDirectMessage}, + Kinds: []uint16{KindDeletion, KindEncryptedDirectMessage}, Tags: TagMap{"letter": {"a", "b"}, "fruit": {"banana"}}, Since: &tm, - IDs: []string{"aaaa", "bbbb"}, + IDs: []ID{{'a', 'a'}, {'b', 'b'}}, }, ), "kind+2tags+since+ids filters should be equal") assert.False(t, FilterEqual( - Filter{Kinds: []int{KindTextNote, KindEncryptedDirectMessage, KindDeletion}}, - Filter{Kinds: []int{KindEncryptedDirectMessage, KindDeletion, KindRepost}}, + Filter{Kinds: []uint16{KindTextNote, KindEncryptedDirectMessage, KindDeletion}}, + Filter{Kinds: []uint16{KindEncryptedDirectMessage, KindDeletion, KindRepost}}, ), "kinds filters shouldn't be equal") } func TestFilterClone(t *testing.T) { ts := Now() - 60*60 flt := Filter{ - Kinds: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + Kinds: []uint16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, Tags: TagMap{"letter": {"a", "b"}, "fruit": {"banana"}}, Since: &ts, - IDs: []string{"9894b4b5cb5166d23ee8899a4151cf0c66aec00bde101982a13b8e8ceb972df9"}, + IDs: []ID{IDFromHex("9894b4b5cb5166d23ee8899a4151cf0c66aec00bde101982a13b8e8ceb972df9")}, } clone := flt.Clone() assert.True(t, FilterEqual(flt, clone), "clone is not equal:\n %v !=\n %v", flt, clone) clone1 := flt.Clone() - clone1.IDs = append(clone1.IDs, "88f0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d") + clone1.IDs = append(clone1.IDs, IDFromHex("88f0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")) assert.False(t, FilterEqual(flt, clone1), "modifying the clone ids should cause it to not be equal anymore") clone2 := flt.Clone() @@ -143,11 +143,11 @@ func TestFilterClone(t *testing.T) { } func TestTheoreticalLimit(t *testing.T) { - require.Equal(t, 6, GetTheoreticalLimit(Filter{IDs: []string{"a", "b", "c", "d", "e", "f"}})) - require.Equal(t, 9, GetTheoreticalLimit(Filter{Authors: []string{"a", "b", "c"}, Kinds: []int{3, 0, 10002}})) - require.Equal(t, 4, GetTheoreticalLimit(Filter{Authors: []string{"a", "b", "c", "d"}, Kinds: []int{10050}})) - require.Equal(t, -1, GetTheoreticalLimit(Filter{Authors: []string{"a", "b", "c", "d"}})) - require.Equal(t, -1, GetTheoreticalLimit(Filter{Kinds: []int{3, 0, 10002}})) - require.Equal(t, 24, GetTheoreticalLimit(Filter{Authors: []string{"a", "b", "c", "d", "e", "f"}, Kinds: []int{30023, 30024}, Tags: TagMap{"d": []string{"aaa", "bbb"}}})) - require.Equal(t, -1, GetTheoreticalLimit(Filter{Authors: []string{"a", "b", "c", "d", "e", "f"}, Kinds: []int{30023, 30024}})) + require.Equal(t, 6, GetTheoreticalLimit(Filter{IDs: []ID{{'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}}})) + require.Equal(t, 9, GetTheoreticalLimit(Filter{Authors: []PubKey{{'a'}, {'b'}, {'c'}}, Kinds: []uint16{3, 0, 10002}})) + require.Equal(t, 4, GetTheoreticalLimit(Filter{Authors: []PubKey{{'a'}, {'b'}, {'c'}, {'d'}}, Kinds: []uint16{10050}})) + require.Equal(t, -1, GetTheoreticalLimit(Filter{Authors: []PubKey{{'a'}, {'b'}, {'c'}, {'d'}}})) + require.Equal(t, -1, GetTheoreticalLimit(Filter{Kinds: []uint16{3, 0, 10002}})) + require.Equal(t, 24, GetTheoreticalLimit(Filter{Authors: []PubKey{{'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}}, Kinds: []uint16{30023, 30024}, Tags: TagMap{"d": []string{"aaa", "bbb"}}})) + require.Equal(t, -1, GetTheoreticalLimit(Filter{Authors: []PubKey{{'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}}, Kinds: []uint16{30023, 30024}})) } diff --git a/go.mod b/go.mod index 89ce563..677d611 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/nbd-wtf/go-nostr +module fiatjaf.com/nostr go 1.24.1 diff --git a/helpers.go b/helpers.go index 0ec7f63..1538cbe 100644 --- a/helpers.go +++ b/helpers.go @@ -1,6 +1,7 @@ package nostr import ( + "encoding/hex" "strconv" "strings" "sync" @@ -49,6 +50,48 @@ func similar[E constraints.Ordered](as, bs []E) bool { return true } +func similarID(as, bs []ID) bool { + if len(as) != len(bs) { + return false + } + + for _, a := range as { + for _, b := range bs { + if b == a { + goto next + } + } + // didn't find a B that corresponded to the current A + return false + + next: + continue + } + + return true +} + +func similarPublicKey(as, bs []PubKey) bool { + if len(as) != len(bs) { + return false + } + + for _, a := range as { + for _, b := range bs { + if b == a { + goto next + } + } + // didn't find a B that corresponded to the current A + return false + + next: + continue + } + + return true +} + // Escaping strings for JSON encoding according to RFC8259. // Also encloses result in quotation marks "". func escapeString(dst []byte, s string) []byte { @@ -140,11 +183,11 @@ func extractSubID(jsonStr string) string { return jsonStr[start : start+end] } -func extractEventID(jsonStr string) string { +func extractEventID(jsonStr string) ID { // look for "id" pattern start := strings.Index(jsonStr, `"id"`) if start == -1 { - return "" + return [32]byte{} } // move to the next quote @@ -152,14 +195,16 @@ func extractEventID(jsonStr string) string { start += 4 + offset + 1 // get 64 characters of the id - return jsonStr[start : start+64] + var id [32]byte + hex.Decode(id[:], unsafe.Slice(unsafe.StringData(jsonStr[start:start+64]), 64)) + return id } -func extractEventPubKey(jsonStr string) string { +func extractEventPubKey(jsonStr string) PubKey { // look for "pubkey" pattern start := strings.Index(jsonStr, `"pubkey"`) if start == -1 { - return "" + return PubKey{} } // move to the next quote @@ -167,7 +212,9 @@ func extractEventPubKey(jsonStr string) string { start += 8 + offset + 1 // get 64 characters of the pubkey - return jsonStr[start : start+64] + var pk [32]byte + hex.Decode(pk[:], unsafe.Slice(unsafe.StringData(jsonStr[start:start+64]), 64)) + return pk } func extractDTag(jsonStr string) string { diff --git a/keyer/bunker.go b/keyer/bunker.go index 96fef87..d88745f 100644 --- a/keyer/bunker.go +++ b/keyer/bunker.go @@ -5,8 +5,8 @@ import ( "errors" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip46" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip46" ) var _ nostr.Keyer = (*BunkerSigner)(nil) diff --git a/keyer/encrypted.go b/keyer/encrypted.go index 73d4567..d11305c 100644 --- a/keyer/encrypted.go +++ b/keyer/encrypted.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip44" - "github.com/nbd-wtf/go-nostr/nip49" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip44" + "fiatjaf.com/nostrlib/nip49" ) var _ nostr.Keyer = (*EncryptedKeySigner)(nil) diff --git a/keyer/lib.go b/keyer/lib.go index 15ad6f1..bfff088 100644 --- a/keyer/lib.go +++ b/keyer/lib.go @@ -7,11 +7,11 @@ import ( "strings" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip05" - "github.com/nbd-wtf/go-nostr/nip19" - "github.com/nbd-wtf/go-nostr/nip46" - "github.com/nbd-wtf/go-nostr/nip49" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip05" + "fiatjaf.com/nostrlib/nip19" + "fiatjaf.com/nostrlib/nip46" + "fiatjaf.com/nostrlib/nip49" "github.com/puzpuzpuz/xsync/v3" ) diff --git a/keyer/manual.go b/keyer/manual.go index 858c81a..6926ead 100644 --- a/keyer/manual.go +++ b/keyer/manual.go @@ -3,7 +3,7 @@ package keyer import ( "context" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) var _ nostr.Keyer = (*ManualSigner)(nil) diff --git a/keyer/plain.go b/keyer/plain.go index 5018e7a..946979a 100644 --- a/keyer/plain.go +++ b/keyer/plain.go @@ -3,8 +3,8 @@ package keyer import ( "context" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip44" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip44" "github.com/puzpuzpuz/xsync/v3" ) diff --git a/keys.go b/keys.go index b3bf6ce..37f0547 100644 --- a/keys.go +++ b/keys.go @@ -2,48 +2,27 @@ package nostr import ( "crypto/rand" - "encoding/hex" "fmt" "io" - "math/big" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" ) -func GeneratePrivateKey() string { - params := btcec.S256().Params() - one := new(big.Int).SetInt64(1) - - b := make([]byte, params.BitSize/8+8) - if _, err := io.ReadFull(rand.Reader, b); err != nil { - return "" +func GeneratePrivateKey() [32]byte { + var sk [32]byte + if _, err := io.ReadFull(rand.Reader, sk[:]); err != nil { + panic(fmt.Errorf("failed to read random bytes when generating private key")) } - - k := new(big.Int).SetBytes(b) - n := new(big.Int).Sub(params.N, one) - k.Mod(k, n) - k.Add(k, one) - - return fmt.Sprintf("%064x", k.Bytes()) + return sk } -func GetPublicKey(sk string) (string, error) { - b, err := hex.DecodeString(sk) - if err != nil { - return "", err - } - - _, pk := btcec.PrivKeyFromBytes(b) - return hex.EncodeToString(schnorr.SerializePubKey(pk)), nil +func GetPublicKey(sk [32]byte) PubKey { + _, pk := btcec.PrivKeyFromBytes(sk[:]) + return [32]byte(pk.SerializeCompressed()[1:]) } -func IsValidPublicKey(pk string) bool { - if !isLowerHex(pk) { - return false - } - - v, _ := hex.DecodeString(pk) - _, err := schnorr.ParsePubKey(v) +func IsValidPublicKey(pk [32]byte) bool { + _, err := schnorr.ParsePubKey(pk[:]) return err == nil } diff --git a/kinds.go b/kinds.go index 4018913..b6cd7f2 100644 --- a/kinds.go +++ b/kinds.go @@ -1,152 +1,152 @@ package nostr const ( - KindProfileMetadata int = 0 - KindTextNote int = 1 - KindRecommendServer int = 2 - KindFollowList int = 3 - KindEncryptedDirectMessage int = 4 - KindDeletion int = 5 - KindRepost int = 6 - KindReaction int = 7 - KindBadgeAward int = 8 - KindSimpleGroupChatMessage int = 9 - KindSimpleGroupThreadedReply int = 10 - KindSimpleGroupThread int = 11 - KindSimpleGroupReply int = 12 - KindSeal int = 13 - KindDirectMessage int = 14 - KindGenericRepost int = 16 - KindReactionToWebsite int = 17 - KindChannelCreation int = 40 - KindChannelMetadata int = 41 - KindChannelMessage int = 42 - KindChannelHideMessage int = 43 - KindChannelMuteUser int = 44 - KindChess int = 64 - KindMergeRequests int = 818 - KindComment int = 1111 - KindBid int = 1021 - KindBidConfirmation int = 1022 - KindOpenTimestamps int = 1040 - KindGiftWrap int = 1059 - KindFileMetadata int = 1063 - KindLiveChatMessage int = 1311 - KindPatch int = 1617 - KindIssue int = 1621 - KindReply int = 1622 - KindStatusOpen int = 1630 - KindStatusApplied int = 1631 - KindStatusClosed int = 1632 - KindStatusDraft int = 1633 - KindProblemTracker int = 1971 - KindReporting int = 1984 - KindLabel int = 1985 - KindRelayReviews int = 1986 - KindAIEmbeddings int = 1987 - KindTorrent int = 2003 - KindTorrentComment int = 2004 - KindCoinjoinPool int = 2022 - KindCommunityPostApproval int = 4550 - KindJobFeedback int = 7000 - KindSimpleGroupPutUser int = 9000 - KindSimpleGroupRemoveUser int = 9001 - KindSimpleGroupEditMetadata int = 9002 - KindSimpleGroupDeleteEvent int = 9005 - KindSimpleGroupCreateGroup int = 9007 - KindSimpleGroupDeleteGroup int = 9008 - KindSimpleGroupCreateInvite int = 9009 - KindSimpleGroupJoinRequest int = 9021 - KindSimpleGroupLeaveRequest int = 9022 - KindZapGoal int = 9041 - KindNutZap int = 9321 - KindTidalLogin int = 9467 - KindZapRequest int = 9734 - KindZap int = 9735 - KindHighlights int = 9802 - KindMuteList int = 10000 - KindPinList int = 10001 - KindRelayListMetadata int = 10002 - KindBookmarkList int = 10003 - KindCommunityList int = 10004 - KindPublicChatList int = 10005 - KindBlockedRelayList int = 10006 - KindSearchRelayList int = 10007 - KindSimpleGroupList int = 10009 - KindInterestList int = 10015 - KindNutZapInfo int = 10019 - KindEmojiList int = 10030 - KindDMRelayList int = 10050 - KindUserServerList int = 10063 - KindFileStorageServerList int = 10096 - KindGoodWikiAuthorList int = 10101 - KindGoodWikiRelayList int = 10102 - KindNWCWalletInfo int = 13194 - KindLightningPubRPC int = 21000 - KindClientAuthentication int = 22242 - KindNWCWalletRequest int = 23194 - KindNWCWalletResponse int = 23195 - KindNostrConnect int = 24133 - KindBlobs int = 24242 - KindHTTPAuth int = 27235 - KindCategorizedPeopleList int = 30000 - KindCategorizedBookmarksList int = 30001 - KindRelaySets int = 30002 - KindBookmarkSets int = 30003 - KindCuratedSets int = 30004 - KindCuratedVideoSets int = 30005 - KindMuteSets int = 30007 - KindProfileBadges int = 30008 - KindBadgeDefinition int = 30009 - KindInterestSets int = 30015 - KindStallDefinition int = 30017 - KindProductDefinition int = 30018 - KindMarketplaceUI int = 30019 - KindProductSoldAsAuction int = 30020 - KindArticle int = 30023 - KindDraftArticle int = 30024 - KindEmojiSets int = 30030 - KindModularArticleHeader int = 30040 - KindModularArticleContent int = 30041 - KindReleaseArtifactSets int = 30063 - KindApplicationSpecificData int = 30078 - KindLiveEvent int = 30311 - KindUserStatuses int = 30315 - KindClassifiedListing int = 30402 - KindDraftClassifiedListing int = 30403 - KindRepositoryAnnouncement int = 30617 - KindRepositoryState int = 30618 - KindSimpleGroupMetadata int = 39000 - KindSimpleGroupAdmins int = 39001 - KindSimpleGroupMembers int = 39002 - KindSimpleGroupRoles int = 39003 - KindWikiArticle int = 30818 - KindRedirects int = 30819 - KindFeed int = 31890 - KindDateCalendarEvent int = 31922 - KindTimeCalendarEvent int = 31923 - KindCalendar int = 31924 - KindCalendarEventRSVP int = 31925 - KindHandlerRecommendation int = 31989 - KindHandlerInformation int = 31990 - KindVideoEvent int = 34235 - KindShortVideoEvent int = 34236 - KindVideoViewEvent int = 34237 - KindCommunityDefinition int = 34550 + KindProfileMetadata uint16 = 0 + KindTextNote uint16 = 1 + KindRecommendServer uint16 = 2 + KindFollowList uint16 = 3 + KindEncryptedDirectMessage uint16 = 4 + KindDeletion uint16 = 5 + KindRepost uint16 = 6 + KindReaction uint16 = 7 + KindBadgeAward uint16 = 8 + KindSimpleGroupChatMessage uint16 = 9 + KindSimpleGroupThreadedReply uint16 = 10 + KindSimpleGroupThread uint16 = 11 + KindSimpleGroupReply uint16 = 12 + KindSeal uint16 = 13 + KindDirectMessage uint16 = 14 + KindGenericRepost uint16 = 16 + KindReactionToWebsite uint16 = 17 + KindChannelCreation uint16 = 40 + KindChannelMetadata uint16 = 41 + KindChannelMessage uint16 = 42 + KindChannelHideMessage uint16 = 43 + KindChannelMuteUser uint16 = 44 + KindChess uint16 = 64 + KindMergeRequests uint16 = 818 + KindComment uint16 = 1111 + KindBid uint16 = 1021 + KindBidConfirmation uint16 = 1022 + KindOpenTimestamps uint16 = 1040 + KindGiftWrap uint16 = 1059 + KindFileMetadata uint16 = 1063 + KindLiveChatMessage uint16 = 1311 + KindPatch uint16 = 1617 + KindIssue uint16 = 1621 + KindReply uint16 = 1622 + KindStatusOpen uint16 = 1630 + KindStatusApplied uint16 = 1631 + KindStatusClosed uint16 = 1632 + KindStatusDraft uint16 = 1633 + KindProblemTracker uint16 = 1971 + KindReporting uint16 = 1984 + KindLabel uint16 = 1985 + KindRelayReviews uint16 = 1986 + KindAIEmbeddings uint16 = 1987 + KindTorrent uint16 = 2003 + KindTorrentComment uint16 = 2004 + KindCoinjoinPool uint16 = 2022 + KindCommunityPostApproval uint16 = 4550 + KindJobFeedback uint16 = 7000 + KindSimpleGroupPutUser uint16 = 9000 + KindSimpleGroupRemoveUser uint16 = 9001 + KindSimpleGroupEditMetadata uint16 = 9002 + KindSimpleGroupDeleteEvent uint16 = 9005 + KindSimpleGroupCreateGroup uint16 = 9007 + KindSimpleGroupDeleteGroup uint16 = 9008 + KindSimpleGroupCreateInvite uint16 = 9009 + KindSimpleGroupJoinRequest uint16 = 9021 + KindSimpleGroupLeaveRequest uint16 = 9022 + KindZapGoal uint16 = 9041 + KindNutZap uint16 = 9321 + KindTidalLogin uint16 = 9467 + KindZapRequest uint16 = 9734 + KindZap uint16 = 9735 + KindHighlights uint16 = 9802 + KindMuteList uint16 = 10000 + KindPinList uint16 = 10001 + KindRelayListMetadata uint16 = 10002 + KindBookmarkList uint16 = 10003 + KindCommunityList uint16 = 10004 + KindPublicChatList uint16 = 10005 + KindBlockedRelayList uint16 = 10006 + KindSearchRelayList uint16 = 10007 + KindSimpleGroupList uint16 = 10009 + KindInterestList uint16 = 10015 + KindNutZapInfo uint16 = 10019 + KindEmojiList uint16 = 10030 + KindDMRelayList uint16 = 10050 + KindUserServerList uint16 = 10063 + KindFileStorageServerList uint16 = 10096 + KindGoodWikiAuthorList uint16 = 10101 + KindGoodWikiRelayList uint16 = 10102 + KindNWCWalletInfo uint16 = 13194 + KindLightningPubRPC uint16 = 21000 + KindClientAuthentication uint16 = 22242 + KindNWCWalletRequest uint16 = 23194 + KindNWCWalletResponse uint16 = 23195 + KindNostrConnect uint16 = 24133 + KindBlobs uint16 = 24242 + KindHTTPAuth uint16 = 27235 + KindCategorizedPeopleList uint16 = 30000 + KindCategorizedBookmarksList uint16 = 30001 + KindRelaySets uint16 = 30002 + KindBookmarkSets uint16 = 30003 + KindCuratedSets uint16 = 30004 + KindCuratedVideoSets uint16 = 30005 + KindMuteSets uint16 = 30007 + KindProfileBadges uint16 = 30008 + KindBadgeDefinition uint16 = 30009 + KindInterestSets uint16 = 30015 + KindStallDefinition uint16 = 30017 + KindProductDefinition uint16 = 30018 + KindMarketplaceUI uint16 = 30019 + KindProductSoldAsAuction uint16 = 30020 + KindArticle uint16 = 30023 + KindDraftArticle uint16 = 30024 + KindEmojiSets uint16 = 30030 + KindModularArticleHeader uint16 = 30040 + KindModularArticleContent uint16 = 30041 + KindReleaseArtifactSets uint16 = 30063 + KindApplicationSpecificData uint16 = 30078 + KindLiveEvent uint16 = 30311 + KindUserStatuses uint16 = 30315 + KindClassifiedListing uint16 = 30402 + KindDraftClassifiedListing uint16 = 30403 + KindRepositoryAnnouncement uint16 = 30617 + KindRepositoryState uint16 = 30618 + KindSimpleGroupMetadata uint16 = 39000 + KindSimpleGroupAdmins uint16 = 39001 + KindSimpleGroupMembers uint16 = 39002 + KindSimpleGroupRoles uint16 = 39003 + KindWikiArticle uint16 = 30818 + KindRedirects uint16 = 30819 + KindFeed uint16 = 31890 + KindDateCalendarEvent uint16 = 31922 + KindTimeCalendarEvent uint16 = 31923 + KindCalendar uint16 = 31924 + KindCalendarEventRSVP uint16 = 31925 + KindHandlerRecommendation uint16 = 31989 + KindHandlerInformation uint16 = 31990 + KindVideoEvent uint16 = 34235 + KindShortVideoEvent uint16 = 34236 + KindVideoViewEvent uint16 = 34237 + KindCommunityDefinition uint16 = 34550 ) -func IsRegularKind(kind int) bool { +func IsRegularKind(kind uint16) bool { return kind < 10000 && kind != 0 && kind != 3 } -func IsReplaceableKind(kind int) bool { +func IsReplaceableKind(kind uint16) bool { return kind == 0 || kind == 3 || (10000 <= kind && kind < 20000) } -func IsEphemeralKind(kind int) bool { +func IsEphemeralKind(kind uint16) bool { return 20000 <= kind && kind < 30000 } -func IsAddressableKind(kind int) bool { +func IsAddressableKind(kind uint16) bool { return 30000 <= kind && kind < 40000 } diff --git a/nip04/nip04_test.go b/nip04/nip04_test.go index dc313dc..123375d 100644 --- a/nip04/nip04_test.go +++ b/nip04/nip04_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" "github.com/stretchr/testify/require" ) diff --git a/nip05/nip05.go b/nip05/nip05.go index 2f7e82e..b8ef441 100644 --- a/nip05/nip05.go +++ b/nip05/nip05.go @@ -8,7 +8,7 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) var NIP05_REGEX = regexp.MustCompile(`^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$`) diff --git a/nip10/nip10.go b/nip10/nip10.go index 7bc52c4..5b848d2 100644 --- a/nip10/nip10.go +++ b/nip10/nip10.go @@ -1,6 +1,6 @@ package nip10 -import "github.com/nbd-wtf/go-nostr" +import "fiatjaf.com/nostrlib" func GetThreadRoot(tags nostr.Tags) *nostr.EventPointer { for _, tag := range tags { diff --git a/nip11/fetch.go b/nip11/fetch.go index a679f14..97dea1d 100644 --- a/nip11/fetch.go +++ b/nip11/fetch.go @@ -7,7 +7,7 @@ import ( "time" jsoniter "github.com/json-iterator/go" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) // Fetch fetches the NIP-11 metadata for a relay. diff --git a/nip13/nip13.go b/nip13/nip13.go index 71fd5a3..3286269 100644 --- a/nip13/nip13.go +++ b/nip13/nip13.go @@ -9,7 +9,7 @@ import ( "runtime" "strconv" - nostr "github.com/nbd-wtf/go-nostr" + nostr "fiatjaf.com/nostrlib" ) var ( diff --git a/nip13/nip13_test.go b/nip13/nip13_test.go index 2b3c2ed..b3afc0b 100644 --- a/nip13/nip13_test.go +++ b/nip13/nip13_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - nostr "github.com/nbd-wtf/go-nostr" + nostr "fiatjaf.com/nostrlib" "github.com/stretchr/testify/require" ) diff --git a/nip14/subject.go b/nip14/subject.go index 84c9ddf..259ddf5 100644 --- a/nip14/subject.go +++ b/nip14/subject.go @@ -1,6 +1,6 @@ package nip14 -import "github.com/nbd-wtf/go-nostr" +import "fiatjaf.com/nostrlib" func GetSubject(tags nostr.Tags) string { for _, tag := range tags { diff --git a/nip17/nip17.go b/nip17/nip17.go index 1647e7a..0b5b320 100644 --- a/nip17/nip17.go +++ b/nip17/nip17.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip59" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip59" ) func GetDMRelays(ctx context.Context, pubkey string, pool *nostr.SimplePool, relaysToQuery []string) []string { diff --git a/nip19/nip19.go b/nip19/nip19.go index ccaaa7c..25e3b50 100644 --- a/nip19/nip19.go +++ b/nip19/nip19.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/btcsuite/btcd/btcutil/bech32" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) func Decode(bech32string string) (prefix string, value any, err error) { diff --git a/nip19/nip19_test.go b/nip19/nip19_test.go index 9ca31dc..8c4edc4 100644 --- a/nip19/nip19_test.go +++ b/nip19/nip19_test.go @@ -3,7 +3,7 @@ package nip19 import ( "testing" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/nip22/nip22.go b/nip22/nip22.go index 1a79c85..a4b9c0e 100644 --- a/nip22/nip22.go +++ b/nip22/nip22.go @@ -1,8 +1,8 @@ package nip22 import ( - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip73" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/nip73" ) func GetThreadRoot(tags nostr.Tags) nostr.Pointer { diff --git a/nip29/group.go b/nip29/group.go index 9d9e1df..08c0e39 100644 --- a/nip29/group.go +++ b/nip29/group.go @@ -6,7 +6,7 @@ import ( "slices" "strings" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type GroupAddress struct { diff --git a/nip29/nip29.go b/nip29/nip29.go index e0b92c9..94fc95b 100644 --- a/nip29/nip29.go +++ b/nip29/nip29.go @@ -3,7 +3,7 @@ package nip29 import ( "slices" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type Role struct { diff --git a/nip31/nip31.go b/nip31/nip31.go index b508340..5383fad 100644 --- a/nip31/nip31.go +++ b/nip31/nip31.go @@ -1,6 +1,6 @@ package nip31 -import "github.com/nbd-wtf/go-nostr" +import "fiatjaf.com/nostrlib" func GetAlt(event nostr.Event) string { for _, tag := range event.Tags { diff --git a/nip34/patch.go b/nip34/patch.go index 1aa4341..0aa81ba 100644 --- a/nip34/patch.go +++ b/nip34/patch.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/bluekeyes/go-gitdiff/gitdiff" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type Patch struct { diff --git a/nip34/repository.go b/nip34/repository.go index a01ff80..3601533 100644 --- a/nip34/repository.go +++ b/nip34/repository.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type Repository struct { diff --git a/nip34/state.go b/nip34/state.go index 0ef4c0f..bc08a1e 100644 --- a/nip34/state.go +++ b/nip34/state.go @@ -3,7 +3,7 @@ package nip34 import ( "strings" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type RepositoryState struct { diff --git a/nip40/nip40.go b/nip40/nip40.go index f1cadfd..7d83e03 100644 --- a/nip40/nip40.go +++ b/nip40/nip40.go @@ -3,7 +3,7 @@ package nip40 import ( "strconv" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) // GetExpiration returns the expiration timestamp for this event, or -1 if no "expiration" tag exists or diff --git a/nip42/nip42.go b/nip42/nip42.go index 9d68ec4..d0448a7 100644 --- a/nip42/nip42.go +++ b/nip42/nip42.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) // CreateUnsignedAuthEvent creates an event which should be sent via an "AUTH" command. diff --git a/nip44/nip44_test.go b/nip44/nip44_test.go index 810c2b6..34abb00 100644 --- a/nip44/nip44_test.go +++ b/nip44/nip44_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" "github.com/stretchr/testify/require" ) diff --git a/nip45/hll_event.go b/nip45/hll_event.go index 51ef45c..26d29e8 100644 --- a/nip45/hll_event.go +++ b/nip45/hll_event.go @@ -4,7 +4,7 @@ import ( "iter" "strconv" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) func HyperLogLogEventPubkeyOffsetsAndReferencesForEvent(evt *nostr.Event) iter.Seq2[string, int] { diff --git a/nip45/hll_filter.go b/nip45/hll_filter.go index 857e5b7..9e949bc 100644 --- a/nip45/hll_filter.go +++ b/nip45/hll_filter.go @@ -3,7 +3,7 @@ package nip45 import ( "strconv" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) // HyperLogLogEventPubkeyOffsetForFilter returns the deterministic pubkey offset that will be used diff --git a/nip46/bunker_session.go b/nip46/bunker_session.go index 1023fe6..15f2a28 100644 --- a/nip46/bunker_session.go +++ b/nip46/bunker_session.go @@ -3,9 +3,9 @@ package nip46 import ( "fmt" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip04" - "github.com/nbd-wtf/go-nostr/nip44" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip04" + "fiatjaf.com/nostrlib/nip44" ) type Session struct { diff --git a/nip46/client.go b/nip46/client.go index f8f3c28..b46ba9d 100644 --- a/nip46/client.go +++ b/nip46/client.go @@ -9,9 +9,9 @@ import ( "sync/atomic" "github.com/mailru/easyjson" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip04" - "github.com/nbd-wtf/go-nostr/nip44" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip04" + "fiatjaf.com/nostrlib/nip44" "github.com/puzpuzpuz/xsync/v3" ) diff --git a/nip46/dynamic-signer.go b/nip46/dynamic-signer.go index eaf7ca4..f938686 100644 --- a/nip46/dynamic-signer.go +++ b/nip46/dynamic-signer.go @@ -7,9 +7,9 @@ import ( "sync" "github.com/mailru/easyjson" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip04" - "github.com/nbd-wtf/go-nostr/nip44" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip04" + "fiatjaf.com/nostrlib/nip44" ) var _ Signer = (*DynamicSigner)(nil) diff --git a/nip46/nip46.go b/nip46/nip46.go index f892671..33529fe 100644 --- a/nip46/nip46.go +++ b/nip46/nip46.go @@ -6,7 +6,7 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) var json = jsoniter.ConfigFastest diff --git a/nip46/static-key-signer.go b/nip46/static-key-signer.go index 75e2dea..70467fa 100644 --- a/nip46/static-key-signer.go +++ b/nip46/static-key-signer.go @@ -7,9 +7,9 @@ import ( "sync" "github.com/mailru/easyjson" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip04" - "github.com/nbd-wtf/go-nostr/nip44" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip04" + "fiatjaf.com/nostrlib/nip44" ) var _ Signer = (*StaticKeySigner)(nil) diff --git a/nip46/wellknownnostrjson.go b/nip46/wellknownnostrjson.go index 82848f7..4008b9c 100644 --- a/nip46/wellknownnostrjson.go +++ b/nip46/wellknownnostrjson.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/nbd-wtf/go-nostr/nip05" + "fiatjaf.com/nostrlib/nip05" ) func queryWellKnownNostrJson(ctx context.Context, fullname string) (pubkey string, relays []string, err error) { diff --git a/nip52/calendar_event.go b/nip52/calendar_event.go index 0947850..dddf547 100644 --- a/nip52/calendar_event.go +++ b/nip52/calendar_event.go @@ -4,7 +4,7 @@ import ( "strconv" "time" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type CalendarEventKind int diff --git a/nip53/nip53.go b/nip53/nip53.go index 8f84083..42ba6c3 100644 --- a/nip53/nip53.go +++ b/nip53/nip53.go @@ -4,7 +4,7 @@ import ( "strconv" "time" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type LiveEvent struct { diff --git a/nip59/nip59.go b/nip59/nip59.go index fd8dab3..724c01b 100644 --- a/nip59/nip59.go +++ b/nip59/nip59.go @@ -5,8 +5,8 @@ import ( "math/rand" "github.com/mailru/easyjson" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip44" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip44" ) // GiftWrap takes a 'rumor', encrypts it with our own key, making a 'seal', then encrypts that with a nonce key and diff --git a/nip60/history.go b/nip60/history.go index b1e17d3..b4fa013 100644 --- a/nip60/history.go +++ b/nip60/history.go @@ -6,7 +6,7 @@ import ( "fmt" "strconv" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type HistoryEntry struct { diff --git a/nip60/lightning-swap.go b/nip60/lightning-swap.go index c052634..4023b2d 100644 --- a/nip60/lightning-swap.go +++ b/nip60/lightning-swap.go @@ -9,7 +9,7 @@ import ( "github.com/elnosh/gonuts/cashu/nuts/nut02" "github.com/elnosh/gonuts/cashu/nuts/nut04" "github.com/elnosh/gonuts/cashu/nuts/nut05" - "github.com/nbd-wtf/go-nostr/nip60/client" + "fiatjaf.com/nostrlib/nip60/client" ) type lightningSwapStatus int diff --git a/nip60/pay.go b/nip60/pay.go index 002cb90..b6bfd76 100644 --- a/nip60/pay.go +++ b/nip60/pay.go @@ -7,8 +7,8 @@ import ( "github.com/elnosh/gonuts/cashu" "github.com/elnosh/gonuts/cashu/nuts/nut05" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip60/client" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip60/client" ) func (w *Wallet) PayBolt11(ctx context.Context, invoice string, opts ...SendOption) (string, error) { diff --git a/nip60/receive.go b/nip60/receive.go index 007cd11..79e5c77 100644 --- a/nip60/receive.go +++ b/nip60/receive.go @@ -7,8 +7,8 @@ import ( "github.com/elnosh/gonuts/cashu" "github.com/elnosh/gonuts/cashu/nuts/nut10" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip60/client" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip60/client" ) type receiveSettings struct { diff --git a/nip60/send-external.go b/nip60/send-external.go index f0fcab2..1dbb6ea 100644 --- a/nip60/send-external.go +++ b/nip60/send-external.go @@ -6,7 +6,7 @@ import ( "github.com/elnosh/gonuts/cashu" "github.com/elnosh/gonuts/cashu/nuts/nut04" - "github.com/nbd-wtf/go-nostr/nip60/client" + "fiatjaf.com/nostrlib/nip60/client" ) func (w *Wallet) SendExternal( diff --git a/nip60/send.go b/nip60/send.go index 384bfe2..b18220e 100644 --- a/nip60/send.go +++ b/nip60/send.go @@ -11,8 +11,8 @@ import ( "github.com/elnosh/gonuts/cashu/nuts/nut02" "github.com/elnosh/gonuts/cashu/nuts/nut10" "github.com/elnosh/gonuts/cashu/nuts/nut11" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip60/client" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip60/client" ) type SendOption func(opts *sendSettings) diff --git a/nip60/swap.go b/nip60/swap.go index 8c21cee..0842547 100644 --- a/nip60/swap.go +++ b/nip60/swap.go @@ -10,7 +10,7 @@ import ( "github.com/elnosh/gonuts/cashu/nuts/nut02" "github.com/elnosh/gonuts/cashu/nuts/nut03" "github.com/elnosh/gonuts/cashu/nuts/nut10" - "github.com/nbd-wtf/go-nostr/nip60/client" + "fiatjaf.com/nostrlib/nip60/client" ) type swapSettings struct { diff --git a/nip60/token.go b/nip60/token.go index 3b6bdaa..7580a4e 100644 --- a/nip60/token.go +++ b/nip60/token.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/elnosh/gonuts/cashu" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type Token struct { diff --git a/nip60/wallet.go b/nip60/wallet.go index ded01f6..fb55335 100644 --- a/nip60/wallet.go +++ b/nip60/wallet.go @@ -11,7 +11,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type Wallet struct { diff --git a/nip60/wallet_test.go b/nip60/wallet_test.go index ffc948b..b1873cd 100644 --- a/nip60/wallet_test.go +++ b/nip60/wallet_test.go @@ -10,8 +10,8 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/elnosh/gonuts/cashu" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/keyer" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/keyer" "github.com/stretchr/testify/require" "golang.org/x/exp/rand" ) diff --git a/nip61/info.go b/nip61/info.go index 98ed93f..d719f5a 100644 --- a/nip61/info.go +++ b/nip61/info.go @@ -5,7 +5,7 @@ import ( "slices" "github.com/elnosh/gonuts/cashu" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type Info struct { diff --git a/nip61/nip61.go b/nip61/nip61.go index 78c8c9c..fef1087 100644 --- a/nip61/nip61.go +++ b/nip61/nip61.go @@ -9,8 +9,8 @@ import ( "slices" "strings" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip60" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip60" ) var NutzapsNotAccepted = errors.New("user doesn't accept nutzaps") diff --git a/nip73/pointer.go b/nip73/pointer.go index 2a27f0a..a40ca38 100644 --- a/nip73/pointer.go +++ b/nip73/pointer.go @@ -1,6 +1,6 @@ package nip73 -import "github.com/nbd-wtf/go-nostr" +import "fiatjaf.com/nostr" var _ nostr.Pointer = (*ExternalPointer)(nil) diff --git a/nip77/envelopes.go b/nip77/envelopes.go index 3043ad7..b77902c 100644 --- a/nip77/envelopes.go +++ b/nip77/envelopes.go @@ -8,7 +8,7 @@ import ( "github.com/mailru/easyjson" jwriter "github.com/mailru/easyjson/jwriter" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" "github.com/tidwall/gjson" ) diff --git a/nip77/example/example.go b/nip77/example/example.go index d0fde7a..e5b60c2 100644 --- a/nip77/example/example.go +++ b/nip77/example/example.go @@ -7,8 +7,8 @@ import ( "github.com/fiatjaf/eventstore" "github.com/fiatjaf/eventstore/slicestore" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip77" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip77" ) func main() { diff --git a/nip77/negentropy/encoding.go b/nip77/negentropy/encoding.go index 239fd7d..b77df13 100644 --- a/nip77/negentropy/encoding.go +++ b/nip77/negentropy/encoding.go @@ -3,7 +3,7 @@ package negentropy import ( "fmt" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) func (n *Negentropy) readTimestamp(reader *StringHexReader) (nostr.Timestamp, error) { diff --git a/nip77/negentropy/fuzz_test.go b/nip77/negentropy/fuzz_test.go index 20b2a16..d266340 100644 --- a/nip77/negentropy/fuzz_test.go +++ b/nip77/negentropy/fuzz_test.go @@ -9,9 +9,9 @@ import ( "sync" "testing" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip77/negentropy" - "github.com/nbd-wtf/go-nostr/nip77/negentropy/storage/vector" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip77/negentropy" + "fiatjaf.com/nostrlib/nip77/negentropy/storage/vector" "github.com/stretchr/testify/require" ) diff --git a/nip77/negentropy/negentropy.go b/nip77/negentropy/negentropy.go index 71e121d..2dac388 100644 --- a/nip77/negentropy/negentropy.go +++ b/nip77/negentropy/negentropy.go @@ -6,7 +6,7 @@ import ( "strings" "unsafe" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) const ( diff --git a/nip77/negentropy/storage/accumulator.go b/nip77/negentropy/storage/accumulator.go index e8d4453..5c4699a 100644 --- a/nip77/negentropy/storage/accumulator.go +++ b/nip77/negentropy/storage/accumulator.go @@ -5,7 +5,7 @@ import ( "encoding/binary" "encoding/hex" - "github.com/nbd-wtf/go-nostr/nip77/negentropy" + "fiatjaf.com/nostrlib/nip77/negentropy" ) type Accumulator struct { diff --git a/nip77/negentropy/storage/vector/vector.go b/nip77/negentropy/storage/vector/vector.go index 13a688e..4f9b3b1 100644 --- a/nip77/negentropy/storage/vector/vector.go +++ b/nip77/negentropy/storage/vector/vector.go @@ -6,9 +6,9 @@ import ( "iter" "slices" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip77/negentropy" - "github.com/nbd-wtf/go-nostr/nip77/negentropy/storage" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/nip77/negentropy" + "fiatjaf.com/nostr/nip77/negentropy/storage" ) type Vector struct { diff --git a/nip77/negentropy/types.go b/nip77/negentropy/types.go index 10b9469..4088206 100644 --- a/nip77/negentropy/types.go +++ b/nip77/negentropy/types.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) const FingerprintSize = 16 diff --git a/nip77/negentropy/whatever_test.go b/nip77/negentropy/whatever_test.go index 3c81115..551593f 100644 --- a/nip77/negentropy/whatever_test.go +++ b/nip77/negentropy/whatever_test.go @@ -6,9 +6,9 @@ import ( "sync" "testing" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip77/negentropy" - "github.com/nbd-wtf/go-nostr/nip77/negentropy/storage/vector" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip77/negentropy" + "fiatjaf.com/nostrlib/nip77/negentropy/storage/vector" "github.com/stretchr/testify/require" ) diff --git a/nip77/nip77.go b/nip77/nip77.go index a4e078c..40d5e11 100644 --- a/nip77/nip77.go +++ b/nip77/nip77.go @@ -5,9 +5,9 @@ import ( "fmt" "sync" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip77/negentropy" - "github.com/nbd-wtf/go-nostr/nip77/negentropy/storage/vector" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip77/negentropy" + "fiatjaf.com/nostrlib/nip77/negentropy/storage/vector" ) type direction struct { diff --git a/nip86/methods.go b/nip86/methods.go index df60614..3e9cfc9 100644 --- a/nip86/methods.go +++ b/nip86/methods.go @@ -5,7 +5,7 @@ import ( "math" "net" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) func DecodeRequest(req Request) (MethodParams, error) { diff --git a/nip92/imeta.go b/nip92/imeta.go index ac1c156..ef79d70 100644 --- a/nip92/imeta.go +++ b/nip92/imeta.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) type IMeta []IMetaEntry diff --git a/nip92/imeta_test.go b/nip92/imeta_test.go index e400748..31660cd 100644 --- a/nip92/imeta_test.go +++ b/nip92/imeta_test.go @@ -3,7 +3,7 @@ package nip92 import ( "testing" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" "github.com/stretchr/testify/require" ) diff --git a/nip94/nip94.go b/nip94/nip94.go index 75a5efb..53b25d6 100644 --- a/nip94/nip94.go +++ b/nip94/nip94.go @@ -3,7 +3,7 @@ package nip94 import ( "strings" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) func ParseFileMetadata(event nostr.Event) FileMetadata { diff --git a/nip96/nip96.go b/nip96/nip96.go index eae7349..5d7bd96 100644 --- a/nip96/nip96.go +++ b/nip96/nip96.go @@ -14,7 +14,7 @@ import ( "strconv" jsoniter "github.com/json-iterator/go" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) // Upload uploads a file to the provided req.Host. diff --git a/nip96/nip96_test.go b/nip96/nip96_test.go index 08d0dd9..bf78f7c 100644 --- a/nip96/nip96_test.go +++ b/nip96/nip96_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/nip96/types.go b/nip96/types.go index 47382e5..2a961ac 100644 --- a/nip96/types.go +++ b/nip96/types.go @@ -5,7 +5,7 @@ import ( "io" "net/http" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) // UploadRequest is a NIP96 upload request. diff --git a/pointers.go b/pointers.go index e20aac6..5ada6cd 100644 --- a/pointers.go +++ b/pointers.go @@ -1,6 +1,7 @@ package nostr import ( + "encoding/hex" "fmt" "strconv" "strings" @@ -30,15 +31,15 @@ var ( // ProfilePointer represents a pointer to a Nostr profile. type ProfilePointer struct { - PublicKey string `json:"pubkey"` + PublicKey PubKey `json:"pubkey"` Relays []string `json:"relays,omitempty"` } // ProfilePointerFromTag creates a ProfilePointer from a "p" tag (but it doesn't have to be necessarily a "p" tag, could be something else). func ProfilePointerFromTag(refTag Tag) (ProfilePointer, error) { - pk := refTag[1] - if !IsValidPublicKey(pk) { - return ProfilePointer{}, fmt.Errorf("invalid pubkey '%s'", pk) + pk, err := PubKeyFromHex(refTag[1]) + if err != nil { + return ProfilePointer{}, err } pointer := ProfilePointer{ @@ -54,29 +55,29 @@ func ProfilePointerFromTag(refTag Tag) (ProfilePointer, error) { // MatchesEvent checks if the pointer matches an event. func (ep ProfilePointer) MatchesEvent(_ Event) bool { return false } -func (ep ProfilePointer) AsTagReference() string { return ep.PublicKey } -func (ep ProfilePointer) AsFilter() Filter { return Filter{Authors: []string{ep.PublicKey}} } +func (ep ProfilePointer) AsFilter() Filter { return Filter{Authors: []PubKey{ep.PublicKey}} } +func (ep ProfilePointer) AsTagReference() string { return hex.EncodeToString(ep.PublicKey[:]) } func (ep ProfilePointer) AsTag() Tag { if len(ep.Relays) > 0 { - return Tag{"p", ep.PublicKey, ep.Relays[0]} + return Tag{"p", hex.EncodeToString(ep.PublicKey[:]), ep.Relays[0]} } - return Tag{"p", ep.PublicKey} + return Tag{"p", hex.EncodeToString(ep.PublicKey[:])} } // EventPointer represents a pointer to a nostr event. type EventPointer struct { - ID string `json:"id"` + ID ID `json:"id"` Relays []string `json:"relays,omitempty"` - Author string `json:"author,omitempty"` - Kind int `json:"kind,omitempty"` + Author PubKey `json:"author,omitempty"` + Kind uint16 `json:"kind,omitempty"` } // EventPointerFromTag creates an EventPointer from an "e" tag (but it could be other tag name, it isn't checked). func EventPointerFromTag(refTag Tag) (EventPointer, error) { - id := refTag[1] - if !IsValid32ByteHex(id) { - return EventPointer{}, fmt.Errorf("invalid id '%s'", id) + id, err := IDFromHex(refTag[1]) + if err != nil { + return EventPointer{}, err } pointer := EventPointer{ @@ -86,35 +87,35 @@ func EventPointerFromTag(refTag Tag) (EventPointer, error) { if relay := (refTag)[2]; IsValidRelayURL(relay) { pointer.Relays = []string{relay} } - if len(refTag) > 3 && IsValidPublicKey(refTag[3]) { - pointer.Author = (refTag)[3] - } else if len(refTag) > 4 && IsValidPublicKey(refTag[4]) { - pointer.Author = (refTag)[4] + if len(refTag) > 3 { + if pk, err := PubKeyFromHex(refTag[3]); err == nil { + pointer.Author = pk + } } } return pointer, nil } func (ep EventPointer) MatchesEvent(evt Event) bool { return evt.ID == ep.ID } -func (ep EventPointer) AsTagReference() string { return ep.ID } -func (ep EventPointer) AsFilter() Filter { return Filter{IDs: []string{ep.ID}} } +func (ep EventPointer) AsFilter() Filter { return Filter{IDs: []ID{ep.ID}} } +func (ep EventPointer) AsTagReference() string { return hex.EncodeToString(ep.ID[:]) } // AsTag converts the pointer to a Tag. func (ep EventPointer) AsTag() Tag { if len(ep.Relays) > 0 { - if ep.Author != "" { - return Tag{"e", ep.ID, ep.Relays[0], ep.Author} + if ep.Author != [32]byte{} { + return Tag{"e", hex.EncodeToString(ep.ID[:]), ep.Relays[0], hex.EncodeToString(ep.Author[:])} } else { - return Tag{"e", ep.ID, ep.Relays[0]} + return Tag{"e", hex.EncodeToString(ep.ID[:]), ep.Relays[0]} } } - return Tag{"e", ep.ID} + return Tag{"e", hex.EncodeToString(ep.ID[:])} } // EntityPointer represents a pointer to a nostr entity (addressable event). type EntityPointer struct { - PublicKey string `json:"pubkey"` - Kind int `json:"kind,omitempty"` + PublicKey PubKey `json:"pubkey"` + Kind uint16 `json:"kind,omitempty"` Identifier string `json:"identifier,omitempty"` Relays []string `json:"relays,omitempty"` } @@ -125,8 +126,10 @@ func EntityPointerFromTag(refTag Tag) (EntityPointer, error) { if len(spl) != 3 { return EntityPointer{}, fmt.Errorf("invalid addr ref '%s'", refTag[1]) } - if !IsValidPublicKey(spl[1]) { - return EntityPointer{}, fmt.Errorf("invalid addr pubkey '%s'", spl[1]) + + pk, err := PubKeyFromHex(spl[1]) + if err != nil { + return EntityPointer{}, err } kind, err := strconv.Atoi(spl[0]) @@ -135,8 +138,8 @@ func EntityPointerFromTag(refTag Tag) (EntityPointer, error) { } pointer := EntityPointer{ - Kind: kind, - PublicKey: spl[1], + Kind: uint16(kind), + PublicKey: pk, Identifier: spl[2], } if len(refTag) > 2 { @@ -161,8 +164,8 @@ func (ep EntityPointer) AsTagReference() string { func (ep EntityPointer) AsFilter() Filter { return Filter{ - Kinds: []int{ep.Kind}, - Authors: []string{ep.PublicKey}, + Kinds: []uint16{ep.Kind}, + Authors: []PubKey{ep.PublicKey}, Tags: TagMap{"d": []string{ep.Identifier}}, } } diff --git a/pool.go b/pool.go index 63c86cc..b006f74 100644 --- a/pool.go +++ b/pool.go @@ -12,7 +12,7 @@ import ( "sync/atomic" "time" - "github.com/nbd-wtf/go-nostr/nip45/hyperloglog" + "fiatjaf.com/nostr/nip45/hyperloglog" "github.com/puzpuzpuz/xsync/v3" ) @@ -29,8 +29,8 @@ type SimplePool struct { cancel context.CancelCauseFunc eventMiddleware func(RelayEvent) - duplicateMiddleware func(relay string, id string) - queryMiddleware func(relay string, pubkey string, kind int) + duplicateMiddleware func(relay string, id ID) + queryMiddleware func(relay string, pubkey PubKey, kind uint16) // custom things not often used penaltyBoxMu sync.Mutex @@ -139,7 +139,7 @@ func (h WithEventMiddleware) ApplyPoolOption(pool *SimplePool) { } // WithDuplicateMiddleware is a function that will be called with all duplicate ids received. -type WithDuplicateMiddleware func(relay string, id string) +type WithDuplicateMiddleware func(relay string, id ID) func (h WithDuplicateMiddleware) ApplyPoolOption(pool *SimplePool) { pool.duplicateMiddleware = h @@ -147,7 +147,7 @@ func (h WithDuplicateMiddleware) ApplyPoolOption(pool *SimplePool) { // WithAuthorKindQueryMiddleware is a function that will be called with every combination of relay+pubkey+kind queried // in a .SubMany*() call -- when applicable (i.e. when the query contains a pubkey and a kind). -type WithAuthorKindQueryMiddleware func(relay string, pubkey string, kind int) +type WithAuthorKindQueryMiddleware func(relay string, pubkey PubKey, kind uint16) func (h WithAuthorKindQueryMiddleware) ApplyPoolOption(pool *SimplePool) { pool.queryMiddleware = h @@ -271,7 +271,7 @@ func (pool *SimplePool) SubscribeMany( filter Filter, opts ...SubscriptionOption, ) chan RelayEvent { - return pool.subMany(ctx, urls, Filters{filter}, nil, opts...) + return pool.subMany(ctx, urls, filter, nil, opts...) } // FetchMany opens a subscription, much like SubscribeMany, but it ends as soon as all Relays @@ -282,17 +282,7 @@ func (pool *SimplePool) FetchMany( filter Filter, opts ...SubscriptionOption, ) chan RelayEvent { - return pool.SubManyEose(ctx, urls, Filters{filter}, opts...) -} - -// Deprecated: SubMany is deprecated: use SubscribeMany instead. -func (pool *SimplePool) SubMany( - ctx context.Context, - urls []string, - filters Filters, - opts ...SubscriptionOption, -) chan RelayEvent { - return pool.subMany(ctx, urls, filters, nil, opts...) + return pool.SubManyEose(ctx, urls, filter, opts...) } // SubscribeManyNotifyEOSE is like SubscribeMany, but takes a channel that is closed when @@ -304,11 +294,11 @@ func (pool *SimplePool) SubscribeManyNotifyEOSE( eoseChan chan struct{}, opts ...SubscriptionOption, ) chan RelayEvent { - return pool.subMany(ctx, urls, Filters{filter}, eoseChan, opts...) + return pool.subMany(ctx, urls, filter, eoseChan, opts...) } type ReplaceableKey struct { - PubKey string + PubKey PubKey D string } @@ -363,7 +353,7 @@ func (pool *SimplePool) FetchManyReplaceable( hasAuthed := false subscribe: - sub, err := relay.Subscribe(ctx, Filters{filter}, opts...) + sub, err := relay.Subscribe(ctx, filter, opts...) if err != nil { debugLogf("error subscribing to %s with %v: %s", relay, filter, err) return @@ -414,14 +404,14 @@ func (pool *SimplePool) FetchManyReplaceable( func (pool *SimplePool) subMany( ctx context.Context, urls []string, - filters Filters, + filter Filter, eoseChan chan struct{}, opts ...SubscriptionOption, ) chan RelayEvent { ctx, cancel := context.WithCancelCause(ctx) _ = cancel // do this so `go vet` will stop complaining events := make(chan RelayEvent) - seenAlready := xsync.NewMapOf[string, Timestamp]() + seenAlready := xsync.NewMapOf[ID, Timestamp]() ticker := time.NewTicker(seenAlreadyDropTick) eoseWg := sync.WaitGroup{} @@ -471,12 +461,10 @@ func (pool *SimplePool) subMany( var sub *Subscription if mh := pool.queryMiddleware; mh != nil { - for _, filter := range filters { - if filter.Kinds != nil && filter.Authors != nil { - for _, kind := range filter.Kinds { - for _, author := range filter.Authors { - mh(nm, author, kind) - } + if filter.Kinds != nil && filter.Authors != nil { + for _, kind := range filter.Kinds { + for _, author := range filter.Authors { + mh(nm, author, kind) } } } @@ -497,13 +485,15 @@ func (pool *SimplePool) subMany( hasAuthed = false subscribe: - sub, err = relay.Subscribe(ctx, filters, append(opts, WithCheckDuplicate(func(id, relay string) bool { - _, exists := seenAlready.Load(id) - if exists && pool.duplicateMiddleware != nil { - pool.duplicateMiddleware(relay, id) - } - return exists - }))...) + sub, err = relay.Subscribe(ctx, filter, append(opts, + WithCheckDuplicate(func(id ID, relay string) bool { + _, exists := seenAlready.Load(id) + if exists && pool.duplicateMiddleware != nil { + pool.duplicateMiddleware(relay, id) + } + return exists + }), + )...) if err != nil { debugLogf("%s reconnecting because subscription died\n", nm) goto reconnect @@ -529,9 +519,7 @@ func (pool *SimplePool) subMany( // so we will update the filters here to include only events seem from now on // and try to reconnect until we succeed now := Now() - for i := range filters { - filters[i].Since = &now - } + filter.Since = &now debugLogf("%s reconnecting because sub.Events is broken\n", nm) goto reconnect } @@ -591,25 +579,26 @@ func (pool *SimplePool) subMany( func (pool *SimplePool) SubManyEose( ctx context.Context, urls []string, - filters Filters, + filter Filter, opts ...SubscriptionOption, ) chan RelayEvent { - seenAlready := xsync.NewMapOf[string, struct{}]() - return pool.subManyEoseNonOverwriteCheckDuplicate(ctx, urls, filters, - WithCheckDuplicate(func(id, relay string) bool { + seenAlready := xsync.NewMapOf[ID, struct{}]() + return pool.subManyEoseNonOverwriteCheckDuplicate(ctx, urls, filter, + WithCheckDuplicate(func(id ID, relay string) bool { _, exists := seenAlready.LoadOrStore(id, struct{}{}) if exists && pool.duplicateMiddleware != nil { pool.duplicateMiddleware(relay, id) } return exists }), - opts...) + opts..., + ) } func (pool *SimplePool) subManyEoseNonOverwriteCheckDuplicate( ctx context.Context, urls []string, - filters Filters, + filter Filter, wcd WithCheckDuplicate, opts ...SubscriptionOption, ) chan RelayEvent { @@ -633,12 +622,10 @@ func (pool *SimplePool) subManyEoseNonOverwriteCheckDuplicate( defer wg.Done() if mh := pool.queryMiddleware; mh != nil { - for _, filter := range filters { - if filter.Kinds != nil && filter.Authors != nil { - for _, kind := range filter.Kinds { - for _, author := range filter.Authors { - mh(nm, author, kind) - } + if filter.Kinds != nil && filter.Authors != nil { + for _, kind := range filter.Kinds { + for _, author := range filter.Authors { + mh(nm, author, kind) } } } @@ -646,16 +633,16 @@ func (pool *SimplePool) subManyEoseNonOverwriteCheckDuplicate( relay, err := pool.EnsureRelay(nm) if err != nil { - debugLogf("error connecting to %s with %v: %s", nm, filters, err) + debugLogf("error connecting to %s with %v: %s", nm, filter, err) return } hasAuthed := false subscribe: - sub, err := relay.Subscribe(ctx, filters, opts...) + sub, err := relay.Subscribe(ctx, filter, opts...) if err != nil { - debugLogf("error subscribing to %s with %v: %s", relay, filters, err) + debugLogf("error subscribing to %s with %v: %s", relay, filter, err) return } @@ -719,7 +706,7 @@ func (pool *SimplePool) CountMany( if err != nil { return } - ce, err := relay.countInternal(ctx, Filters{filter}, opts...) + ce, err := relay.countInternal(ctx, filter, opts...) if err != nil { return } @@ -742,7 +729,7 @@ func (pool *SimplePool) QuerySingle( opts ...SubscriptionOption, ) *RelayEvent { ctx, cancel := context.WithCancelCause(ctx) - for ievt := range pool.SubManyEose(ctx, urls, Filters{filter}, opts...) { + for ievt := range pool.SubManyEose(ctx, urls, filter, opts...) { cancel(errors.New("got the first event and ended successfully")) return &ievt } @@ -759,14 +746,14 @@ func (pool *SimplePool) BatchedSubManyEose( res := make(chan RelayEvent) wg := sync.WaitGroup{} wg.Add(len(dfs)) - seenAlready := xsync.NewMapOf[string, struct{}]() + seenAlready := xsync.NewMapOf[ID, struct{}]() for _, df := range dfs { go func(df DirectedFilter) { for ie := range pool.subManyEoseNonOverwriteCheckDuplicate(ctx, []string{df.Relay}, - Filters{df.Filter}, - WithCheckDuplicate(func(id, relay string) bool { + df.Filter, + WithCheckDuplicate(func(id ID, relay string) bool { _, exists := seenAlready.LoadOrStore(id, struct{}{}) if exists && pool.duplicateMiddleware != nil { pool.duplicateMiddleware(relay, id) diff --git a/relay.go b/relay.go index fb665dd..0bc3123 100644 --- a/relay.go +++ b/relay.go @@ -36,7 +36,7 @@ type Relay struct { challenge string // NIP-42 challenge, we only keep the last noticeHandler func(string) // NIP-01 NOTICEs customHandler func(string) // nonstandard unparseable messages - okCallbacks *xsync.MapOf[string, func(bool, string)] + okCallbacks *xsync.MapOf[ID, func(bool, string)] writeQueue chan writeRequest subscriptionChannelCloseQueue chan *Subscription @@ -58,7 +58,7 @@ func NewRelay(ctx context.Context, url string, opts ...RelayOption) *Relay { connectionContext: ctx, connectionContextCancel: cancel, Subscriptions: xsync.NewMapOf[int64, *Subscription](), - okCallbacks: xsync.NewMapOf[string, func(bool, string)](), + okCallbacks: xsync.NewMapOf[ID, func(bool, string)](), writeQueue: make(chan writeRequest), subscriptionChannelCloseQueue: make(chan *Subscription), requestHeader: nil, @@ -351,7 +351,8 @@ func (r *Relay) Auth(ctx context.Context, sign func(event *Event) error) error { return r.publish(ctx, authEvent.ID, &AuthEnvelope{Event: authEvent}) } -func (r *Relay) publish(ctx context.Context, id string, env Envelope) error { +// publish can be used both for EVENT and for AUTH +func (r *Relay) publish(ctx context.Context, id ID, env Envelope) error { var err error var cancel context.CancelFunc diff --git a/relay_js_test.go b/relay_js_test.go index c278a2e..301effc 100644 --- a/relay_js_test.go +++ b/relay_js_test.go @@ -57,12 +57,11 @@ func TestPublish(t *testing.T) { assert.NoError(t, err) } -func makeKeyPair(t *testing.T) (priv, pub string) { +func makeKeyPair(t *testing.T) (priv, pub [32]byte) { t.Helper() privkey := GeneratePrivateKey() - pubkey, err := GetPublicKey(privkey) - assert.NoError(t, err) + pubkey := GetPublicKey(privkey) return privkey, pubkey } diff --git a/relay_test.go b/relay_test.go index acf2f18..f6ad059 100644 --- a/relay_test.go +++ b/relay_test.go @@ -74,7 +74,7 @@ func TestPublishBlocked(t *testing.T) { assert.NoError(t, err) // send back a not ok nip-20 command result - res := []any{"OK", textNote.ID, false, "blocked"} + res := []any{"OK", textNote.ID.String(), false, "blocked"} websocket.JSON.Send(conn, res) }) defer ws.Close() @@ -175,12 +175,11 @@ var anyOriginHandshake = func(conf *websocket.Config, r *http.Request) error { return nil } -func makeKeyPair(t *testing.T) (priv, pub string) { +func makeKeyPair(t *testing.T) (priv, pub [32]byte) { t.Helper() privkey := GeneratePrivateKey() - pubkey, err := GetPublicKey(privkey) - assert.NoError(t, err) + pubkey := GetPublicKey(privkey) return privkey, pubkey } diff --git a/sdk/addressable_loader.go b/sdk/addressable_loader.go index d4c678f..478304a 100644 --- a/sdk/addressable_loader.go +++ b/sdk/addressable_loader.go @@ -5,8 +5,8 @@ import ( "sync" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/dataloader" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/sdk/dataloader" ) // this is similar to replaceable_loader and reuses logic from that. @@ -21,16 +21,16 @@ const ( ) func (sys *System) initializeAddressableDataloaders() { - sys.addressableLoaders = make([]*dataloader.Loader[string, []*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 int) *dataloader.Loader[string, []*nostr.Event] { +func (sys *System) createAddressableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, []*nostr.Event] { return dataloader.NewBatchedLoader( - func(ctxs []context.Context, pubkeys []string) map[string]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{ @@ -42,11 +42,11 @@ func (sys *System) createAddressableDataloader(kind int) *dataloader.Loader[stri func (sys *System) batchLoadAddressableEvents( ctxs []context.Context, - kind int, - pubkeys []string, -) map[string]dataloader.Result[[]*nostr.Event] { + kind uint16, + pubkeys []nostr.PubKey, +) map[nostr.PubKey]dataloader.Result[[]*nostr.Event] { batchSize := len(pubkeys) - results := make(map[string]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)) @@ -62,7 +62,7 @@ func (sys *System) batchLoadAddressableEvents( defer cancel() // build batched queries for the external relays - go func(i int, pubkey string) { + go func(i int, pubkey nostr.PubKey) { // gather relays we'll use for this pubkey relays := sys.determineRelaysToQuery(ctx, pubkey, kind) @@ -77,8 +77,8 @@ func (sys *System) batchLoadAddressableEvents( dfilter = nostr.DirectedFilter{ Relay: relay, Filter: nostr.Filter{ - Kinds: []int{kind}, - Authors: make([]string, 0, batchSize-i /* this and all pubkeys after this can be added */), + Kinds: []uint16{kind}, + Authors: make([]nostr.PubKey, 0, batchSize-i /* this and all pubkeys after this can be added */), }, } idx = len(relayFilter) diff --git a/sdk/cache/interface.go b/sdk/cache/interface.go index 5153913..c844f72 100644 --- a/sdk/cache/interface.go +++ b/sdk/cache/interface.go @@ -3,8 +3,8 @@ package cache import "time" type Cache32[V any] interface { - Get(k string) (v V, ok bool) - Delete(k string) - Set(k string, v V) bool - SetWithTTL(k string, v V, d time.Duration) bool + Get(k [32]byte) (v V, ok bool) + Delete(k [32]byte) + Set(k [32]byte, v V) bool + SetWithTTL(k [32]byte, v V, d time.Duration) bool } diff --git a/sdk/cache/memory/cache.go b/sdk/cache/memory/cache.go index 9dc771c..97d1cac 100644 --- a/sdk/cache/memory/cache.go +++ b/sdk/cache/memory/cache.go @@ -2,47 +2,33 @@ package cache_memory import ( "encoding/binary" - "encoding/hex" "time" "github.com/dgraph-io/ristretto" ) type RistrettoCache[V any] struct { - Cache *ristretto.Cache[string, V] + Cache *ristretto.Cache[uint64, V] } -func New32[V any](max int64) *RistrettoCache[V] { - cache, _ := ristretto.NewCache(&ristretto.Config[string, V]{ +func New[V any](max int64) *RistrettoCache[V] { + cache, _ := ristretto.NewCache(&ristretto.Config[uint64, V]{ NumCounters: max * 10, MaxCost: max, BufferItems: 64, - KeyToHash: func(key string) (uint64, uint64) { return h32(key), 0 }, + KeyToHash: func(key uint64) (uint64, uint64) { return key, 0 }, }) return &RistrettoCache[V]{Cache: cache} } -func (s RistrettoCache[V]) Get(k string) (v V, ok bool) { return s.Cache.Get(k) } -func (s RistrettoCache[V]) Delete(k string) { s.Cache.Del(k) } -func (s RistrettoCache[V]) Set(k string, v V) bool { return s.Cache.Set(k, v, 1) } -func (s RistrettoCache[V]) SetWithTTL(k string, v V, d time.Duration) bool { - return s.Cache.SetWithTTL(k, v, 1, d) +func (s RistrettoCache[V]) Get(k [32]byte) (v V, ok bool) { + return s.Cache.Get(binary.BigEndian.Uint64(k[32-8:])) +} +func (s RistrettoCache[V]) Delete(k [32]byte) { s.Cache.Del(binary.BigEndian.Uint64(k[32-8:])) } +func (s RistrettoCache[V]) Set(k [32]byte, v V) bool { + return s.Cache.Set(binary.BigEndian.Uint64(k[32-8:]), v, 1) } -func h32(key string) uint64 { - // we get an event id or pubkey as hex, - // so just extract the last 8 bytes from it and turn them into a uint64 - return shortUint64(key) -} - -func shortUint64(idOrPubkey string) uint64 { - length := len(idOrPubkey) - if length < 8 { - return 0 - } - b, err := hex.DecodeString(idOrPubkey[length-8:]) - if err != nil { - return 0 - } - return uint64(binary.BigEndian.Uint32(b)) +func (s RistrettoCache[V]) SetWithTTL(k [32]byte, v V, d time.Duration) bool { + return s.Cache.SetWithTTL(binary.BigEndian.Uint64(k[32-8:]), v, 1, d) } diff --git a/sdk/event_relays.go b/sdk/event_relays.go index aedc687..db9d3cb 100644 --- a/sdk/event_relays.go +++ b/sdk/event_relays.go @@ -1,22 +1,21 @@ package sdk import ( - "encoding/hex" - "fmt" "slices" - "github.com/nbd-wtf/go-nostr/sdk/kvstore" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/sdk/kvstore" ) const eventRelayPrefix = byte('r') // makeEventRelayKey creates a key for storing event relay information. // It uses the first 8 bytes of the event ID to create a compact key. -func makeEventRelayKey(eventID []byte) []byte { +func makeEventRelayKey(id nostr.ID) []byte { // format: 'r' + first 8 bytes of event ID key := make([]byte, 9) key[0] = eventRelayPrefix - copy(key[1:], eventID[:8]) + copy(key[1:], id[:8]) return key } @@ -75,15 +74,9 @@ func decodeRelayList(data []byte) []string { // trackEventRelay records that an event was seen on a particular relay. // If onlyIfItExists is true, it will only update existing records and not create new ones. -func (sys *System) trackEventRelay(eventID string, relay string, onlyIfItExists bool) { - // decode the event ID hex into bytes - idBytes, err := hex.DecodeString(eventID) - if err != nil || len(idBytes) < 8 { - return - } - +func (sys *System) trackEventRelay(id nostr.ID, relay string, onlyIfItExists bool) { // get the key for this event - key := makeEventRelayKey(idBytes) + key := makeEventRelayKey(id) // update the relay list atomically sys.KVStore.Update(key, func(data []byte) ([]byte, error) { @@ -111,15 +104,9 @@ func (sys *System) trackEventRelay(eventID string, relay string, onlyIfItExists // GetEventRelays returns all known relay URLs an event is known to be available on. // It is based on information kept on KVStore. -func (sys *System) GetEventRelays(eventID string) ([]string, error) { - // decode the event ID hex into bytes - idBytes, err := hex.DecodeString(eventID) - if err != nil || len(idBytes) < 8 { - return nil, fmt.Errorf("invalid event id") - } - +func (sys *System) GetEventRelays(id nostr.ID) ([]string, error) { // get the key for this event - key := makeEventRelayKey(idBytes) + key := makeEventRelayKey(id) // get stored relay list data, err := sys.KVStore.Get(key) diff --git a/sdk/feeds.go b/sdk/feeds.go index 5333cd3..e711152 100644 --- a/sdk/feeds.go +++ b/sdk/feeds.go @@ -8,7 +8,7 @@ import ( "sync" "sync/atomic" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostr" ) const ( @@ -29,7 +29,7 @@ func makePubkeyStreamKey(prefix byte, pubkey string) []byte { // each pubkey (stored in KVStore) onwards. func (sys *System) StreamLiveFeed( ctx context.Context, - pubkeys []string, + pubkeys []nostr.PubKey, kinds []int, ) (<-chan *nostr.Event, error) { events := make(chan *nostr.Event) diff --git a/sdk/feeds_test.go b/sdk/feeds_test.go index 8452b91..2fdb112 100644 --- a/sdk/feeds_test.go +++ b/sdk/feeds_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" + "fiatjaf.com/nostr" "github.com/fiatjaf/eventstore/slicestore" "github.com/fiatjaf/khatru" - "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/require" ) @@ -56,9 +56,9 @@ func TestStreamLiveFeed(t *testing.T) { // generate two random keypairs for testing sk1 := nostr.GeneratePrivateKey() - pk1, _ := nostr.GetPublicKey(sk1) + pk1 := nostr.GetPublicKey(sk1) sk2 := nostr.GeneratePrivateKey() - pk2, _ := nostr.GetPublicKey(sk2) + pk2 := nostr.GetPublicKey(sk2) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -131,7 +131,7 @@ func TestStreamLiveFeed(t *testing.T) { go sys.Pool.PublishMany(ctx, []string{"ws://localhost:48482", "ws://localhost:48483"}, evt2) // start streaming events for both pubkeys - events, err := sys.StreamLiveFeed(ctx, []string{pk1, pk2}, []int{1}) + events, err := sys.StreamLiveFeed(ctx, []nostr.PubKey{pk1, pk2}, []int{1}) if err != nil { t.Fatalf("failed to start streaming: %v", err) } diff --git a/sdk/hints/badgerh/db.go b/sdk/hints/badgerh/db.go index 6936101..8d61ea5 100644 --- a/sdk/hints/badgerh/db.go +++ b/sdk/hints/badgerh/db.go @@ -1,14 +1,13 @@ package badgerh import ( - "encoding/hex" "fmt" "math" "slices" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/sdk/hints" "github.com/dgraph-io/badger/v4" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/hints" ) var _ hints.HintsDB = (*BadgerHints)(nil) @@ -30,7 +29,7 @@ func (bh *BadgerHints) Close() { bh.db.Close() } -func (bh *BadgerHints) Save(pubkey string, relay string, hintkey hints.HintKey, ts nostr.Timestamp) { +func (bh *BadgerHints) Save(pubkey nostr.PubKey, relay string, hintkey hints.HintKey, ts nostr.Timestamp) { if now := nostr.Now(); ts > now { ts = now } @@ -64,7 +63,7 @@ func (bh *BadgerHints) Save(pubkey string, relay string, hintkey hints.HintKey, } } -func (bh *BadgerHints) TopN(pubkey string, n int) []string { +func (bh *BadgerHints) TopN(pubkey nostr.PubKey, n int) []string { type relayScore struct { relay string score int64 @@ -73,7 +72,7 @@ func (bh *BadgerHints) TopN(pubkey string, n int) []string { scores := make([]relayScore, 0, n) err := bh.db.View(func(txn *badger.Txn) error { opts := badger.DefaultIteratorOptions - opts.Prefix, _ = hex.DecodeString(pubkey) + opts.Prefix = pubkey[:] it := txn.NewIterator(opts) defer it.Close() @@ -112,7 +111,7 @@ func (bh *BadgerHints) TopN(pubkey string, n int) []string { return result } -func (bh *BadgerHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores { +func (bh *BadgerHints) GetDetailedScores(pubkey nostr.PubKey, n int) []hints.RelayScores { type relayScore struct { relay string tss timestamps @@ -121,11 +120,10 @@ func (bh *BadgerHints) GetDetailedScores(pubkey string, n int) []hints.RelayScor scores := make([]relayScore, 0, n) err := bh.db.View(func(txn *badger.Txn) error { - prefix, _ := hex.DecodeString(pubkey) it := txn.NewIterator(badger.DefaultIteratorOptions) defer it.Close() - for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + for it.Seek(pubkey[:]); it.ValidForPrefix(pubkey[:]); it.Next() { item := it.Item() k := item.Key() relay := string(k[32:]) @@ -172,7 +170,7 @@ func (bh *BadgerHints) PrintScores() { it := txn.NewIterator(badger.DefaultIteratorOptions) defer it.Close() - var lastPubkey string + var lastPubkey nostr.PubKey i := 0 for it.Seek(nil); it.Valid(); it.Next() { diff --git a/sdk/hints/badgerh/keys.go b/sdk/hints/badgerh/keys.go index 3673a4b..077c971 100644 --- a/sdk/hints/badgerh/keys.go +++ b/sdk/hints/badgerh/keys.go @@ -2,20 +2,19 @@ package badgerh import ( "encoding/binary" - "encoding/hex" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostr" ) -func encodeKey(pubhintkey, relay string) []byte { +func encodeKey(pubhintkey nostr.PubKey, relay string) []byte { k := make([]byte, 32+len(relay)) - hex.Decode(k[0:32], []byte(pubhintkey)) + copy(k[0:32], pubhintkey[:]) copy(k[32:], relay) return k } -func parseKey(k []byte) (pubkey string, relay string) { - pubkey = hex.EncodeToString(k[0:32]) +func parseKey(k []byte) (pubkey nostr.PubKey, relay string) { + pubkey = [32]byte(k[0:32]) relay = string(k[32:]) return } diff --git a/sdk/hints/interface.go b/sdk/hints/interface.go index bb5c523..f8b7108 100644 --- a/sdk/hints/interface.go +++ b/sdk/hints/interface.go @@ -1,6 +1,8 @@ package hints -import "github.com/nbd-wtf/go-nostr" +import ( + "fiatjaf.com/nostr" +) type RelayScores struct { Relay string @@ -9,8 +11,8 @@ type RelayScores struct { } type HintsDB interface { - TopN(pubkey string, n int) []string - Save(pubkey string, relay string, key HintKey, score nostr.Timestamp) + TopN(pubkey nostr.PubKey, n int) []string + Save(pubkey nostr.PubKey, relay string, key HintKey, score nostr.Timestamp) PrintScores() - GetDetailedScores(pubkey string, n int) []RelayScores + GetDetailedScores(pubkey nostr.PubKey, n int) []RelayScores } diff --git a/sdk/hints/keys.go b/sdk/hints/keys.go index 022b230..e12a17f 100644 --- a/sdk/hints/keys.go +++ b/sdk/hints/keys.go @@ -1,6 +1,6 @@ package hints -import "github.com/nbd-wtf/go-nostr" +import "fiatjaf.com/nostrlib" const END_OF_WORLD nostr.Timestamp = 2208999600 // 2040-01-01 diff --git a/sdk/hints/memoryh/db.go b/sdk/hints/memoryh/db.go index 573626f..6e31dfe 100644 --- a/sdk/hints/memoryh/db.go +++ b/sdk/hints/memoryh/db.go @@ -6,8 +6,8 @@ import ( "slices" "sync" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/hints" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/sdk/hints" ) var _ hints.HintsDB = (*HintDB)(nil) diff --git a/sdk/hints/sqlh/db.go b/sdk/hints/sqlh/db.go index 491ae18..b510fb2 100644 --- a/sdk/hints/sqlh/db.go +++ b/sdk/hints/sqlh/db.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/jmoiron/sqlx" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/hints" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/sdk/hints" ) type SQLHints struct { diff --git a/sdk/hints/test/libsql_test.go b/sdk/hints/test/libsql_test.go index a221652..565d152 100644 --- a/sdk/hints/test/libsql_test.go +++ b/sdk/hints/test/libsql_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/nbd-wtf/go-nostr/sdk/hints/sqlh" + "fiatjaf.com/nostrlib/sdk/hints/sqlh" "github.com/stretchr/testify/require" _ "github.com/tursodatabase/go-libsql" ) diff --git a/sdk/hints/test/mattnsqlite_test.go b/sdk/hints/test/mattnsqlite_test.go index 13569c4..d61cbeb 100644 --- a/sdk/hints/test/mattnsqlite_test.go +++ b/sdk/hints/test/mattnsqlite_test.go @@ -8,7 +8,7 @@ import ( "testing" _ "github.com/mattn/go-sqlite3" - "github.com/nbd-wtf/go-nostr/sdk/hints/sqlh" + "fiatjaf.com/nostrlib/sdk/hints/sqlh" "github.com/stretchr/testify/require" ) diff --git a/sdk/hints/test/memory_test.go b/sdk/hints/test/memory_test.go index 6611659..cd456cc 100644 --- a/sdk/hints/test/memory_test.go +++ b/sdk/hints/test/memory_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/nbd-wtf/go-nostr/sdk/hints/memoryh" + "fiatjaf.com/nostrlib/sdk/hints/memoryh" ) func TestMemoryHints(t *testing.T) { diff --git a/sdk/hints/test/moderncsqlite_test.go b/sdk/hints/test/moderncsqlite_test.go index 6f32dfc..727b379 100644 --- a/sdk/hints/test/moderncsqlite_test.go +++ b/sdk/hints/test/moderncsqlite_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/nbd-wtf/go-nostr/sdk/hints/sqlh" + "fiatjaf.com/nostrlib/sdk/hints/sqlh" "github.com/stretchr/testify/require" _ "modernc.org/sqlite" ) diff --git a/sdk/hints/test/suite.go b/sdk/hints/test/suite.go index b8da7af..dbbc113 100644 --- a/sdk/hints/test/suite.go +++ b/sdk/hints/test/suite.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/hints" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/sdk/hints" "github.com/stretchr/testify/require" ) diff --git a/sdk/hints/test/wasmsqlite_test.go b/sdk/hints/test/wasmsqlite_test.go index c4dbba6..b6c1062 100644 --- a/sdk/hints/test/wasmsqlite_test.go +++ b/sdk/hints/test/wasmsqlite_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/nbd-wtf/go-nostr/sdk/hints/sqlh" + "fiatjaf.com/nostrlib/sdk/hints/sqlh" _ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" "github.com/stretchr/testify/require" diff --git a/sdk/input.go b/sdk/input.go index bade2a7..17d07be 100644 --- a/sdk/input.go +++ b/sdk/input.go @@ -4,9 +4,9 @@ import ( "context" "encoding/hex" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip05" - "github.com/nbd-wtf/go-nostr/nip19" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip05" + "fiatjaf.com/nostrlib/nip19" ) // InputToProfile turns any npub/nprofile/hex/nip05 input into a ProfilePointer (or nil). diff --git a/sdk/kvstore/badger/store.go b/sdk/kvstore/badger/store.go index 9ad7db2..25984cb 100644 --- a/sdk/kvstore/badger/store.go +++ b/sdk/kvstore/badger/store.go @@ -2,7 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v4" - "github.com/nbd-wtf/go-nostr/sdk/kvstore" + "fiatjaf.com/nostrlib/sdk/kvstore" ) var _ kvstore.KVStore = (*Store)(nil) diff --git a/sdk/kvstore/lmdb/store.go b/sdk/kvstore/lmdb/store.go index 189c793..de49717 100644 --- a/sdk/kvstore/lmdb/store.go +++ b/sdk/kvstore/lmdb/store.go @@ -4,7 +4,7 @@ import ( "os" "github.com/PowerDNS/lmdb-go/lmdb" - "github.com/nbd-wtf/go-nostr/sdk/kvstore" + "fiatjaf.com/nostrlib/sdk/kvstore" ) var _ kvstore.KVStore = (*Store)(nil) diff --git a/sdk/kvstore/memory/store.go b/sdk/kvstore/memory/store.go index 3f5edb1..c8a5547 100644 --- a/sdk/kvstore/memory/store.go +++ b/sdk/kvstore/memory/store.go @@ -3,7 +3,7 @@ package memory import ( "sync" - "github.com/nbd-wtf/go-nostr/sdk/kvstore" + "fiatjaf.com/nostrlib/sdk/kvstore" ) var _ kvstore.KVStore = (*Store)(nil) diff --git a/sdk/list.go b/sdk/list.go index 54155e2..2219c71 100644 --- a/sdk/list.go +++ b/sdk/list.go @@ -3,23 +3,22 @@ package sdk import ( "context" "slices" - "strconv" "sync" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/cache" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/sdk/cache" ) type GenericList[I TagItemWithValue] struct { - PubKey string `json:"-"` // must always be set otherwise things will break + 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() string + Value() any } var ( @@ -30,8 +29,8 @@ var ( func fetchGenericList[I TagItemWithValue]( sys *System, ctx context.Context, - pubkey string, - actualKind int, + pubkey nostr.PubKey, + actualKind uint16, replaceableIndex replaceableIndex, parseTag func(nostr.Tag) (I, bool), cache cache.Cache32[GenericList[I]], @@ -39,8 +38,8 @@ func fetchGenericList[I TagItemWithValue]( // 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 - n, _ := strconv.ParseUint(pubkey[14:16], 16, 8) - lockIdx := (n + uint64(actualKind)) % 60 + n := pubkey[7] + lockIdx := (uint16(n) + actualKind) % 60 genericListMutexes[lockIdx].Lock() if valueWasJustCached[lockIdx] { @@ -58,7 +57,7 @@ func fetchGenericList[I TagItemWithValue]( v := GenericList[I]{PubKey: pubkey} - events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []int{actualKind}, Authors: []string{pubkey}}) + events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}) if len(events) != 0 { // ok, we found something locally items := parseItemsFromEventTags(events[0], parseTag) @@ -104,7 +103,7 @@ func fetchGenericList[I TagItemWithValue]( func tryFetchListFromNetwork[I TagItemWithValue]( ctx context.Context, sys *System, - pubkey string, + pubkey nostr.PubKey, replaceableIndex replaceableIndex, parseTag func(nostr.Tag) (I, bool), ) *GenericList[I] { @@ -140,7 +139,7 @@ func parseItemsFromEventTags[I TagItemWithValue]( return result } -func getLocalStoreRefreshDaysForKind(kind int) nostr.Timestamp { +func getLocalStoreRefreshDaysForKind(kind uint16) nostr.Timestamp { switch kind { case 0: return 7 diff --git a/sdk/lists_event.go b/sdk/lists_event.go index 1a18005..4624b49 100644 --- a/sdk/lists_event.go +++ b/sdk/lists_event.go @@ -2,11 +2,9 @@ package sdk import ( "context" - "strconv" - "strings" - "github.com/nbd-wtf/go-nostr" - cache_memory "github.com/nbd-wtf/go-nostr/sdk/cache/memory" + "fiatjaf.com/nostr" + cache_memory "fiatjaf.com/nostr/sdk/cache/memory" ) type EventRef struct{ nostr.Pointer } @@ -15,7 +13,7 @@ func (e EventRef) Value() string { return e.Pointer.AsTagReference() } func (sys *System) FetchBookmarkList(ctx context.Context, pubkey string) GenericList[EventRef] { if sys.BookmarkListCache == nil { - sys.BookmarkListCache = cache_memory.New32[GenericList[EventRef]](1000) + sys.BookmarkListCache = cache_memory.New[GenericList[EventRef]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10003, kind_10003, parseEventRef, sys.BookmarkListCache) @@ -24,7 +22,7 @@ func (sys *System) FetchBookmarkList(ctx context.Context, pubkey string) Generic func (sys *System) FetchPinList(ctx context.Context, pubkey string) GenericList[EventRef] { if sys.PinListCache == nil { - sys.PinListCache = cache_memory.New32[GenericList[EventRef]](1000) + sys.PinListCache = cache_memory.New[GenericList[EventRef]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10001, kind_10001, parseEventRef, sys.PinListCache) @@ -37,36 +35,16 @@ func parseEventRef(tag nostr.Tag) (evr EventRef, ok bool) { } switch tag[0] { case "e": - if !nostr.IsValid32ByteHex(tag[1]) { + pointer, err := nostr.EventPointerFromTag(tag) + if err != nil { return evr, false } - pointer := nostr.EventPointer{ - ID: tag[1], - } - if len(tag) >= 3 { - pointer.Relays = []string{nostr.NormalizeURL(tag[2])} - if len(tag) >= 4 { - pointer.Author = tag[3] - } - } evr.Pointer = pointer case "a": - spl := strings.SplitN(tag[1], ":", 3) - if len(spl) != 3 || !nostr.IsValidPublicKey(spl[1]) { + pointer, err := nostr.EntityPointerFromTag(tag) + if err != nil { return evr, false } - pointer := nostr.EntityPointer{ - PublicKey: spl[1], - Identifier: spl[2], - } - if kind, err := strconv.Atoi(spl[0]); err != nil { - return evr, false - } else { - pointer.Kind = kind - } - if len(tag) >= 3 { - pointer.Relays = []string{nostr.NormalizeURL(tag[2])} - } evr.Pointer = pointer default: return evr, false diff --git a/sdk/lists_profile.go b/sdk/lists_profile.go index 972fa93..ad10e66 100644 --- a/sdk/lists_profile.go +++ b/sdk/lists_profile.go @@ -5,39 +5,39 @@ import ( "net/url" "strings" - "github.com/nbd-wtf/go-nostr" - cache_memory "github.com/nbd-wtf/go-nostr/sdk/cache/memory" + "fiatjaf.com/nostr" + cache_memory "fiatjaf.com/nostr/sdk/cache/memory" ) type ProfileRef struct { - Pubkey string + Pubkey nostr.PubKey Relay string Petname string } -func (f ProfileRef) Value() string { return f.Pubkey } +func (f ProfileRef) Value() nostr.PubKey { return f.Pubkey } -func (sys *System) FetchFollowList(ctx context.Context, pubkey string) GenericList[ProfileRef] { +func (sys *System) FetchFollowList(ctx context.Context, pubkey nostr.PubKey) GenericList[ProfileRef] { if sys.FollowListCache == nil { - sys.FollowListCache = cache_memory.New32[GenericList[ProfileRef]](1000) + sys.FollowListCache = cache_memory.New[GenericList[ProfileRef]](1000) } fl, _ := fetchGenericList(sys, ctx, pubkey, 3, kind_3, parseProfileRef, sys.FollowListCache) return fl } -func (sys *System) FetchMuteList(ctx context.Context, pubkey string) GenericList[ProfileRef] { +func (sys *System) FetchMuteList(ctx context.Context, pubkey nostr.PubKey) GenericList[ProfileRef] { if sys.MuteListCache == nil { - sys.MuteListCache = cache_memory.New32[GenericList[ProfileRef]](1000) + sys.MuteListCache = cache_memory.New[GenericList[ProfileRef]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10000, kind_10000, parseProfileRef, sys.MuteListCache) return ml } -func (sys *System) FetchFollowSets(ctx context.Context, pubkey string) GenericSets[ProfileRef] { +func (sys *System) FetchFollowSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[ProfileRef] { if sys.FollowSetsCache == nil { - sys.FollowSetsCache = cache_memory.New32[GenericSets[ProfileRef]](1000) + sys.FollowSetsCache = cache_memory.New[GenericSets[ProfileRef]](1000) } ml, _ := fetchGenericSets(sys, ctx, pubkey, 30000, kind_30000, parseProfileRef, sys.FollowSetsCache) @@ -52,11 +52,13 @@ func parseProfileRef(tag nostr.Tag) (fw ProfileRef, ok bool) { return fw, false } - fw.Pubkey = tag[1] - if !nostr.IsValidPublicKey(fw.Pubkey) { + pubkey, err := nostr.PubKeyFromHex(fw.Pubkey) + if err != nil { return fw, false } + fw.Pubkey = pubkey + if len(tag) > 2 { if _, err := url.Parse(tag[2]); err == nil { fw.Relay = nostr.NormalizeURL(tag[2]) diff --git a/sdk/lists_relay.go b/sdk/lists_relay.go index 47f11be..68fd70f 100644 --- a/sdk/lists_relay.go +++ b/sdk/lists_relay.go @@ -3,8 +3,8 @@ package sdk import ( "context" - "github.com/nbd-wtf/go-nostr" - cache_memory "github.com/nbd-wtf/go-nostr/sdk/cache/memory" + "fiatjaf.com/nostr" + cache_memory "fiatjaf.com/nostr/sdk/cache/memory" ) type Relay struct { @@ -19,32 +19,32 @@ type RelayURL string func (r RelayURL) Value() string { return string(r) } -func (sys *System) FetchRelayList(ctx context.Context, pubkey string) GenericList[Relay] { +func (sys *System) FetchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[Relay] { ml, _ := fetchGenericList(sys, ctx, pubkey, 10002, kind_10002, parseRelayFromKind10002, sys.RelayListCache) return ml } -func (sys *System) FetchBlockedRelayList(ctx context.Context, pubkey string) GenericList[RelayURL] { +func (sys *System) FetchBlockedRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[RelayURL] { if sys.BlockedRelayListCache == nil { - sys.BlockedRelayListCache = cache_memory.New32[GenericList[RelayURL]](1000) + sys.BlockedRelayListCache = cache_memory.New[GenericList[RelayURL]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10006, kind_10006, parseRelayURL, sys.BlockedRelayListCache) return ml } -func (sys *System) FetchSearchRelayList(ctx context.Context, pubkey string) GenericList[RelayURL] { +func (sys *System) FetchSearchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[RelayURL] { if sys.SearchRelayListCache == nil { - sys.SearchRelayListCache = cache_memory.New32[GenericList[RelayURL]](1000) + sys.SearchRelayListCache = cache_memory.New[GenericList[RelayURL]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10007, kind_10007, parseRelayURL, sys.SearchRelayListCache) return ml } -func (sys *System) FetchRelaySets(ctx context.Context, pubkey string) GenericSets[RelayURL] { +func (sys *System) FetchRelaySets(ctx context.Context, pubkey nostr.PubKey) GenericSets[RelayURL] { if sys.RelaySetsCache == nil { - sys.RelaySetsCache = cache_memory.New32[GenericSets[RelayURL]](1000) + sys.RelaySetsCache = cache_memory.New[GenericSets[RelayURL]](1000) } ml, _ := fetchGenericSets(sys, ctx, pubkey, 30002, kind_30002, parseRelayURL, sys.RelaySetsCache) @@ -52,7 +52,11 @@ func (sys *System) FetchRelaySets(ctx context.Context, pubkey string) GenericSet } func parseRelayFromKind10002(tag nostr.Tag) (rl Relay, ok bool) { - if u := tag.Value(); u != "" && tag[0] == "r" { + if len(tag) < 2 { + return rl, false + } + + if u := tag[1]; u != "" && tag[0] == "r" { if !nostr.IsValidRelayURL(u) { return rl, false } @@ -78,7 +82,11 @@ func parseRelayFromKind10002(tag nostr.Tag) (rl Relay, ok bool) { } func parseRelayURL(tag nostr.Tag) (rl RelayURL, ok bool) { - if u := tag.Value(); u != "" && tag[0] == "relay" { + if len(tag) < 2 { + return rl, false + } + + if u := tag[1]; u != "" && tag[0] == "relay" { if !nostr.IsValidRelayURL(u) { return rl, false } diff --git a/sdk/lists_topics.go b/sdk/lists_topics.go index fd57a85..16ffd69 100644 --- a/sdk/lists_topics.go +++ b/sdk/lists_topics.go @@ -3,26 +3,26 @@ package sdk import ( "context" - "github.com/nbd-wtf/go-nostr" - cache_memory "github.com/nbd-wtf/go-nostr/sdk/cache/memory" + "fiatjaf.com/nostr" + cache_memory "fiatjaf.com/nostr/sdk/cache/memory" ) type Topic string func (r Topic) Value() string { return string(r) } -func (sys *System) FetchTopicList(ctx context.Context, pubkey string) GenericList[Topic] { +func (sys *System) FetchTopicList(ctx context.Context, pubkey nostr.PubKey) GenericList[Topic] { if sys.TopicListCache == nil { - sys.TopicListCache = cache_memory.New32[GenericList[Topic]](1000) + sys.TopicListCache = cache_memory.New[GenericList[Topic]](1000) } ml, _ := fetchGenericList(sys, ctx, pubkey, 10015, kind_10015, parseTopicString, sys.TopicListCache) return ml } -func (sys *System) FetchTopicSets(ctx context.Context, pubkey string) GenericSets[Topic] { +func (sys *System) FetchTopicSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[Topic] { if sys.TopicSetsCache == nil { - sys.TopicSetsCache = cache_memory.New32[GenericSets[Topic]](1000) + sys.TopicSetsCache = cache_memory.New[GenericSets[Topic]](1000) } ml, _ := fetchGenericSets(sys, ctx, pubkey, 30015, kind_30015, parseTopicString, sys.TopicSetsCache) @@ -30,8 +30,12 @@ func (sys *System) FetchTopicSets(ctx context.Context, pubkey string) GenericSet } func parseTopicString(tag nostr.Tag) (t Topic, ok bool) { - if t := tag.Value(); t != "" && tag[0] == "t" { + if len(tag) < 2 { + return t, false + } + if t := tag[1]; t != "" && tag[0] == "t" { return Topic(t), true } + return t, false } diff --git a/sdk/loader_helpers.go b/sdk/loader_helpers.go index 1766163..f9da927 100644 --- a/sdk/loader_helpers.go +++ b/sdk/loader_helpers.go @@ -2,18 +2,17 @@ package sdk import ( "encoding/binary" - "encoding/hex" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostr" ) var kvStoreLastFetchPrefix = byte('f') -func makeLastFetchKey(kind int, pubkey string) []byte { +func makeLastFetchKey(kind uint16, pubkey nostr.PubKey) []byte { buf := make([]byte, 1+5+32) buf[0] = kvStoreLastFetchPrefix binary.LittleEndian.PutUint32(buf[1:], uint32(kind)) - hex.Decode(buf[5:], []byte(pubkey)) + copy(buf[5:], pubkey[:]) return buf } diff --git a/sdk/metadata.go b/sdk/metadata.go index 3337bc2..dc2a768 100644 --- a/sdk/metadata.go +++ b/sdk/metadata.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip05" - "github.com/nbd-wtf/go-nostr/nip19" - "github.com/nbd-wtf/go-nostr/sdk/hints" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip05" + "fiatjaf.com/nostrlib/nip19" + "fiatjaf.com/nostrlib/sdk/hints" ) // ProfileMetadata represents user profile information from kind 0 events. diff --git a/sdk/outbox.go b/sdk/outbox.go index 2008bfb..8d6e498 100644 --- a/sdk/outbox.go +++ b/sdk/outbox.go @@ -2,14 +2,15 @@ package sdk import ( "context" - "strconv" "time" + + "fiatjaf.com/nostr" ) var outboxShortTermCache = [256]ostcEntry{} type ostcEntry struct { - pubkey string + pubkey nostr.PubKey relays []string when time.Time } @@ -18,8 +19,8 @@ type ostcEntry struct { // NIP-05, past attempts at fetching data from a user from a given relay, including successes and failures, and // the "write" relays of kind:10002, in order to determine the best possible list of relays where a user might be // currently publishing their events to. -func (sys *System) FetchOutboxRelays(ctx context.Context, pubkey string, n int) []string { - ostcIndex, _ := strconv.ParseUint(pubkey[12:14], 16, 8) +func (sys *System) FetchOutboxRelays(ctx context.Context, pubkey nostr.PubKey, n int) []string { + ostcIndex := pubkey[7] now := time.Now() if entry := outboxShortTermCache[ostcIndex]; entry.pubkey == pubkey && entry.when.Add(time.Minute*2).After(now) { return entry.relays diff --git a/sdk/replaceable_loader.go b/sdk/replaceable_loader.go index 6fa51ac..9bd8129 100644 --- a/sdk/replaceable_loader.go +++ b/sdk/replaceable_loader.go @@ -9,8 +9,8 @@ import ( "sync/atomic" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/dataloader" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/sdk/dataloader" ) type replaceableIndex int @@ -33,7 +33,7 @@ const ( type EventResult dataloader.Result[*nostr.Event] func (sys *System) initializeReplaceableDataloaders() { - sys.replaceableLoaders = make([]*dataloader.Loader[string, *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 int) *dataloader.Loader[string, *nostr.Event] { +func (sys *System) createReplaceableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, *nostr.Event] { return dataloader.NewBatchedLoader( - func(ctxs []context.Context, pubkeys []string) map[string]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{ @@ -62,11 +62,11 @@ func (sys *System) createReplaceableDataloader(kind int) *dataloader.Loader[stri func (sys *System) batchLoadReplaceableEvents( ctxs []context.Context, - kind int, - pubkeys []string, -) map[string]dataloader.Result[*nostr.Event] { + kind uint16, + pubkeys []nostr.PubKey, +) map[nostr.PubKey]dataloader.Result[*nostr.Event] { batchSize := len(pubkeys) - results := make(map[string]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)) @@ -83,7 +83,7 @@ func (sys *System) batchLoadReplaceableEvents( defer cancel() // build batched queries for the external relays - go func(i int, pubkey string) { + go func(i int, pubkey nostr.PubKey) { // gather relays we'll use for this pubkey relays := sys.determineRelaysToQuery(ctx, pubkey, kind) @@ -98,8 +98,8 @@ func (sys *System) batchLoadReplaceableEvents( dfilter = nostr.DirectedFilter{ Relay: relay, Filter: nostr.Filter{ - Kinds: []int{kind}, - Authors: make([]string, 0, batchSize-i /* this and all pubkeys after this can be added */), + Kinds: []uint16{kind}, + Authors: make([]nostr.PubKey, 0, batchSize-i /* this and all pubkeys after this can be added */), }, } idx = len(relayFilter) @@ -122,7 +122,7 @@ func (sys *System) batchLoadReplaceableEvents( // query all relays with the prepared filters wg.Wait() multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter, - nostr.WithLabel("repl~"+strconv.Itoa(kind)), + nostr.WithLabel("repl~"+strconv.Itoa(int(kind))), ) for { select { @@ -141,7 +141,7 @@ func (sys *System) batchLoadReplaceableEvents( } } -func (sys *System) determineRelaysToQuery(ctx context.Context, pubkey string, kind int) []string { +func (sys *System) determineRelaysToQuery(ctx context.Context, pubkey nostr.PubKey, kind uint16) []string { var relays []string // search in specific relays for user diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index f132aed..bd82fd3 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -7,7 +7,7 @@ import ( "sync" "testing" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" "github.com/stretchr/testify/require" ) diff --git a/sdk/search.go b/sdk/search.go index 2482964..ebd5c5f 100644 --- a/sdk/search.go +++ b/sdk/search.go @@ -3,7 +3,7 @@ package sdk import ( "context" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostrlib" ) func (sys *System) SearchUsers(ctx context.Context, query string) []ProfileMetadata { diff --git a/sdk/set.go b/sdk/set.go index 904306d..b401f4d 100644 --- a/sdk/set.go +++ b/sdk/set.go @@ -3,17 +3,16 @@ package sdk import ( "context" "slices" - "strconv" "time" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/cache" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/sdk/cache" ) // this is similar to list.go and inherits code from that. type GenericSets[I TagItemWithValue] struct { - PubKey string `json:"-"` + PubKey nostr.PubKey `json:"-"` Events []*nostr.Event `json:"-"` Sets map[string][]I @@ -22,14 +21,14 @@ type GenericSets[I TagItemWithValue] struct { func fetchGenericSets[I TagItemWithValue]( sys *System, ctx context.Context, - pubkey string, - actualKind int, + pubkey nostr.PubKey, + actualKind uint16, addressableIndex addressableIndex, parseTag func(nostr.Tag) (I, bool), cache cache.Cache32[GenericSets[I]], ) (fl GenericSets[I], fromInternal bool) { - n, _ := strconv.ParseUint(pubkey[14:16], 16, 8) - lockIdx := (n + uint64(actualKind)) % 60 + n := pubkey[7] + lockIdx := (uint16(n) + actualKind) % 60 genericListMutexes[lockIdx].Lock() if valueWasJustCached[lockIdx] { @@ -47,7 +46,7 @@ func fetchGenericSets[I TagItemWithValue]( v := GenericSets[I]{PubKey: pubkey} - events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []int{actualKind}, Authors: []string{pubkey}}) + events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}) if len(events) != 0 { // ok, we found something locally sets := parseSetsFromEvents(events, parseTag) @@ -93,7 +92,7 @@ func fetchGenericSets[I TagItemWithValue]( func tryFetchSetsFromNetwork[I TagItemWithValue]( ctx context.Context, sys *System, - pubkey string, + pubkey nostr.PubKey, addressableIndex addressableIndex, parseTag func(nostr.Tag) (I, bool), ) *GenericSets[I] { diff --git a/sdk/specific_event.go b/sdk/specific_event.go index 274ccac..27ddfdf 100644 --- a/sdk/specific_event.go +++ b/sdk/specific_event.go @@ -6,9 +6,9 @@ import ( "slices" "sync" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/nip19" - "github.com/nbd-wtf/go-nostr/sdk/hints" + "fiatjaf.com/nostrlib" + "fiatjaf.com/nostrlib/nip19" + "fiatjaf.com/nostrlib/sdk/hints" ) // FetchSpecificEventParameters contains options for fetching specific events. diff --git a/sdk/system.go b/sdk/system.go index ae82ad7..9a1adfd 100644 --- a/sdk/system.go +++ b/sdk/system.go @@ -4,16 +4,16 @@ import ( "context" "math/rand/v2" + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/sdk/cache" + cache_memory "fiatjaf.com/nostr/sdk/cache/memory" + "fiatjaf.com/nostr/sdk/dataloader" + "fiatjaf.com/nostr/sdk/hints" + "fiatjaf.com/nostr/sdk/hints/memoryh" + "fiatjaf.com/nostr/sdk/kvstore" + kvstore_memory "fiatjaf.com/nostr/sdk/kvstore/memory" "github.com/fiatjaf/eventstore" "github.com/fiatjaf/eventstore/nullstore" - "github.com/nbd-wtf/go-nostr" - "github.com/nbd-wtf/go-nostr/sdk/cache" - cache_memory "github.com/nbd-wtf/go-nostr/sdk/cache/memory" - "github.com/nbd-wtf/go-nostr/sdk/dataloader" - "github.com/nbd-wtf/go-nostr/sdk/hints" - "github.com/nbd-wtf/go-nostr/sdk/hints/memoryh" - "github.com/nbd-wtf/go-nostr/sdk/kvstore" - kvstore_memory "github.com/nbd-wtf/go-nostr/sdk/kvstore/memory" ) // System represents the core functionality of the SDK, providing access to @@ -53,8 +53,8 @@ type System struct { StoreRelay nostr.RelayStore - replaceableLoaders []*dataloader.Loader[string, *nostr.Event] - addressableLoaders []*dataloader.Loader[string, []*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. @@ -130,10 +130,10 @@ func NewSystem(mods ...SystemModifier) *System { } if sys.MetadataCache == nil { - sys.MetadataCache = cache_memory.New32[ProfileMetadata](8000) + sys.MetadataCache = cache_memory.New[ProfileMetadata](8000) } if sys.RelayListCache == nil { - sys.RelayListCache = cache_memory.New32[GenericList[Relay]](8000) + sys.RelayListCache = cache_memory.New[GenericList[Relay]](8000) } if sys.Store == nil { diff --git a/sdk/tracker.go b/sdk/tracker.go index 545f149..c06c236 100644 --- a/sdk/tracker.go +++ b/sdk/tracker.go @@ -3,12 +3,12 @@ package sdk import ( "net/url" - "github.com/nbd-wtf/go-nostr" + "fiatjaf.com/nostr" + "fiatjaf.com/nostrlib/sdk/hints" "github.com/nbd-wtf/go-nostr/nip27" - "github.com/nbd-wtf/go-nostr/sdk/hints" ) -func (sys *System) TrackQueryAttempts(relay string, author string, kind int) { +func (sys *System) TrackQueryAttempts(relay string, author nostr.PubKey, kind uint16) { if IsVirtualRelay(relay) { return } @@ -83,8 +83,10 @@ func (sys *System) trackEventHints(ie nostr.RelayEvent) { if p, err := url.Parse(tag[2]); err != nil || (p.Scheme != "wss" && p.Scheme != "ws") { continue } - if tag[0] == "p" && nostr.IsValidPublicKey(tag[1]) { - sys.Hints.Save(tag[1], nostr.NormalizeURL(tag[2]), hints.LastInHint, ie.CreatedAt) + if tag[0] == "p" { + if pk, err := nostr.PubKeyFromHex(tag[1]); err == nil { + sys.Hints.Save(pk, nostr.NormalizeURL(tag[2]), hints.LastInHint, ie.CreatedAt) + } } } default: @@ -101,8 +103,10 @@ func (sys *System) trackEventHints(ie nostr.RelayEvent) { if p, err := url.Parse(tag[2]); err != nil || (p.Scheme != "wss" && p.Scheme != "ws") { continue } - if tag[0] == "p" && nostr.IsValidPublicKey(tag[1]) { - sys.Hints.Save(tag[1], nostr.NormalizeURL(tag[2]), hints.LastInHint, ie.CreatedAt) + if tag[0] == "p" { + if pk, err := nostr.PubKeyFromHex(tag[1]); err == nil { + sys.Hints.Save(pk, nostr.NormalizeURL(tag[2]), hints.LastInHint, ie.CreatedAt) + } } } @@ -144,7 +148,7 @@ func (sys *System) trackEventHints(ie nostr.RelayEvent) { } // TrackEventRelaysD is a companion to TrackEventRelays meant to be used with WithDuplicateMiddleware() -func (sys *System) TrackEventRelaysD(relay, id string) { +func (sys *System) TrackEventRelaysD(relay string, id nostr.ID) { if IsVirtualRelay(relay) { return } diff --git a/signature.go b/signature.go index 8164033..f648e6d 100644 --- a/signature.go +++ b/signature.go @@ -4,7 +4,6 @@ package nostr import ( "crypto/sha256" - "encoding/hex" "fmt" "github.com/btcsuite/btcd/btcec/v2" @@ -16,22 +15,13 @@ import ( // If the signature is invalid bool will be false and err will be set. func (evt Event) CheckSignature() (bool, error) { // read and check pubkey - pk, err := hex.DecodeString(evt.PubKey) - if err != nil { - return false, fmt.Errorf("event pubkey '%s' is invalid hex: %w", evt.PubKey, err) - } - - pubkey, err := schnorr.ParsePubKey(pk) + pubkey, err := schnorr.ParsePubKey(evt.PubKey[:]) if err != nil { return false, fmt.Errorf("event has invalid pubkey '%s': %w", evt.PubKey, err) } // read signature - s, err := hex.DecodeString(evt.Sig) - if err != nil { - return false, fmt.Errorf("signature '%s' is invalid hex: %w", evt.Sig, err) - } - sig, err := schnorr.ParseSignature(s) + sig, err := schnorr.ParseSignature(evt.Sig[:]) if err != nil { return false, fmt.Errorf("failed to parse signature: %w", err) } @@ -42,21 +32,18 @@ func (evt Event) CheckSignature() (bool, error) { } // Sign signs an event with a given privateKey. +// // It sets the event's ID, PubKey, and Sig fields. +// // Returns an error if the private key is invalid or if signing fails. -func (evt *Event) Sign(secretKey string) error { - s, err := hex.DecodeString(secretKey) - if err != nil { - return fmt.Errorf("Sign called with invalid secret key '%s': %w", secretKey, err) - } - +func (evt *Event) Sign(secretKey [32]byte) error { if evt.Tags == nil { evt.Tags = make(Tags, 0) } - sk, pk := btcec.PrivKeyFromBytes(s) - pkBytes := pk.SerializeCompressed() - evt.PubKey = hex.EncodeToString(pkBytes[1:]) + sk, pk := btcec.PrivKeyFromBytes(secretKey[:]) + pkBytes := pk.SerializeCompressed()[1:] + evt.PubKey = [32]byte(pkBytes) h := sha256.Sum256(evt.Serialize()) sig, err := schnorr.Sign(sk, h[:], schnorr.FastSign()) @@ -64,8 +51,9 @@ func (evt *Event) Sign(secretKey string) error { return err } - evt.ID = hex.EncodeToString(h[:]) - evt.Sig = hex.EncodeToString(sig.Serialize()) + evt.ID = h + sigb := sig.Serialize() + evt.Sig = [64]byte(sigb) return nil } diff --git a/signature_libsecp256k1.go b/signature_libsecp256k1.go index ab8955e..9c20698 100644 --- a/signature_libsecp256k1.go +++ b/signature_libsecp256k1.go @@ -26,7 +26,6 @@ import "C" import ( "crypto/rand" "crypto/sha256" - "encoding/hex" "errors" "fmt" "unsafe" @@ -35,61 +34,41 @@ import ( ) func (evt Event) CheckSignature() (bool, error) { - var pk [32]byte - _, err := hex.Decode(pk[:], []byte(evt.PubKey)) - if err != nil { - return false, fmt.Errorf("event pubkey '%s' is invalid hex: %w", evt.PubKey, err) - } - - var sig [64]byte - _, err = hex.Decode(sig[:], []byte(evt.Sig)) - if err != nil { - return false, fmt.Errorf("event signature '%s' is invalid hex: %w", evt.Sig, err) - } - msg := sha256.Sum256(evt.Serialize()) var xonly C.secp256k1_xonly_pubkey - if C.secp256k1_xonly_pubkey_parse(globalSecp256k1Context, &xonly, (*C.uchar)(unsafe.Pointer(&pk[0]))) != 1 { + if C.secp256k1_xonly_pubkey_parse(globalSecp256k1Context, &xonly, (*C.uchar)(unsafe.Pointer(&evt.PubKey[0]))) != 1 { return false, fmt.Errorf("failed to parse xonly pubkey") } - res := C.secp256k1_schnorrsig_verify(globalSecp256k1Context, (*C.uchar)(unsafe.Pointer(&sig[0])), (*C.uchar)(unsafe.Pointer(&msg[0])), 32, &xonly) + res := C.secp256k1_schnorrsig_verify(globalSecp256k1Context, (*C.uchar)(unsafe.Pointer(&evt.Sig[0])), (*C.uchar)(unsafe.Pointer(&msg[0])), 32, &xonly) return res == 1, nil } -func (evt *Event) Sign(secretKey string, signOpts ...schnorr.SignOption) error { - sk, err := hex.DecodeString(secretKey) - if err != nil { - return fmt.Errorf("Sign called with invalid secret key '%s': %w", secretKey, err) - } - +// Sign signs an event with a given privateKey. +func (evt *Event) Sign(secretKey [32]byte, signOpts ...schnorr.SignOption) error { if evt.Tags == nil { evt.Tags = make(Tags, 0) } var keypair C.secp256k1_keypair - if C.secp256k1_keypair_create(globalSecp256k1Context, &keypair, (*C.uchar)(unsafe.Pointer(&sk[0]))) != 1 { + if C.secp256k1_keypair_create(globalSecp256k1Context, &keypair, (*C.uchar)(unsafe.Pointer(&secretKey[0]))) != 1 { return errors.New("failed to parse private key") } var xonly C.secp256k1_xonly_pubkey - var pk [32]byte C.secp256k1_keypair_xonly_pub(globalSecp256k1Context, &xonly, nil, &keypair) - C.secp256k1_xonly_pubkey_serialize(globalSecp256k1Context, (*C.uchar)(unsafe.Pointer(&pk[0])), &xonly) - evt.PubKey = hex.EncodeToString(pk[:]) + C.secp256k1_xonly_pubkey_serialize(globalSecp256k1Context, (*C.uchar)(unsafe.Pointer(&evt.PubKey[0])), &xonly) h := sha256.Sum256(evt.Serialize()) - var sig [64]byte var random [32]byte rand.Read(random[:]) - if C.secp256k1_schnorrsig_sign32(globalSecp256k1Context, (*C.uchar)(unsafe.Pointer(&sig[0])), (*C.uchar)(unsafe.Pointer(&h[0])), &keypair, (*C.uchar)(unsafe.Pointer(&random[0]))) != 1 { + if C.secp256k1_schnorrsig_sign32(globalSecp256k1Context, (*C.uchar)(unsafe.Pointer(&evt.Sig[0])), (*C.uchar)(unsafe.Pointer(&h[0])), &keypair, (*C.uchar)(unsafe.Pointer(&random[0]))) != 1 { return errors.New("failed to sign message") } - evt.ID = hex.EncodeToString(h[:]) - evt.Sig = hex.EncodeToString(sig[:]) + evt.ID = h return nil } diff --git a/subscription.go b/subscription.go index 298dd39..c761f33 100644 --- a/subscription.go +++ b/subscription.go @@ -13,8 +13,8 @@ type Subscription struct { counter int64 id string - Relay *Relay - Filters Filters + Relay *Relay + Filter Filter // for this to be treated as a COUNT and not a REQ this must be set countResult chan CountEnvelope @@ -35,7 +35,7 @@ type Subscription struct { // if it is not nil, checkDuplicate will be called for every event received // if it returns true that event will not be processed further. - checkDuplicate func(id string, relay string) bool + checkDuplicate func(id ID, relay string) bool // if it is not nil, checkDuplicateReplaceable will be called for every event received // if it returns true that event will not be processed further. @@ -63,7 +63,7 @@ type WithLabel string func (_ WithLabel) IsSubscriptionOption() {} // WithCheckDuplicate sets checkDuplicate on the subscription -type WithCheckDuplicate func(id, relay string) bool +type WithCheckDuplicate func(id ID, relay string) bool func (_ WithCheckDuplicate) IsSubscriptionOption() {} @@ -119,7 +119,7 @@ func (sub *Subscription) dispatchEvent(evt *Event) { func (sub *Subscription) dispatchEose() { if sub.eosed.CompareAndSwap(false, true) { - sub.match = sub.Filters.MatchIgnoringTimestampConstraints + sub.match = sub.Filter.MatchesIgnoringTimestampConstraints go func() { sub.storedwg.Wait() sub.EndOfStoredEvents <- struct{}{} @@ -167,8 +167,8 @@ func (sub *Subscription) Close() { // Sub sets sub.Filters and then calls sub.Fire(ctx). // The subscription will be closed if the context expires. -func (sub *Subscription) Sub(_ context.Context, filters Filters) { - sub.Filters = filters +func (sub *Subscription) Sub(_ context.Context, filter Filter) { + sub.Filter = filter sub.Fire() } @@ -176,11 +176,9 @@ func (sub *Subscription) Sub(_ context.Context, filters Filters) { func (sub *Subscription) Fire() error { var reqb []byte if sub.countResult == nil { - reqb, _ = ReqEnvelope{sub.id, sub.Filters}.MarshalJSON() - } else if len(sub.Filters) == 1 { - reqb, _ = CountEnvelope{sub.id, sub.Filters[0], nil, nil}.MarshalJSON() + reqb, _ = ReqEnvelope{sub.id, sub.Filter}.MarshalJSON() } else { - return fmt.Errorf("unexpected sub configuration") + reqb, _ = CountEnvelope{sub.id, sub.Filter, nil, nil}.MarshalJSON() } sub.live.Store(true) diff --git a/subscription_test.go b/subscription_test.go index 7aefbb9..9625ae8 100644 --- a/subscription_test.go +++ b/subscription_test.go @@ -17,7 +17,7 @@ func TestSubscribeBasic(t *testing.T) { rl := mustRelayConnect(t, RELAY) defer rl.Close() - sub, err := rl.Subscribe(context.Background(), Filters{{Kinds: []int{KindTextNote}, Limit: 2}}) + sub, err := rl.Subscribe(context.Background(), Filters{{Kinds: []uint16{KindTextNote}, Limit: 2}}) assert.NoError(t, err) timeout := time.After(5 * time.Second) @@ -51,14 +51,14 @@ func TestNestedSubscriptions(t *testing.T) { n := atomic.Uint32{} // fetch 2 replies to a note - sub, err := rl.Subscribe(context.Background(), Filters{{Kinds: []int{KindTextNote}, Tags: TagMap{"e": []string{"0e34a74f8547e3b95d52a2543719b109fd0312aba144e2ef95cba043f42fe8c5"}}, Limit: 3}}) + sub, err := rl.Subscribe(context.Background(), Filters{{Kinds: []uint16{KindTextNote}, Tags: TagMap{"e": []string{"0e34a74f8547e3b95d52a2543719b109fd0312aba144e2ef95cba043f42fe8c5"}}, Limit: 3}}) assert.NoError(t, err) for { select { case event := <-sub.Events: // now fetch author of this - sub, err := rl.Subscribe(context.Background(), Filters{{Kinds: []int{KindProfileMetadata}, Authors: []string{event.PubKey}, Limit: 1}}) + sub, err := rl.Subscribe(context.Background(), Filters{{Kinds: []uint16{KindProfileMetadata}, Authors: []PubKey{event.PubKey}, Limit: 1}}) assert.NoError(t, err) for { diff --git a/types.go b/types.go new file mode 100644 index 0000000..c86b2f0 --- /dev/null +++ b/types.go @@ -0,0 +1,68 @@ +package nostr + +import ( + "encoding/hex" + "fmt" + "unsafe" +) + +type PubKey [32]byte + +func (pk PubKey) String() string { return hex.EncodeToString(pk[:]) } + +func PubKeyFromHex(pkh string) (PubKey, error) { + pk := PubKey{} + if len(pkh) != 64 { + return pk, fmt.Errorf("pubkey should be 64-char hex, got '%s'", pkh) + } + if _, err := hex.Decode(pk[:], unsafe.Slice(unsafe.StringData(pkh), 64)); err != nil { + return pk, fmt.Errorf("'%s' is not valid hex: %w", pkh, err) + } + + if !IsValidPublicKey(pk) { + return pk, fmt.Errorf("'%s' is not a valid pubkey", pkh) + } + + return pk, nil +} + +func PubKeyFromHexCheap(pkh string) (PubKey, error) { + pk := PubKey{} + if len(pkh) != 64 { + return pk, fmt.Errorf("pubkey should be 64-char hex, got '%s'", pkh) + } + if _, err := hex.Decode(pk[:], unsafe.Slice(unsafe.StringData(pkh), 64)); err != nil { + return pk, fmt.Errorf("'%s' is not valid hex: %w", pkh, err) + } + + return pk, nil +} + +func MustPubKeyFromHex(pkh string) PubKey { + pk := PubKey{} + hex.Decode(pk[:], unsafe.Slice(unsafe.StringData(pkh), 64)) + return pk +} + +type ID [32]byte + +func (id ID) String() string { return hex.EncodeToString(id[:]) } + +func IDFromHex(idh string) (ID, error) { + id := ID{} + + if len(idh) != 64 { + return id, fmt.Errorf("pubkey should be 64-char hex, got '%s'", idh) + } + if _, err := hex.Decode(id[:], unsafe.Slice(unsafe.StringData(idh), 64)); err != nil { + return id, fmt.Errorf("'%s' is not valid hex: %w", idh, err) + } + + return id, nil +} + +func MustIDFromHex(idh string) ID { + id := ID{} + hex.Decode(id[:], unsafe.Slice(unsafe.StringData(idh), 64)) + return id +} diff --git a/utils.go b/utils.go index 4ccf3cc..b7bf200 100644 --- a/utils.go +++ b/utils.go @@ -1,10 +1,10 @@ package nostr import ( + "bytes" "cmp" "encoding/hex" "net/url" - "strings" ) // IsValidRelayURL checks if a URL is a valid relay URL (ws:// or wss://). @@ -34,7 +34,7 @@ func IsValid32ByteHex(thing string) bool { // CompareEvent is meant to to be used with slices.Sort func CompareEvent(a, b Event) int { if a.CreatedAt == b.CreatedAt { - return strings.Compare(a.ID, b.ID) + return bytes.Compare(a.ID[:], b.ID[:]) } return cmp.Compare(a.CreatedAt, b.CreatedAt) } @@ -42,7 +42,7 @@ func CompareEvent(a, b Event) int { // CompareEventReverse is meant to to be used with slices.Sort func CompareEventReverse(b, a Event) int { if a.CreatedAt == b.CreatedAt { - return strings.Compare(a.ID, b.ID) + return bytes.Compare(a.ID[:], b.ID[:]) } return cmp.Compare(a.CreatedAt, b.CreatedAt) } @@ -60,7 +60,7 @@ func CompareEventPtr(a, b *Event) int { } if a.CreatedAt == b.CreatedAt { - return strings.Compare(a.ID, b.ID) + return bytes.Compare(a.ID[:], b.ID[:]) } return cmp.Compare(a.CreatedAt, b.CreatedAt) } @@ -78,7 +78,7 @@ func CompareEventPtrReverse(b, a *Event) int { } if a.CreatedAt == b.CreatedAt { - return strings.Compare(a.ID, b.ID) + return bytes.Compare(a.ID[:], b.ID[:]) } return cmp.Compare(a.CreatedAt, b.CreatedAt) }