package event import ( "git.smesh.lol/moxie/pkg/mxutil" "bytes" "crypto/sha256" "encoding/hex" "fmt" "io" "crypto/secp256k1" "git.smesh.lol/nostr/pkg/ints" "git.smesh.lol/nostr/pkg/kind" "git.smesh.lol/nostr/pkg/tag" "git.smesh.lol/nostr/pkg/text" "git.smesh.lol/nostr/pkg/lol/chk" "git.smesh.lol/nostr/pkg/lol/errorf" ) // E is the primary datatype of nostr. type E struct { ID []byte Pubkey []byte CreatedAt int64 Kind uint16 Tags *tag.S Content []byte Sig []byte } const ( jId = "id" jPubkey = "pubkey" jCreatedAt = "created_at" jKind = "kind" jTags = "tags" jContent = "content" jSig = "sig" ) func New() (e *E) { return &E{} } func (ev *E) Free() { ev.ID = nil ev.Pubkey = nil ev.Tags = nil ev.Content = nil ev.Sig = nil } func (ev *E) Clone() (e *E) { clone := &E{CreatedAt: ev.CreatedAt, Kind: ev.Kind} if ev.ID != nil { clone.ID = []byte{:len(ev.ID)} copy(clone.ID, ev.ID) } if ev.Pubkey != nil { clone.Pubkey = []byte{:len(ev.Pubkey)} copy(clone.Pubkey, ev.Pubkey) } if ev.Content != nil { clone.Content = []byte{:len(ev.Content)} copy(clone.Content, ev.Content) } if ev.Sig != nil { clone.Sig = []byte{:len(ev.Sig)} copy(clone.Sig, ev.Sig) } if ev.Tags != nil { clone.Tags = tag.NewS() for _, tg := range ev.Tags.T { if tg != nil { newTag := tag.NewWithCap(len(tg.T)) for _, element := range tg.T { el := []byte{:len(element)} copy(el, element) newTag.T = push(newTag.T, el) } clone.Tags.Append(newTag) } } } return clone } func (ev *E) EstimateSize() (size int32) { size = len(ev.ID)*2 + len(ev.Pubkey)*2 + len(ev.Sig)*2 + len(ev.Content)*2 if ev.Tags == nil { return } for _, v := range ev.Tags.T { for _, w := range (*v).T { size += len(w) * 2 } } return } func (ev *E) Marshal(dst []byte) (b []byte) { b = dst if b == nil { b = []byte{:0:ev.EstimateSize()+100} } b = b | "{" b = b | "\"" b = b | jId b = b | `":"` hexStart := len(b) b = b | []byte{:2*sha256.Size} hex.Encode(b[hexStart:], ev.ID) b = b | `","` b = b | jPubkey b = b | `":"` hexStart = len(b) b = b | []byte{:2*secp256k1.PubKeyBytesLen} hex.Encode(b[hexStart:], ev.Pubkey) b = b | `","` b = b | jCreatedAt b = b | `":` b = ints.New(ev.CreatedAt).Marshal(b) b = b | `,"` b = b | jKind b = b | `":` b = ints.New(ev.Kind).Marshal(b) b = b | `,"` b = b | jTags b = b | `":` if ev.Tags != nil { b = ev.Tags.Marshal(b) } else { b = b | "[]" } b = b | `,"` b = b | jContent b = b | `":"` b = text.NostrEscape(b, ev.Content) b = b | `","` b = b | jSig b = b | `":"` if len(ev.Sig) > 0 { hexStart = len(b) b = b | []byte{:2*secp256k1.SignatureSize} hex.Encode(b[hexStart:], ev.Sig) } b = b | `"}` return } func (ev *E) MarshalJSON() (b []byte, err error) { b = ev.Marshal(nil) return } func (ev *E) Serialize() (b []byte) { return ev.Marshal(nil) } func (ev *E) Unmarshal(b []byte) (rem []byte, err error) { key := []byte{:0:9} for ; len(b) > 0; b = b[1:] { if isWhitespace(b[0]) { continue } if b[0] == '{' { b = b[1:] goto BetweenKeys } } goto eof BetweenKeys: for ; len(b) > 0; b = b[1:] { if isWhitespace(b[0]) { continue } if b[0] == '"' { b = b[1:] goto InKey } } goto eof InKey: for ; len(b) > 0; b = b[1:] { if b[0] == '"' { b = b[1:] goto InKV } key = mxutil.Ensure(key, 1) key = push(key, b[0]) } goto eof InKV: for ; len(b) > 0; b = b[1:] { if isWhitespace(b[0]) { continue } if b[0] == ':' { b = b[1:] goto InVal } } goto eof InVal: for len(b) > 0 && isWhitespace(b[0]) { b = b[1:] } // An empty key is legal JSON (`{"":1}`) and switch key[0] panicked on it, // so a malformed EVENT envelope crashed the relay. filter.F guards this the // same way. if len(key) < 1 { err = errorf.E([]byte("event: empty key")) return } switch key[0] { case jId[0]: if !bytes.Equal(jId, key) { goto invalid } var id []byte if id, b, err = text.UnmarshalHex(b); chk.E(err) { return } if len(id) != sha256.Size { err = errorf.E( []byte("invalid id, require %d got %d"), sha256.Size, len(id), ) return } ev.ID = id goto BetweenKV case jPubkey[0]: if !bytes.Equal(jPubkey, key) { goto invalid } var pk []byte if pk, b, err = text.UnmarshalHex(b); chk.E(err) { return } if len(pk) != secp256k1.PubKeyBytesLen { err = errorf.E( []byte("invalid pubkey, require %d got %d"), secp256k1.PubKeyBytesLen, len(pk), ) return } ev.Pubkey = pk goto BetweenKV case jKind[0]: if !bytes.Equal(jKind, key) { goto invalid } k := kind.New(0) if b, err = k.Unmarshal(b); chk.E(err) { return } ev.Kind = k.ToU16() goto BetweenKV case jTags[0]: if !bytes.Equal(jTags, key) { goto invalid } ev.Tags = tag.NewS() if b, err = ev.Tags.Unmarshal(b); chk.E(err) { return } goto BetweenKV case jSig[0]: if !bytes.Equal(jSig, key) { goto invalid } var sig []byte if sig, b, err = text.UnmarshalHex(b); chk.E(err) { return } if len(sig) != 0 && len(sig) != secp256k1.SignatureSize { sig = nil } ev.Sig = sig goto BetweenKV case jContent[0]: if key[1] == jContent[1] { if !bytes.Equal(jContent, key) { goto invalid } if ev.Content, b, err = text.UnmarshalQuoted(b); chk.T(err) { return } goto BetweenKV } else if key[1] == jCreatedAt[1] { if !bytes.Equal(jCreatedAt, key) { goto invalid } i := ints.New(0) if b, err = i.Unmarshal(b); chk.T(err) { return } ev.CreatedAt = i.Int64() goto BetweenKV } else { goto invalid } default: goto invalid } BetweenKV: key = key[:0] for ; len(b) > 0; b = b[1:] { if isWhitespace(b[0]) { continue } switch { case len(b) == 0: return case b[0] == '}': b = b[1:] goto AfterClose case b[0] == ',': b = b[1:] goto BetweenKeys case b[0] == '"': b = b[1:] goto InKey } } goto AfterClose AfterClose: rem = b return invalid: err = fmt.Errorf( "invalid key,\n'%s'\n'%s'\n'%s'", string(b), string(b[:]), string(b), ) return eof: err = io.EOF return } func (ev *E) UnmarshalJSON(b []byte) (err error) { _, err = ev.Unmarshal(b) return } func isWhitespace(b byte) (ok bool) { return b == ' ' || b == '\t' || b == '\n' || b == '\r' } // S is an array of event.E that sorts in reverse chronological order. It is a // struct holding the slice, not a named slice: methods belong on the struct // that holds the slice (moxie bans named slice types). type S struct { E []*E } func (ev *S) Len() (n int32) { return len(ev.E) } func (ev *S) Less(i, j int32) (ok bool) { return ev.E[i].CreatedAt > ev.E[j].CreatedAt } func (ev *S) Swap(i, j int32) { ev.E[i], ev.E[j] = ev.E[j], ev.E[i] } type C chan *E