mostly because some clients use p tags in follow lists for whatever reason. follow lists are generally fetched by pubkey+kind so the missing index because it's not a pubkey is really nbd, and pubkey tag searches will also work with the kind.
168 lines
3.9 KiB
Go
168 lines
3.9 KiB
Go
package event
|
|
|
|
import (
|
|
"io"
|
|
|
|
"x.realy.lol/chk"
|
|
"x.realy.lol/ec/schnorr"
|
|
"x.realy.lol/hex"
|
|
"x.realy.lol/timestamp"
|
|
"x.realy.lol/varint"
|
|
)
|
|
|
|
// todo: maybe we should make e and p tag values binary to reduce space usage
|
|
|
|
// MarshalWrite writes a binary encoding of an event.
|
|
//
|
|
// NOTE: Event must not be nil or this will panic. Use event.New.
|
|
//
|
|
// [ 32 bytes Id ]
|
|
// [ 32 bytes Pubkey ]
|
|
// [ varint CreatedAt ]
|
|
// [ 2 bytes Kind ]
|
|
// [ varint Tags length ]
|
|
//
|
|
// [ varint tag length ]
|
|
// [ varint tag element length ]
|
|
// [ tag element data ]
|
|
// ...
|
|
//
|
|
// [ varint Content length ]
|
|
// [ 64 bytes Sig ]
|
|
func (ev *E) MarshalWrite(w io.Writer) (err error) {
|
|
if ev == nil {
|
|
panic("cannot marshal a nil event")
|
|
}
|
|
_, _ = w.Write(ev.GetIdBytes())
|
|
_, _ = w.Write(ev.GetPubkeyBytes())
|
|
varint.Encode(w, uint64(ev.CreatedAt))
|
|
varint.Encode(w, uint64(ev.Kind))
|
|
varint.Encode(w, uint64(len(ev.Tags)))
|
|
for _, x := range ev.Tags {
|
|
varint.Encode(w, uint64(len(x)))
|
|
// e and p tag values should be hex
|
|
var isBin bool
|
|
if len(x) > 1 && (x[0] == "e" || x[0] == "p") {
|
|
isBin = true
|
|
}
|
|
for i, y := range x {
|
|
if i == 1 && isBin {
|
|
var b []byte
|
|
if b, err = hex.Dec(y); err != nil {
|
|
b = []byte(y)
|
|
// non-hex "p" or "e" tags have a 1 prefix to indicate not to hex decode.
|
|
_, _ = w.Write([]byte{1})
|
|
err = nil
|
|
} else {
|
|
if len(b) != 32 {
|
|
// err = errorf.E("e or p tag value with invalid decoded byte length %d '%0x'", len(b), b)
|
|
b = []byte(y)
|
|
_, _ = w.Write([]byte{1})
|
|
} else {
|
|
// hex values have a 2 prefix
|
|
_, _ = w.Write([]byte{2})
|
|
}
|
|
}
|
|
varint.Encode(w, uint64(len(b)))
|
|
_, _ = w.Write(b)
|
|
} else {
|
|
varint.Encode(w, uint64(len(y)))
|
|
_, _ = w.Write([]byte(y))
|
|
}
|
|
}
|
|
}
|
|
varint.Encode(w, uint64(len(ev.Content)))
|
|
_, _ = w.Write([]byte(ev.Content))
|
|
_, _ = w.Write(ev.GetSigBytes())
|
|
return err
|
|
}
|
|
|
|
// UnmarshalRead decodes an event in binary form into an allocated event struct.
|
|
//
|
|
// NOTE: Event must not be nil or this will panic. Use event.New.
|
|
func (ev *E) UnmarshalRead(r io.Reader) (err error) {
|
|
if ev == nil {
|
|
panic("cannot unmarshal into nil event struct")
|
|
}
|
|
id := make([]byte, 32)
|
|
if _, err = r.Read(id); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Id = hex.Enc(id)
|
|
pubkey := make([]byte, 32)
|
|
if _, err = r.Read(pubkey); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Pubkey = hex.Enc(pubkey)
|
|
var ca uint64
|
|
if ca, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
ev.CreatedAt = timestamp.New(ca)
|
|
var k uint64
|
|
if k, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Kind = int(k)
|
|
var nTags uint64
|
|
if nTags, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
for range nTags {
|
|
var nField uint64
|
|
if nField, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
var t []string
|
|
var isBin bool
|
|
for i := range nField {
|
|
var pr byte
|
|
if i == 1 && isBin {
|
|
prf := make([]byte, 1)
|
|
if _, err = r.Read(prf); chk.E(err) {
|
|
return
|
|
}
|
|
pr = prf[0]
|
|
}
|
|
var lenField uint64
|
|
if lenField, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
field := make([]byte, lenField)
|
|
if _, err = r.Read(field); chk.E(err) {
|
|
return
|
|
}
|
|
// if it is first field, length 1 and is e or p, the value field should be binary
|
|
if i == 0 && len(field) == 1 && (field[0] == 'e' || field[0] == 'p') {
|
|
isBin = true
|
|
}
|
|
if i == 1 && isBin {
|
|
if pr == 2 {
|
|
// this is a binary value, was an e or p tag key, 32 bytes long, encode
|
|
// value field to hex
|
|
f := make([]byte, 64)
|
|
_ = hex.EncBytes(f, field)
|
|
field = f
|
|
}
|
|
}
|
|
t = append(t, string(field))
|
|
}
|
|
ev.Tags = append(ev.Tags, t)
|
|
}
|
|
var cLen uint64
|
|
if cLen, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
content := make([]byte, cLen)
|
|
if _, err = r.Read(content); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Content = string(content)
|
|
sig := make([]byte, schnorr.SignatureSize)
|
|
if _, err = r.Read(sig); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Sig = hex.Enc(sig)
|
|
return
|
|
}
|