event.mx raw
1 package event
2
3 import (
4 "git.smesh.lol/moxie/pkg/mxutil"
5 "bytes"
6 "crypto/sha256"
7 "encoding/hex"
8 "fmt"
9 "io"
10
11 "crypto/secp256k1"
12 "git.smesh.lol/nostr/pkg/ints"
13 "git.smesh.lol/nostr/pkg/kind"
14 "git.smesh.lol/nostr/pkg/tag"
15 "git.smesh.lol/nostr/pkg/text"
16 "git.smesh.lol/nostr/pkg/lol/chk"
17 "git.smesh.lol/nostr/pkg/lol/errorf"
18 )
19
20 // E is the primary datatype of nostr.
21 type E struct {
22 ID []byte
23 Pubkey []byte
24 CreatedAt int64
25 Kind uint16
26 Tags *tag.S
27 Content []byte
28 Sig []byte
29 }
30
31 const (
32 jId = "id"
33 jPubkey = "pubkey"
34 jCreatedAt = "created_at"
35 jKind = "kind"
36 jTags = "tags"
37 jContent = "content"
38 jSig = "sig"
39 )
40
41 func New() (e *E) { return &E{} }
42
43 func (ev *E) Free() {
44 ev.ID = nil
45 ev.Pubkey = nil
46 ev.Tags = nil
47 ev.Content = nil
48 ev.Sig = nil
49 }
50
51 func (ev *E) Clone() (e *E) {
52 clone := &E{CreatedAt: ev.CreatedAt, Kind: ev.Kind}
53 if ev.ID != nil {
54 clone.ID = []byte{:len(ev.ID)}
55 copy(clone.ID, ev.ID)
56 }
57 if ev.Pubkey != nil {
58 clone.Pubkey = []byte{:len(ev.Pubkey)}
59 copy(clone.Pubkey, ev.Pubkey)
60 }
61 if ev.Content != nil {
62 clone.Content = []byte{:len(ev.Content)}
63 copy(clone.Content, ev.Content)
64 }
65 if ev.Sig != nil {
66 clone.Sig = []byte{:len(ev.Sig)}
67 copy(clone.Sig, ev.Sig)
68 }
69 if ev.Tags != nil {
70 clone.Tags = tag.NewS()
71 for _, tg := range ev.Tags.T {
72 if tg != nil {
73 newTag := tag.NewWithCap(len(tg.T))
74 for _, element := range tg.T {
75 el := []byte{:len(element)}
76 copy(el, element)
77 newTag.T = push(newTag.T, el)
78 }
79 clone.Tags.Append(newTag)
80 }
81 }
82 }
83 return clone
84 }
85
86 func (ev *E) EstimateSize() (size int32) {
87 size = len(ev.ID)*2 + len(ev.Pubkey)*2 + len(ev.Sig)*2 + len(ev.Content)*2
88 if ev.Tags == nil {
89 return
90 }
91 for _, v := range ev.Tags.T {
92 for _, w := range (*v).T {
93 size += len(w) * 2
94 }
95 }
96 return
97 }
98
99 func (ev *E) Marshal(dst []byte) (b []byte) {
100 b = dst
101 if b == nil {
102 b = []byte{:0:ev.EstimateSize()+100}
103 }
104 b = b | "{"
105 b = b | "\""
106 b = b | jId
107 b = b | `":"`
108 hexStart := len(b)
109 b = b | []byte{:2*sha256.Size}
110 hex.Encode(b[hexStart:], ev.ID)
111 b = b | `","`
112 b = b | jPubkey
113 b = b | `":"`
114 hexStart = len(b)
115 b = b | []byte{:2*secp256k1.PubKeyBytesLen}
116 hex.Encode(b[hexStart:], ev.Pubkey)
117 b = b | `","`
118 b = b | jCreatedAt
119 b = b | `":`
120 b = ints.New(ev.CreatedAt).Marshal(b)
121 b = b | `,"`
122 b = b | jKind
123 b = b | `":`
124 b = ints.New(ev.Kind).Marshal(b)
125 b = b | `,"`
126 b = b | jTags
127 b = b | `":`
128 if ev.Tags != nil {
129 b = ev.Tags.Marshal(b)
130 } else {
131 b = b | "[]"
132 }
133 b = b | `,"`
134 b = b | jContent
135 b = b | `":"`
136 b = text.NostrEscape(b, ev.Content)
137 b = b | `","`
138 b = b | jSig
139 b = b | `":"`
140 if len(ev.Sig) > 0 {
141 hexStart = len(b)
142 b = b | []byte{:2*secp256k1.SignatureSize}
143 hex.Encode(b[hexStart:], ev.Sig)
144 }
145 b = b | `"}`
146 return
147 }
148
149 func (ev *E) MarshalJSON() (b []byte, err error) {
150 b = ev.Marshal(nil)
151 return
152 }
153
154 func (ev *E) Serialize() (b []byte) { return ev.Marshal(nil) }
155
156 func (ev *E) Unmarshal(b []byte) (rem []byte, err error) {
157 key := []byte{:0:9}
158 for ; len(b) > 0; b = b[1:] {
159 if isWhitespace(b[0]) {
160 continue
161 }
162 if b[0] == '{' {
163 b = b[1:]
164 goto BetweenKeys
165 }
166 }
167 goto eof
168 BetweenKeys:
169 for ; len(b) > 0; b = b[1:] {
170 if isWhitespace(b[0]) {
171 continue
172 }
173 if b[0] == '"' {
174 b = b[1:]
175 goto InKey
176 }
177 }
178 goto eof
179 InKey:
180 for ; len(b) > 0; b = b[1:] {
181 if b[0] == '"' {
182 b = b[1:]
183 goto InKV
184 }
185 key = mxutil.Ensure(key, 1)
186 key = push(key, b[0])
187 }
188 goto eof
189 InKV:
190 for ; len(b) > 0; b = b[1:] {
191 if isWhitespace(b[0]) {
192 continue
193 }
194 if b[0] == ':' {
195 b = b[1:]
196 goto InVal
197 }
198 }
199 goto eof
200 InVal:
201 for len(b) > 0 && isWhitespace(b[0]) {
202 b = b[1:]
203 }
204 // An empty key is legal JSON (`{"":1}`) and switch key[0] panicked on it,
205 // so a malformed EVENT envelope crashed the relay. filter.F guards this the
206 // same way.
207 if len(key) < 1 {
208 err = errorf.E([]byte("event: empty key"))
209 return
210 }
211 switch key[0] {
212 case jId[0]:
213 if !bytes.Equal(jId, key) {
214 goto invalid
215 }
216 var id []byte
217 if id, b, err = text.UnmarshalHex(b); chk.E(err) {
218 return
219 }
220 if len(id) != sha256.Size {
221 err = errorf.E(
222 []byte("invalid id, require %d got %d"), sha256.Size, len(id),
223 )
224 return
225 }
226 ev.ID = id
227 goto BetweenKV
228 case jPubkey[0]:
229 if !bytes.Equal(jPubkey, key) {
230 goto invalid
231 }
232 var pk []byte
233 if pk, b, err = text.UnmarshalHex(b); chk.E(err) {
234 return
235 }
236 if len(pk) != secp256k1.PubKeyBytesLen {
237 err = errorf.E(
238 []byte("invalid pubkey, require %d got %d"),
239 secp256k1.PubKeyBytesLen, len(pk),
240 )
241 return
242 }
243 ev.Pubkey = pk
244 goto BetweenKV
245 case jKind[0]:
246 if !bytes.Equal(jKind, key) {
247 goto invalid
248 }
249 k := kind.New(0)
250 if b, err = k.Unmarshal(b); chk.E(err) {
251 return
252 }
253 ev.Kind = k.ToU16()
254 goto BetweenKV
255 case jTags[0]:
256 if !bytes.Equal(jTags, key) {
257 goto invalid
258 }
259 ev.Tags = tag.NewS()
260 if b, err = ev.Tags.Unmarshal(b); chk.E(err) {
261 return
262 }
263 goto BetweenKV
264 case jSig[0]:
265 if !bytes.Equal(jSig, key) {
266 goto invalid
267 }
268 var sig []byte
269 if sig, b, err = text.UnmarshalHex(b); chk.E(err) {
270 return
271 }
272 if len(sig) != 0 && len(sig) != secp256k1.SignatureSize {
273 sig = nil
274 }
275 ev.Sig = sig
276 goto BetweenKV
277 case jContent[0]:
278 if key[1] == jContent[1] {
279 if !bytes.Equal(jContent, key) {
280 goto invalid
281 }
282 if ev.Content, b, err = text.UnmarshalQuoted(b); chk.T(err) {
283 return
284 }
285 goto BetweenKV
286 } else if key[1] == jCreatedAt[1] {
287 if !bytes.Equal(jCreatedAt, key) {
288 goto invalid
289 }
290 i := ints.New(0)
291 if b, err = i.Unmarshal(b); chk.T(err) {
292 return
293 }
294 ev.CreatedAt = i.Int64()
295 goto BetweenKV
296 } else {
297 goto invalid
298 }
299 default:
300 goto invalid
301 }
302 BetweenKV:
303 key = key[:0]
304 for ; len(b) > 0; b = b[1:] {
305 if isWhitespace(b[0]) {
306 continue
307 }
308 switch {
309 case len(b) == 0:
310 return
311 case b[0] == '}':
312 b = b[1:]
313 goto AfterClose
314 case b[0] == ',':
315 b = b[1:]
316 goto BetweenKeys
317 case b[0] == '"':
318 b = b[1:]
319 goto InKey
320 }
321 }
322 goto AfterClose
323 AfterClose:
324 rem = b
325 return
326 invalid:
327 err = fmt.Errorf(
328 "invalid key,\n'%s'\n'%s'\n'%s'", string(b), string(b[:]),
329 string(b),
330 )
331 return
332 eof:
333 err = io.EOF
334 return
335 }
336
337 func (ev *E) UnmarshalJSON(b []byte) (err error) {
338 _, err = ev.Unmarshal(b)
339 return
340 }
341
342 func isWhitespace(b byte) (ok bool) {
343 return b == ' ' || b == '\t' || b == '\n' || b == '\r'
344 }
345
346 // S is an array of event.E that sorts in reverse chronological order. It is a
347 // struct holding the slice, not a named slice: methods belong on the struct
348 // that holds the slice (moxie bans named slice types).
349 type S struct {
350 E []*E
351 }
352
353 func (ev *S) Len() (n int32) { return len(ev.E) }
354 func (ev *S) Less(i, j int32) (ok bool) { return ev.E[i].CreatedAt > ev.E[j].CreatedAt }
355 func (ev *S) Swap(i, j int32) { ev.E[i], ev.E[j] = ev.E[j], ev.E[i] }
356
357 type C chan *E
358