canonical.go raw
1 package event
2
3 import (
4 "next.orly.dev/pkg/nostr/encoders/hex"
5 "next.orly.dev/pkg/nostr/encoders/ints"
6 "next.orly.dev/pkg/nostr/encoders/text"
7 "github.com/minio/sha256-simd"
8 )
9
10 // ToCanonical converts the event to the canonical encoding used to derive the
11 // event ID.
12 func (ev *E) ToCanonical(dst []byte) (b []byte) {
13 b = dst
14 // Pre-allocate buffer if nil to reduce reallocations
15 if b == nil {
16 // Estimate size: [0," + hex(pubkey) + "," + timestamp + "," + kind + "," + tags + "," + content + ]
17 estimatedSize := 5 + 2*len(ev.Pubkey) + 20 + 10 + 100
18 if ev.Tags != nil {
19 for _, tag := range *ev.Tags {
20 for _, elem := range tag.T {
21 estimatedSize += len(elem)*2 + 10 // escaped element + overhead
22 }
23 }
24 }
25 estimatedSize += len(ev.Content)*2 + 10 // escaped content + overhead
26 b = make([]byte, 0, estimatedSize)
27 }
28 b = append(b, "[0,\""...)
29 b = hex.EncAppend(b, ev.Pubkey)
30 b = append(b, "\","...)
31 b = ints.New(ev.CreatedAt).Marshal(b)
32 b = append(b, ',')
33 b = ints.New(ev.Kind).Marshal(b)
34 b = append(b, ',')
35 if ev.Tags != nil {
36 b = ev.Tags.Marshal(b)
37 } else {
38 b = append(b, '[')
39 b = append(b, ']')
40 }
41 b = append(b, ',')
42 b = text.AppendQuote(b, ev.Content, text.NostrEscape)
43 b = append(b, ']')
44 return
45 }
46
47 // GetIDBytes returns the raw SHA256 hash of the canonical form of an event.E.
48 func (ev *E) GetIDBytes() []byte { return Hash(ev.ToCanonical(nil)) }
49
50 // Hash is a little helper generate a hash and return a slice instead of an
51 // array.
52 func Hash(in []byte) (out []byte) {
53 h := sha256.Sum256(in)
54 return h[:]
55 }
56