event.mx raw
1 package nostr
2
3 import (
4 "smesh.lol/web/common/helpers"
5 "smesh.lol/web/common/jsbridge/schnorr"
6 )
7
8 // Event is a Nostr event (NIP-01).
9 type Event struct {
10 ID string `json:"id"`
11 PubKey string `json:"pubkey"`
12 CreatedAt int64 `json:"created_at"`
13 Kind int `json:"kind"`
14 Tags Tags `json:"tags"`
15 Content string `json:"content"`
16 Sig string `json:"sig"`
17 }
18
19 // Serialize returns the canonical JSON array for ID computation:
20 // [0,<pubkey>,<created_at>,<kind>,<tags>,<content>]
21 func (e *Event) Serialize() string {
22 buf := []byte{:0:256}
23 buf = append(buf, "[0,"...)
24 buf = append(buf, helpers.JsonString(e.PubKey)...)
25 buf = append(buf, ',')
26 buf = append(buf, helpers.Itoa(e.CreatedAt)...)
27 buf = append(buf, ',')
28 buf = append(buf, helpers.Itoa(int64(e.Kind))...)
29 buf = append(buf, ',')
30 buf = serializeTags(buf, e.Tags)
31 buf = append(buf, ',')
32 buf = append(buf, helpers.JsonString(e.Content)...)
33 buf = append(buf, ']')
34 return string(buf)
35 }
36
37 // ComputeID computes and sets the event ID (SHA-256 of serialized form).
38 func (e *Event) ComputeID() string {
39 ser := e.Serialize()
40 hash := schnorr.SHA256Sum([]byte(ser))
41 e.ID = helpers.HexEncode(hash)
42 return e.ID
43 }
44
45 // CheckID verifies the event ID matches the content.
46 func (e *Event) CheckID() bool {
47 ser := e.Serialize()
48 hash := schnorr.SHA256Sum([]byte(ser))
49 expected := helpers.HexEncode(hash)
50 return e.ID == expected
51 }
52
53 // Sign computes the event ID and signs it with the given secret key.
54 // Sets ID, PubKey, and Sig. Returns false on failure.
55 func (e *Event) Sign(seckey [32]byte, auxRand [32]byte) bool {
56 pk, ok := schnorr.PubKeyFromSecKey(seckey[:])
57 if !ok {
58 return false
59 }
60 e.PubKey = helpers.HexEncode(pk)
61 e.ComputeID()
62 idBytes := helpers.HexDecode(e.ID)
63 if len(idBytes) != 32 {
64 return false
65 }
66 sig, ok := schnorr.SignSchnorr(seckey[:], idBytes, auxRand[:])
67 if !ok {
68 return false
69 }
70 e.Sig = helpers.HexEncode(sig)
71 return true
72 }
73
74 // CheckSig verifies the event signature against its pubkey and ID.
75 func (e *Event) CheckSig() bool {
76 pk := helpers.HexDecode(e.PubKey)
77 if len(pk) != 32 {
78 return false
79 }
80 id := helpers.HexDecode(e.ID)
81 if len(id) != 32 {
82 return false
83 }
84 if len(e.Sig) != 128 {
85 return false
86 }
87 sig := helpers.HexDecode(e.Sig)
88 if len(sig) != 64 {
89 return false
90 }
91 return schnorr.VerifySchnorr(pk, id, sig)
92 }
93
94 // ToJSON returns the event as a JSON object string.
95 func (e *Event) ToJSON() string {
96 buf := []byte{:0:512}
97 buf = append(buf, `{"id":`...)
98 buf = append(buf, helpers.JsonString(e.ID)...)
99 buf = append(buf, `,"pubkey":`...)
100 buf = append(buf, helpers.JsonString(e.PubKey)...)
101 buf = append(buf, `,"created_at":`...)
102 buf = append(buf, helpers.Itoa(e.CreatedAt)...)
103 buf = append(buf, `,"kind":`...)
104 buf = append(buf, helpers.Itoa(int64(e.Kind))...)
105 buf = append(buf, `,"tags":`...)
106 buf = serializeTags(buf, e.Tags)
107 buf = append(buf, `,"content":`...)
108 buf = append(buf, helpers.JsonString(e.Content)...)
109 buf = append(buf, `,"sig":`...)
110 buf = append(buf, helpers.JsonString(e.Sig)...)
111 buf = append(buf, '}')
112 return string(buf)
113 }
114
115 func serializeTags(buf []byte, tags Tags) []byte {
116 buf = append(buf, '[')
117 for i, tag := range tags {
118 if i > 0 {
119 buf = append(buf, ',')
120 }
121 buf = append(buf, '[')
122 for j, s := range tag {
123 if j > 0 {
124 buf = append(buf, ',')
125 }
126 buf = append(buf, helpers.JsonString(s)...)
127 }
128 buf = append(buf, ']')
129 }
130 buf = append(buf, ']')
131 return buf
132 }
133