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