1 //go:build !(js && wasm)
2 3 package database
4 5 import (
6 "fmt"
7 8 "next.orly.dev/pkg/lol/chk"
9 "next.orly.dev/pkg/lol/errorf"
10 "next.orly.dev/pkg/database/indexes/types"
11 "next.orly.dev/pkg/nostr/encoders/event"
12 "next.orly.dev/pkg/nostr/encoders/filter"
13 "next.orly.dev/pkg/nostr/encoders/hex"
14 "next.orly.dev/pkg/nostr/encoders/kind"
15 "next.orly.dev/pkg/nostr/encoders/tag"
16 "next.orly.dev/pkg/nostr/encoders/tag/atag"
17 "next.orly.dev/pkg/interfaces/store"
18 )
19 20 // CheckForDeleted checks if the event is deleted, and returns an error with
21 // prefix "blocked:" if it is. This function also allows designating admin
22 // pubkeys that also may delete the event, normally only the author is allowed
23 // to delete an event.
24 func (d *D) CheckForDeleted(ev *event.E, admins [][]byte) (err error) {
25 // log.T.F("CheckForDeleted: checking event %x", ev.ID)
26 keys := append([][]byte{ev.Pubkey}, admins...)
27 authors := tag.NewFromBytesSlice(keys...)
28 // if the event is addressable, check for a deletion event with the same
29 // kind/pubkey/dtag
30 if kind.IsParameterizedReplaceable(ev.Kind) {
31 var idxs []Range
32 // construct a tag
33 t := ev.Tags.GetFirst([]byte("d"))
34 a := atag.T{
35 Kind: kind.New(ev.Kind),
36 Pubkey: ev.Pubkey,
37 DTag: t.Value(),
38 }
39 at := a.Marshal(nil)
40 if idxs, err = GetIndexesFromFilter(
41 &filter.F{
42 Authors: authors,
43 Kinds: kind.NewS(kind.Deletion),
44 Tags: tag.NewS(tag.NewFromAny("#a", at)),
45 },
46 ); chk.E(err) {
47 return
48 }
49 var sers types.Uint40s
50 for _, idx := range idxs {
51 var s types.Uint40s
52 if s, err = d.GetSerialsByRange(idx); chk.E(err) {
53 return
54 }
55 sers = append(sers, s...)
56 }
57 if len(sers) > 0 {
58 // there can be multiple of these because the author/kind/tag is a
59 // stable value but refers to any event from the author, of the
60 // kind, with the identifier. so we need to fetch the full ID index
61 // to get the timestamp and ensure that the event post-dates it.
62 // otherwise, it should be rejected.
63 var idPkTss []*store.IdPkTs
64 var tmp []*store.IdPkTs
65 if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
66 return
67 }
68 idPkTss = append(idPkTss, tmp...)
69 // find the newest deletion timestamp without sorting to reduce cost
70 maxTs := idPkTss[0].Ts
71 for i := 1; i < len(idPkTss); i++ {
72 if idPkTss[i].Ts > maxTs {
73 maxTs = idPkTss[i].Ts
74 }
75 }
76 if ev.CreatedAt < maxTs {
77 err = errorf.E(
78 "blocked: %0x was deleted by address %s because it is older than the delete: event: %d delete: %d",
79 ev.ID, at, ev.CreatedAt, maxTs,
80 )
81 return
82 }
83 return
84 }
85 return
86 }
87 // if the event is replaceable, check if there is a deletion event newer
88 // than the event, it must specify the same kind/pubkey. this type of delete
89 // only has the k tag to specify the kind, it can be what an author would
90 // use, as the author is part of the replaceable event specification.
91 if kind.IsReplaceable(ev.Kind) {
92 var idxs []Range
93 if idxs, err = GetIndexesFromFilter(
94 &filter.F{
95 Authors: tag.NewFromBytesSlice(ev.Pubkey),
96 Kinds: kind.NewS(kind.Deletion),
97 Tags: tag.NewS(
98 tag.NewFromAny("#k", fmt.Sprint(ev.Kind)),
99 ),
100 },
101 ); chk.E(err) {
102 return
103 }
104 var sers types.Uint40s
105 for _, idx := range idxs {
106 var s types.Uint40s
107 if s, err = d.GetSerialsByRange(idx); chk.E(err) {
108 return
109 }
110 sers = append(sers, s...)
111 }
112 if len(sers) > 0 {
113 var idPkTss []*store.IdPkTs
114 var tmp []*store.IdPkTs
115 if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
116 return
117 }
118 idPkTss = append(idPkTss, tmp...)
119 // find the newest deletion without sorting to reduce cost
120 maxTs := idPkTss[0].Ts
121 maxId := idPkTss[0].Id
122 for i := 1; i < len(idPkTss); i++ {
123 if idPkTss[i].Ts > maxTs {
124 maxTs = idPkTss[i].Ts
125 maxId = idPkTss[i].Id
126 }
127 }
128 if ev.CreatedAt < maxTs {
129 err = fmt.Errorf(
130 "blocked: %0x was deleted: the event is older than the delete event %0x: event: %d delete: %d",
131 ev.ID, maxId, ev.CreatedAt, maxTs,
132 )
133 return
134 }
135 }
136 // this type of delete can also use an a tag to specify kind and
137 // author, which would be required for admin deletes
138 idxs = nil
139 // construct a tag
140 a := atag.T{
141 Kind: kind.New(ev.Kind),
142 Pubkey: ev.Pubkey,
143 }
144 at := a.Marshal(nil)
145 if idxs, err = GetIndexesFromFilter(
146 &filter.F{
147 Authors: authors,
148 Kinds: kind.NewS(kind.Deletion),
149 Tags: tag.NewS(tag.NewFromAny("#a", at)),
150 },
151 ); chk.E(err) {
152 return
153 }
154 sers = nil
155 for _, idx := range idxs {
156 var s types.Uint40s
157 if s, err = d.GetSerialsByRange(idx); chk.E(err) {
158 return
159 }
160 sers = append(sers, s...)
161 }
162 if len(sers) > 0 {
163 var idPkTss []*store.IdPkTs
164 var tmp []*store.IdPkTs
165 if tmp, err = d.GetFullIdPubkeyBySerials(sers); chk.E(err) {
166 return
167 }
168 idPkTss = append(idPkTss, tmp...)
169 // find the newest deletion without sorting to reduce cost
170 maxTs := idPkTss[0].Ts
171 // maxId := idPkTss[0].Id
172 for i := 1; i < len(idPkTss); i++ {
173 if idPkTss[i].Ts > maxTs {
174 maxTs = idPkTss[i].Ts
175 // maxId = idPkTss[i].Id
176 }
177 }
178 if ev.CreatedAt < maxTs {
179 err = errorf.E(
180 "blocked: %0x was deleted by address %s because it is older than the delete: event: %d delete: %d",
181 ev.ID, at, ev.CreatedAt, maxTs,
182 )
183 return
184 }
185 return
186 }
187 return
188 }
189 // otherwise we check for a delete by event id
190 // log.T.F("CheckForDeleted: checking for e-tag deletion of event %x", ev.ID)
191 // log.T.F("CheckForDeleted: authors filter: %v", authors)
192 // log.T.F("CheckForDeleted: looking for tag e with value: %s", hex.Enc(ev.ID))
193 var idxs []Range
194 if idxs, err = GetIndexesFromFilter(
195 &filter.F{
196 Authors: authors,
197 Kinds: kind.NewS(kind.Deletion),
198 Tags: tag.NewS(
199 tag.NewFromAny("e", hex.Enc(ev.ID)),
200 ),
201 },
202 ); chk.E(err) {
203 return
204 }
205 // log.T.F("CheckForDeleted: found %d indexes", len(idxs))
206 var sers types.Uint40s
207 for _, idx := range idxs {
208 // log.T.F("CheckForDeleted: checking index %d: %v", i, idx)
209 var s types.Uint40s
210 if s, err = d.GetSerialsByRange(idx); chk.E(err) {
211 return
212 }
213 // log.T.F("CheckForDeleted: index %d returned %d serials", i, len(s))
214 if len(s) > 0 {
215 // Any e-tag deletion found means the exact event was deleted and cannot be resubmitted
216 // log.T.F("CheckForDeleted: found e-tag deletion for event %x", ev.ID)
217 err = errorf.E("blocked: %0x has been deleted", ev.ID)
218 return
219 }
220 }
221 if len(sers) > 0 {
222 // Any e-tag deletion found means the exact event was deleted and cannot be resubmitted
223 err = errorf.E("blocked: %0x has been deleted", ev.ID)
224 return
225 }
226 227 return
228 }
229