mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-09-18 11:32:25 +02:00
change parsing so NIP12 can work, rename types.
This commit is contained in:
24
event.go
24
event.go
@@ -7,6 +7,8 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fiatjaf/bip340"
|
"github.com/fiatjaf/bip340"
|
||||||
)
|
)
|
||||||
@@ -24,7 +26,7 @@ type Event struct {
|
|||||||
ID string `json:"id"` // it's the hash of the serialized event
|
ID string `json:"id"` // it's the hash of the serialized event
|
||||||
|
|
||||||
PubKey string `json:"pubkey"`
|
PubKey string `json:"pubkey"`
|
||||||
CreatedAt uint32 `json:"created_at"`
|
CreatedAt Time `json:"created_at"`
|
||||||
|
|
||||||
Kind int `json:"kind"`
|
Kind int `json:"kind"`
|
||||||
|
|
||||||
@@ -33,6 +35,24 @@ type Event struct {
|
|||||||
Sig string `json:"sig"`
|
Sig string `json:"sig"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Time time.Time
|
||||||
|
|
||||||
|
func (tm *Time) UnmarshalJSON(payload []byte) error {
|
||||||
|
var unix int64
|
||||||
|
err := json.Unmarshal(payload, &unix)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("time must be a unix timestamp as an integer, not '%s': %w",
|
||||||
|
string(payload), err)
|
||||||
|
}
|
||||||
|
t := Time(time.Unix(unix, 0))
|
||||||
|
tm = &t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Time) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(strconv.FormatInt(time.Time(t).Unix(), 10)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Serialize outputs a byte array that can be hashed/signed to identify/authenticate
|
// Serialize outputs a byte array that can be hashed/signed to identify/authenticate
|
||||||
func (evt *Event) Serialize() []byte {
|
func (evt *Event) Serialize() []byte {
|
||||||
// the serialization process is just putting everything into a JSON array
|
// the serialization process is just putting everything into a JSON array
|
||||||
@@ -46,7 +66,7 @@ func (evt *Event) Serialize() []byte {
|
|||||||
arr[1] = evt.PubKey
|
arr[1] = evt.PubKey
|
||||||
|
|
||||||
// created_at
|
// created_at
|
||||||
arr[2] = int64(evt.CreatedAt)
|
arr[2] = int64(time.Time(evt.CreatedAt).Unix())
|
||||||
|
|
||||||
// kind
|
// kind
|
||||||
arr[3] = int64(evt.Kind)
|
arr[3] = int64(evt.Kind)
|
||||||
|
55
filter.go
55
filter.go
@@ -1,18 +1,21 @@
|
|||||||
package nostr
|
package nostr
|
||||||
|
|
||||||
type EventFilters []EventFilter
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type EventFilter struct {
|
type Filters []Filter
|
||||||
IDs StringList `json:"ids,omitempty"`
|
|
||||||
Kinds IntList `json:"kinds,omitempty"`
|
type Filter struct {
|
||||||
Authors StringList `json:"authors,omitempty"`
|
IDs StringList
|
||||||
Since uint32 `json:"since,omitempty"`
|
Kinds IntList
|
||||||
Until uint32 `json:"until,omitempty"`
|
Authors StringList
|
||||||
TagE StringList `json:"#e,omitempty"`
|
Since *time.Time
|
||||||
TagP StringList `json:"#p,omitempty"`
|
Until *time.Time
|
||||||
|
Tags map[string]StringList
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eff EventFilters) Match(event *Event) bool {
|
func (eff Filters) Match(event *Event) bool {
|
||||||
for _, filter := range eff {
|
for _, filter := range eff {
|
||||||
if filter.Matches(event) {
|
if filter.Matches(event) {
|
||||||
return true
|
return true
|
||||||
@@ -21,7 +24,7 @@ func (eff EventFilters) Match(event *Event) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ef EventFilter) Matches(event *Event) bool {
|
func (ef Filter) Matches(event *Event) bool {
|
||||||
if event == nil {
|
if event == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -38,26 +41,24 @@ func (ef EventFilter) Matches(event *Event) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ef.TagE != nil && !event.Tags.ContainsAny("e", ef.TagE) {
|
for f, v := range ef.Tags {
|
||||||
|
if v != nil && !event.Tags.ContainsAny(f, v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ef.Since != nil && time.Time(event.CreatedAt).Before(*ef.Since) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ef.TagP != nil && !event.Tags.ContainsAny("p", ef.TagP) {
|
if ef.Until != nil && time.Time(event.CreatedAt).After(*ef.Until) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if ef.Since != 0 && event.CreatedAt < ef.Since {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if ef.Until != 0 && event.CreatedAt >= ef.Until {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilterEqual(a EventFilter, b EventFilter) bool {
|
func FilterEqual(a Filter, b Filter) bool {
|
||||||
if !a.Kinds.Equals(b.Kinds) {
|
if !a.Kinds.Equals(b.Kinds) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -70,12 +71,18 @@ func FilterEqual(a EventFilter, b EventFilter) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !a.TagE.Equals(b.TagE) {
|
if len(a.Tags) != len(b.Tags) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !a.TagP.Equals(b.TagP) {
|
for f, av := range a.Tags {
|
||||||
|
if bv, ok := b.Tags[f]; !ok {
|
||||||
return false
|
return false
|
||||||
|
} else {
|
||||||
|
if !av.Equals(bv) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.Since != b.Since {
|
if a.Since != b.Since {
|
||||||
|
151
filter_aux.go
151
filter_aux.go
@@ -1,5 +1,13 @@
|
|||||||
package nostr
|
package nostr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/valyala/fastjson"
|
||||||
|
)
|
||||||
|
|
||||||
type StringList []string
|
type StringList []string
|
||||||
type IntList []int
|
type IntList []int
|
||||||
|
|
||||||
@@ -62,3 +70,146 @@ func (haystack IntList) Contains(needle int) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Filter) UnmarshalJSON(payload []byte) error {
|
||||||
|
var fastjsonParser fastjson.Parser
|
||||||
|
parsed, err := fastjsonParser.ParseBytes(payload)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse filter: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := parsed.Object()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("filter is not an object")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Tags = make(map[string]StringList)
|
||||||
|
|
||||||
|
var visiterr error
|
||||||
|
obj.Visit(func(k []byte, v *fastjson.Value) {
|
||||||
|
key := string(k)
|
||||||
|
switch key {
|
||||||
|
case "ids":
|
||||||
|
f.IDs, err = fastjsonArrayToStringList(v)
|
||||||
|
if err != nil {
|
||||||
|
visiterr = fmt.Errorf("invalid 'ids' field: %w", err)
|
||||||
|
}
|
||||||
|
case "kinds":
|
||||||
|
f.Kinds, err = fastjsonArrayToIntList(v)
|
||||||
|
if err != nil {
|
||||||
|
visiterr = fmt.Errorf("invalid 'kinds' field: %w", err)
|
||||||
|
}
|
||||||
|
case "authors":
|
||||||
|
f.Authors, err = fastjsonArrayToStringList(v)
|
||||||
|
if err != nil {
|
||||||
|
visiterr = fmt.Errorf("invalid 'authors' field: %w", err)
|
||||||
|
}
|
||||||
|
case "since":
|
||||||
|
val, err := v.Int64()
|
||||||
|
if err != nil {
|
||||||
|
visiterr = fmt.Errorf("invalid 'since' field: %w", err)
|
||||||
|
}
|
||||||
|
tm := time.Unix(val, 0)
|
||||||
|
f.Since = &tm
|
||||||
|
case "until":
|
||||||
|
val, err := v.Int64()
|
||||||
|
if err != nil {
|
||||||
|
visiterr = fmt.Errorf("invalid 'until' field: %w", err)
|
||||||
|
}
|
||||||
|
tm := time.Unix(val, 0)
|
||||||
|
f.Until = &tm
|
||||||
|
default:
|
||||||
|
if strings.HasPrefix(key, "#") {
|
||||||
|
f.Tags[key[1:]], err = fastjsonArrayToStringList(v)
|
||||||
|
if err != nil {
|
||||||
|
visiterr = fmt.Errorf("invalid '%s' field: %w", key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if visiterr != nil {
|
||||||
|
return visiterr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Filter) MarshalJSON() ([]byte, error) {
|
||||||
|
var arena fastjson.Arena
|
||||||
|
|
||||||
|
o := arena.NewObject()
|
||||||
|
|
||||||
|
if f.IDs != nil {
|
||||||
|
o.Set("ids", stringListToFastjsonArray(&arena, f.IDs))
|
||||||
|
}
|
||||||
|
if f.Kinds != nil {
|
||||||
|
o.Set("kinds", intListToFastjsonArray(&arena, f.Kinds))
|
||||||
|
}
|
||||||
|
if f.Authors != nil {
|
||||||
|
o.Set("authors", stringListToFastjsonArray(&arena, f.Authors))
|
||||||
|
}
|
||||||
|
if f.Since != nil {
|
||||||
|
o.Set("since", arena.NewNumberInt(int(f.Since.Unix())))
|
||||||
|
}
|
||||||
|
if f.Until != nil {
|
||||||
|
o.Set("until", arena.NewNumberInt(int(f.Until.Unix())))
|
||||||
|
}
|
||||||
|
if f.Tags != nil {
|
||||||
|
for k, v := range f.Tags {
|
||||||
|
o.Set("#"+k, stringListToFastjsonArray(&arena, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.MarshalTo(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringListToFastjsonArray(arena *fastjson.Arena, sl StringList) *fastjson.Value {
|
||||||
|
arr := arena.NewArray()
|
||||||
|
for i, v := range sl {
|
||||||
|
arr.SetArrayItem(i, arena.NewString(v))
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
func intListToFastjsonArray(arena *fastjson.Arena, il IntList) *fastjson.Value {
|
||||||
|
arr := arena.NewArray()
|
||||||
|
for i, v := range il {
|
||||||
|
arr.SetArrayItem(i, arena.NewNumberInt(v))
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
func fastjsonArrayToStringList(v *fastjson.Value) (StringList, error) {
|
||||||
|
arr, err := v.Array()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sl := make(StringList, len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
sb, err := v.StringBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sl[i] = string(sb)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fastjsonArrayToIntList(v *fastjson.Value) (IntList, error) {
|
||||||
|
arr, err := v.Array()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
il := make(IntList, len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
il[i], err = v.Int()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return il, nil
|
||||||
|
}
|
||||||
|
1
go.mod
1
go.mod
@@ -8,4 +8,5 @@ require (
|
|||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/tyler-smith/go-bip32 v1.0.0
|
github.com/tyler-smith/go-bip32 v1.0.0
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0
|
github.com/tyler-smith/go-bip39 v1.1.0
|
||||||
|
github.com/valyala/fastjson v1.6.3 // indirect
|
||||||
)
|
)
|
||||||
|
2
go.sum
2
go.sum
@@ -47,6 +47,8 @@ github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJ
|
|||||||
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
|
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||||
|
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
|
||||||
|
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||||
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
@@ -35,7 +35,7 @@ type RelayPool struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RelayPoolPolicy interface {
|
type RelayPoolPolicy interface {
|
||||||
ShouldRead(EventFilters) bool
|
ShouldRead(Filters) bool
|
||||||
ShouldWrite(*Event) bool
|
ShouldWrite(*Event) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ type SimplePolicy struct {
|
|||||||
Write bool
|
Write bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SimplePolicy) ShouldRead(_ EventFilters) bool {
|
func (s SimplePolicy) ShouldRead(_ Filters) bool {
|
||||||
return s.Read
|
return s.Read
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ func (r *RelayPool) Remove(url string) {
|
|||||||
delete(r.websockets, nm)
|
delete(r.websockets, nm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RelayPool) Sub(filters EventFilters) *Subscription {
|
func (r *RelayPool) Sub(filters Filters) *Subscription {
|
||||||
random := make([]byte, 7)
|
random := make([]byte, 7)
|
||||||
rand.Read(random)
|
rand.Read(random)
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ func (r *RelayPool) PublishEvent(evt *Event) (*Event, chan PublishStatus, error)
|
|||||||
}
|
}
|
||||||
status <- PublishStatus{relay, PublishStatusSent}
|
status <- PublishStatus{relay, PublishStatusSent}
|
||||||
|
|
||||||
subscription := r.Sub(EventFilters{EventFilter{IDs: []string{evt.ID}}})
|
subscription := r.Sub(Filters{Filter{IDs: []string{evt.ID}}})
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-subscription.UniqueEvents:
|
case event := <-subscription.UniqueEvents:
|
||||||
|
@@ -4,7 +4,7 @@ type Subscription struct {
|
|||||||
channel string
|
channel string
|
||||||
relays map[string]*Connection
|
relays map[string]*Connection
|
||||||
|
|
||||||
filters EventFilters
|
filters Filters
|
||||||
Events chan EventMessage
|
Events chan EventMessage
|
||||||
|
|
||||||
started bool
|
started bool
|
||||||
|
Reference in New Issue
Block a user