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