protowire.mx raw
1 // Package protowire binds the musiquay protocol vocabulary to the relay's
2 // native event type. The vocabulary is a leaf (protocol imports only runtime,
3 // errorf and text), so this package may import it without dragging the relay
4 // into the vocabulary: it imports nothing from morly.
5 //
6 // Two jobs live here. The encoding job turns an event's tag list into the
7 // [][]string the protocol parsers take. The policy job answers what a
8 // blob-bearing event's asset address is, which the vocabulary itself cannot
9 // know: it depends on the event kind.
10 package protowire
11
12 import (
13 "git.smesh.lol/musiquay/pkg/protocol"
14 "git.smesh.lol/nostr/pkg/event"
15 )
16
17 // TagsOf copies an event's tag list into the [][]string shape the protocol
18 // parsers take.
19 //
20 // The outer slice and every inner row are freshly allocated and presized:
21 // ev.Tags.T already knows both lengths, so there is nothing to grow and no
22 // push. A nil tag entry is left as a nil row rather than copied, since a
23 // parser skips rows shorter than one element anyway.
24 func TagsOf(ev *event.E) (tags [][]string) {
25 if ev == nil || ev.Tags == nil {
26 return nil
27 }
28 src := ev.Tags.T
29 tags = [][]string{:len(src)}
30 for i := range src {
31 tg := src[i]
32 if tg == nil {
33 continue
34 }
35 row := []string{:len(tg.T)}
36 for j := range tg.T {
37 row[j] = tg.T[j]
38 }
39 tags[i] = row
40 }
41 return
42 }
43
44 // ValidateEvent parses and validates a payload by kind.
45 //
46 // Every kind the protocol defines and has a parser for is checked. Kinds the
47 // protocol defines without a parser, and every kind outside the vocabulary,
48 // return nil: the relay stores a payload this build has no rule for rather
49 // than rejecting it. A Musiquay kind added before its parser must therefore
50 // fall through here, never reach a nil parser.
51 func ValidateEvent(ev *event.E) (err error) {
52 if ev == nil {
53 return nil
54 }
55 tags := TagsOf(ev)
56 content := string(ev.Content)
57 switch ev.Kind {
58 case protocol.KindTrack:
59 v, perr := protocol.ParseTrack(tags, content)
60 if perr != nil {
61 return perr
62 }
63 return v.Validate()
64 case protocol.KindAlbum:
65 v, perr := protocol.ParseAlbum(tags, content)
66 if perr != nil {
67 return perr
68 }
69 return v.Validate()
70 case protocol.KindPlaylist:
71 v, perr := protocol.ParsePlaylist(tags, content)
72 if perr != nil {
73 return perr
74 }
75 return v.Validate()
76 case protocol.KindSetlist:
77 v, perr := protocol.ParseSetlist(tags, content)
78 if perr != nil {
79 return perr
80 }
81 return v.Validate()
82 case protocol.KindReleaseCredits:
83 v, perr := protocol.ParseReleaseCredits(tags, content)
84 if perr != nil {
85 return perr
86 }
87 return v.Validate()
88 case protocol.KindTrackCredits:
89 v, perr := protocol.ParseTrackCredits(tags, content)
90 if perr != nil {
91 return perr
92 }
93 return v.Validate()
94 case protocol.KindEdition:
95 v, perr := protocol.ParseEdition(tags, content)
96 if perr != nil {
97 return perr
98 }
99 return v.Validate()
100 case protocol.KindAccessFilter:
101 v, perr := protocol.ParseAccessFilter(tags, content)
102 if perr != nil {
103 return perr
104 }
105 return v.Validate()
106 case protocol.KindPlayReport:
107 v, perr := protocol.ParsePlayReport(tags, content)
108 if perr != nil {
109 return perr
110 }
111 return v.Validate()
112 case protocol.KindPurchaseOrder:
113 v, perr := protocol.ParsePurchaseOrder(tags, content)
114 if perr != nil {
115 return perr
116 }
117 return v.Validate()
118 case protocol.KindDeliveryReceipt:
119 v, perr := protocol.ParseDeliveryReceipt(tags, content)
120 if perr != nil {
121 return perr
122 }
123 return v.Validate()
124 case protocol.KindHostAggregate:
125 v, perr := protocol.ParseHostAggregate(tags, content)
126 if perr != nil {
127 return perr
128 }
129 return v.Validate()
130 case protocol.KindPublisherRollup:
131 v, perr := protocol.ParsePublisherRollup(tags, content)
132 if perr != nil {
133 return perr
134 }
135 return v.Validate()
136 case protocol.KindCreditLabel:
137 v, perr := protocol.ParseCreditLabel(tags, content)
138 if perr != nil {
139 return perr
140 }
141 return v.Validate()
142 case protocol.KindComment:
143 v, perr := protocol.ParseContribution(tags, content)
144 if perr != nil {
145 return perr
146 }
147 return v.Validate()
148 case protocol.KindCalendar:
149 v, perr := protocol.ParseCalendarEvent(tags, content)
150 if perr != nil {
151 return perr
152 }
153 return v.Validate()
154 case protocol.KindRSVP:
155 v, perr := protocol.ParseRSVP(tags, content)
156 if perr != nil {
157 return perr
158 }
159 return v.Validate()
160 // The NIP-15 payloads are JSON in content with no payload tags, so their
161 // parsers take content alone.
162 case protocol.KindStall:
163 v, perr := protocol.ParseStall(content)
164 if perr != nil {
165 return perr
166 }
167 return v.Validate()
168 case protocol.KindProduct:
169 v, perr := protocol.ParseProduct(content)
170 if perr != nil {
171 return perr
172 }
173 return v.Validate()
174 case protocol.KindListing:
175 v, perr := protocol.ParseListing(tags, content)
176 if perr != nil {
177 return perr
178 }
179 return v.Validate()
180 }
181 return nil
182 }
183
184 // AssetOf returns the asset address a blob-bearing event belongs to: the first
185 // a tag for a track and a delivery receipt, and the asset inside the d value
186 // for an access filter. Anything else has no asset here.
187 //
188 // An access filter whose d is a subscription tier (sub:<retailer>:<channel>)
189 // names no asset, and so does one whose d fails to parse; both return ok=false
190 // rather than an empty string that a caller might index.
191 func AssetOf(ev *event.E) (asset string, ok bool) {
192 if ev == nil {
193 return "", false
194 }
195 switch ev.Kind {
196 case protocol.KindTrack, protocol.KindDeliveryReceipt:
197 tv := ev.Tags.GetFirst([]byte("a"))
198 if tv == nil || tv.Len() < 2 {
199 return "", false
200 }
201 return string(tv.Value()), true
202 case protocol.KindAccessFilter:
203 dv := ev.Tags.GetFirst([]byte("d"))
204 if dv == nil || dv.Len() < 2 {
205 return "", false
206 }
207 tier, perr := protocol.ParseFilterD(string(dv.Value()))
208 if perr != nil || tier.Asset == nil {
209 return "", false
210 }
211 return tier.Asset.String(), true
212 }
213 return "", false
214 }
215