-
Notifications
You must be signed in to change notification settings - Fork 108
/
event.go
174 lines (152 loc) · 5.12 KB
/
event.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package nostr
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/mailru/easyjson"
)
type Event struct {
ID string
PubKey string
CreatedAt Timestamp
Kind int
Tags Tags
Content string
Sig string
// anything here will be mashed together with the main event object when serializing
extra map[string]any
}
const (
KindProfileMetadata int = 0
KindTextNote int = 1
KindRecommendServer int = 2
KindContactList int = 3
KindEncryptedDirectMessage int = 4
KindDeletion int = 5
KindRepost int = 6
KindReaction int = 7
KindSimpleGroupChatMessage int = 9
KindSimpleGroupThread int = 11
KindSimpleGroupReply int = 12
KindChannelCreation int = 40
KindChannelMetadata int = 41
KindChannelMessage int = 42
KindChannelHideMessage int = 43
KindChannelMuteUser int = 44
KindPatch int = 1617
KindFileMetadata int = 1063
KindSimpleGroupAddUser int = 9000
KindSimpleGroupRemoveUser int = 9001
KindSimpleGroupEditMetadata int = 9002
KindSimpleGroupAddPermission int = 9003
KindSimpleGroupRemovePermission int = 9004
KindSimpleGroupDeleteEvent int = 9005
KindSimpleGroupEditGroupStatus int = 9006
KindSimpleGroupCreateGroup int = 9007
KindSimpleGroupDeleteGroup int = 9008
KindSimpleGroupJoinRequest int = 9021
KindZapRequest int = 9734
KindZap int = 9735
KindMuteList int = 10000
KindPinList int = 10001
KindRelayListMetadata int = 10002
KindNWCWalletInfo int = 13194
KindClientAuthentication int = 22242
KindNWCWalletRequest int = 23194
KindNWCWalletResponse int = 23195
KindNostrConnect int = 24133
KindCategorizedPeopleList int = 30000
KindCategorizedBookmarksList int = 30001
KindProfileBadges int = 30008
KindBadgeDefinition int = 30009
KindStallDefinition int = 30017
KindProductDefinition int = 30018
KindArticle int = 30023
KindApplicationSpecificData int = 30078
KindRepositoryAnnouncement int = 30617
KindSimpleGroupMetadata int = 39000
KindSimpleGroupAdmins int = 39001
KindSimpleGroupMembers int = 39002
)
// Event Stringer interface, just returns the raw JSON as a string.
func (evt Event) String() string {
j, _ := easyjson.Marshal(evt)
return string(j)
}
// GetID serializes and returns the event ID as a string.
func (evt *Event) GetID() string {
h := sha256.Sum256(evt.Serialize())
return hex.EncodeToString(h[:])
}
// Serialize outputs a byte array that can be hashed/signed to identify/authenticate.
// JSON encoding as defined in RFC4627.
func (evt *Event) Serialize() []byte {
// the serialization process is just putting everything into a JSON array
// so the order is kept. See NIP-01
dst := make([]byte, 0)
// the header portion is easy to serialize
// [0,"pubkey",created_at,kind,[
dst = append(dst, []byte(
fmt.Sprintf(
"[0,\"%s\",%d,%d,",
evt.PubKey,
evt.CreatedAt,
evt.Kind,
))...)
// tags
dst = evt.Tags.marshalTo(dst)
dst = append(dst, ',')
// content needs to be escaped in general as it is user generated.
dst = escapeString(dst, evt.Content)
dst = append(dst, ']')
return dst
}
// CheckSignature checks if the signature is valid for the id
// (which is a hash of the serialized event content).
// returns an error if the signature itself is invalid.
func (evt Event) CheckSignature() (bool, error) {
// read and check pubkey
pk, err := hex.DecodeString(evt.PubKey)
if err != nil {
return false, fmt.Errorf("event pubkey '%s' is invalid hex: %w", evt.PubKey, err)
}
pubkey, err := schnorr.ParsePubKey(pk)
if err != nil {
return false, fmt.Errorf("event has invalid pubkey '%s': %w", evt.PubKey, err)
}
// read signature
s, err := hex.DecodeString(evt.Sig)
if err != nil {
return false, fmt.Errorf("signature '%s' is invalid hex: %w", evt.Sig, err)
}
sig, err := schnorr.ParseSignature(s)
if err != nil {
return false, fmt.Errorf("failed to parse signature: %w", err)
}
// check signature
hash := sha256.Sum256(evt.Serialize())
return sig.Verify(hash[:], pubkey), nil
}
// Sign signs an event with a given privateKey.
func (evt *Event) Sign(privateKey string, signOpts ...schnorr.SignOption) error {
s, err := hex.DecodeString(privateKey)
if err != nil {
return fmt.Errorf("Sign called with invalid private key '%s': %w", privateKey, err)
}
if evt.Tags == nil {
evt.Tags = make(Tags, 0)
}
sk, pk := btcec.PrivKeyFromBytes(s)
pkBytes := pk.SerializeCompressed()
evt.PubKey = hex.EncodeToString(pkBytes[1:])
h := sha256.Sum256(evt.Serialize())
sig, err := schnorr.Sign(sk, h[:], signOpts...)
if err != nil {
return err
}
evt.ID = hex.EncodeToString(h[:])
evt.Sig = hex.EncodeToString(sig.Serialize())
return nil
}