implemented event and req
This commit is contained in:
@@ -21,7 +21,7 @@ const L = "REQ"
|
||||
// newly received events after it returns an eoseenvelope.T.
|
||||
type T struct {
|
||||
Subscription []byte
|
||||
Filters filter.S
|
||||
Filters *filter.S
|
||||
}
|
||||
|
||||
var _ codec.Envelope = (*T)(nil)
|
||||
@@ -32,14 +32,14 @@ func New() *T { return new(T) }
|
||||
|
||||
// NewFrom creates a new reqenvelope.T with a provided subscription.Id and
|
||||
// filters.T.
|
||||
func NewFrom(id []byte, ff filter.S) *T {
|
||||
func NewFrom(id []byte, ff *filter.S) *T {
|
||||
return &T{
|
||||
Subscription: id,
|
||||
Filters: ff,
|
||||
}
|
||||
}
|
||||
|
||||
func NewWithId[V string | []byte](id V, ff filter.S) (sub *T) {
|
||||
func NewWithId[V string | []byte](id V, ff *filter.S) (sub *T) {
|
||||
return &T{
|
||||
Subscription: []byte(id),
|
||||
Filters: ff,
|
||||
@@ -69,7 +69,7 @@ func (en *T) Marshal(dst []byte) (b []byte) {
|
||||
o = append(o, '"')
|
||||
o = append(o, en.Subscription...)
|
||||
o = append(o, '"')
|
||||
for _, f := range en.Filters {
|
||||
for _, f := range *en.Filters {
|
||||
o = append(o, ',')
|
||||
o = f.Marshal(o)
|
||||
}
|
||||
@@ -90,6 +90,7 @@ func (en *T) Unmarshal(b []byte) (r []byte, err error) {
|
||||
if r, err = text.Comma(r); chk.E(err) {
|
||||
return
|
||||
}
|
||||
en.Filters = new(filter.S)
|
||||
if r, err = en.Filters.Unmarshal(r); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ func (ev *E) MarshalBinary(w io.Writer) {
|
||||
varint.Encode(w, uint64(ev.CreatedAt))
|
||||
varint.Encode(w, uint64(ev.Kind))
|
||||
varint.Encode(w, uint64(ev.Tags.Len()))
|
||||
for _, x := range ev.Tags.ToSliceOfTags() {
|
||||
for _, x := range *ev.Tags {
|
||||
varint.Encode(w, uint64(x.Len()))
|
||||
for _, y := range x.ToSliceOfBytes() {
|
||||
for _, y := range x.T {
|
||||
varint.Encode(w, uint64(len(y)))
|
||||
_, _ = w.Write(y)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoders.orly/hex"
|
||||
"encoders.orly/ints"
|
||||
"encoders.orly/text"
|
||||
"lol.mleku.dev/log"
|
||||
)
|
||||
|
||||
// ToCanonical converts the event to the canonical encoding used to derive the
|
||||
@@ -14,14 +15,15 @@ func (ev *E) ToCanonical(dst []byte) (b []byte) {
|
||||
b = append(b, "[0,\""...)
|
||||
b = hex.EncAppend(b, ev.Pubkey)
|
||||
b = append(b, "\","...)
|
||||
b = ints.New(ev.CreatedAt).Marshal(nil)
|
||||
b = ints.New(ev.CreatedAt).Marshal(b)
|
||||
b = append(b, ',')
|
||||
b = ints.New(ev.Kind).Marshal(nil)
|
||||
b = ints.New(ev.Kind).Marshal(b)
|
||||
b = append(b, ',')
|
||||
b = ev.Tags.Marshal(b)
|
||||
b = append(b, ',')
|
||||
b = text.AppendQuote(b, ev.Content, text.NostrEscape)
|
||||
b = append(b, ']')
|
||||
log.D.F("canonical: %s", b)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"crypto.orly/ec/schnorr"
|
||||
"crypto.orly/sha256"
|
||||
"encoders.orly/event"
|
||||
"encoders.orly/ints"
|
||||
"encoders.orly/kind"
|
||||
"encoders.orly/tag"
|
||||
@@ -100,6 +101,50 @@ func (f *F) Sort() {
|
||||
}
|
||||
}
|
||||
|
||||
// MatchesIgnoringTimestampConstraints checks a filter against an event and
|
||||
// determines if the event matches the filter, ignoring timestamp constraints..
|
||||
func (f *F) MatchesIgnoringTimestampConstraints(ev *event.E) bool {
|
||||
if ev == nil {
|
||||
return false
|
||||
}
|
||||
if f.Ids.Len() > 0 && !f.Ids.Contains(ev.ID) {
|
||||
return false
|
||||
}
|
||||
if f.Kinds.Len() > 0 && !f.Kinds.Contains(ev.Kind) {
|
||||
return false
|
||||
}
|
||||
if f.Authors.Len() > 0 && !f.Authors.Contains(ev.Pubkey) {
|
||||
return false
|
||||
}
|
||||
if f.Tags.Len() > 0 {
|
||||
for _, v := range *f.Tags {
|
||||
if v.Len() < 2 {
|
||||
continue
|
||||
}
|
||||
key := v.Key()
|
||||
values := v.T[1:]
|
||||
if !ev.Tags.ContainsAny(key, values) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Matches checks a filter against an event and determines if the event matches the filter.
|
||||
func (f *F) Matches(ev *event.E) (match bool) {
|
||||
if !f.MatchesIgnoringTimestampConstraints(ev) {
|
||||
return
|
||||
}
|
||||
if f.Since.Int() != 0 && ev.CreatedAt < f.Since.I64() {
|
||||
return
|
||||
}
|
||||
if f.Until.Int() != 0 && ev.CreatedAt > f.Until.I64() {
|
||||
return
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Marshal a filter into raw JSON bytes, minified. The field ordering and sort
|
||||
// of fields is canonicalized so that a hash can identify the same filter.
|
||||
func (f *F) Marshal(dst []byte) (b []byte) {
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
package filter
|
||||
|
||||
import (
|
||||
"encoders.orly/event"
|
||||
"lol.mleku.dev/errorf"
|
||||
)
|
||||
|
||||
type S []*F
|
||||
|
||||
// Match checks if a set of filters.T matches on an event.F.
|
||||
func (s *S) Match(event *event.E) bool {
|
||||
for _, f := range *s {
|
||||
if f.Matches(event) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Marshal encodes a slice of filters as a JSON array of objects.
|
||||
// It appends the result to dst and returns the resulting slice.
|
||||
func (s S) Marshal(dst []byte) (b []byte) {
|
||||
@@ -43,7 +54,7 @@ func (s *S) Unmarshal(b []byte) (r []byte, err error) {
|
||||
if len(r) == 0 {
|
||||
return
|
||||
}
|
||||
f := new(F)
|
||||
f := New()
|
||||
var rem []byte
|
||||
if rem, err = f.Unmarshal(r); err != nil {
|
||||
return
|
||||
|
||||
@@ -59,11 +59,11 @@ func (k *K) ToU64() uint64 {
|
||||
func (k *K) Name() string { return GetString(k.K) }
|
||||
|
||||
// Equal checks if
|
||||
func (k *K) Equal(k2 *K) bool {
|
||||
if k == nil || k2 == nil {
|
||||
func (k *K) Equal(k2 uint16) bool {
|
||||
if k == nil {
|
||||
return false
|
||||
}
|
||||
return k.K == k2.K
|
||||
return k.K == k2
|
||||
}
|
||||
|
||||
var Privileged = []*K{
|
||||
@@ -80,7 +80,7 @@ var Privileged = []*K{
|
||||
// the pubkeys in the event and p tags of the event are party to.
|
||||
func (k *K) IsPrivileged() (is bool) {
|
||||
for i := range Privileged {
|
||||
if k.Equal(Privileged[i]) {
|
||||
if k.Equal(Privileged[i].K) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func (k *S) Clone() (c *S) {
|
||||
// Even if a custom number is found, this codebase does not have the logic to
|
||||
// deal with the kind so such a search is pointless and for which reason static
|
||||
// typing always wins. No mistakes possible with known quantities.
|
||||
func (k *S) Contains(s *K) bool {
|
||||
func (k *S) Contains(s uint16) bool {
|
||||
for i := range k.K {
|
||||
if k.K[i].Equal(s) {
|
||||
return true
|
||||
|
||||
52
pkg/encoders/reason/reason.go
Normal file
52
pkg/encoders/reason/reason.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package reason
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"lol.mleku.dev/log"
|
||||
)
|
||||
|
||||
// R is the machine-readable prefix before the colon in an OK or CLOSED envelope message.
|
||||
// Below are the most common kinds that are mentioned in NIP-01.
|
||||
type R []byte
|
||||
|
||||
var (
|
||||
AuthRequired = R("auth-required")
|
||||
PoW = R("pow")
|
||||
Duplicate = R("duplicate")
|
||||
Blocked = R("blocked")
|
||||
RateLimited = R("rate-limited")
|
||||
Invalid = R("invalid")
|
||||
Error = R("error")
|
||||
Unsupported = R("unsupported")
|
||||
Restricted = R("restricted")
|
||||
)
|
||||
|
||||
// S returns the R as a string
|
||||
func (r R) S() string { return string(r) }
|
||||
|
||||
// B returns the R as a byte slice.
|
||||
func (r R) B() []byte { return r }
|
||||
|
||||
// IsPrefix returns whether a text contains the same R prefix.
|
||||
func (r R) IsPrefix(reason []byte) bool {
|
||||
return bytes.HasPrefix(
|
||||
reason, r.B(),
|
||||
)
|
||||
}
|
||||
|
||||
// F allows creation of a full R text with a printf style format.
|
||||
func (r R) F(format string, params ...any) (o []byte) {
|
||||
log.D.F(format, params...)
|
||||
return Msg(r, format, params...)
|
||||
}
|
||||
|
||||
// Msg constructs a properly formatted message with a machine-readable prefix
|
||||
// for OK and CLOSED envelopes.
|
||||
func Msg(prefix R, format string, params ...any) (o []byte) {
|
||||
if len(prefix) < 1 {
|
||||
prefix = Error
|
||||
}
|
||||
return []byte(fmt.Sprintf(prefix.S()+": "+format, params...))
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"encoders.orly/text"
|
||||
"lol.mleku.dev/errorf"
|
||||
utils "utils.orly"
|
||||
"utils.orly/bufpool"
|
||||
)
|
||||
|
||||
@@ -67,21 +68,28 @@ func (t *T) Less(i, j int) bool {
|
||||
|
||||
func (t *T) Swap(i, j int) { t.T[i], t.T[j] = t.T[j], t.T[i] }
|
||||
|
||||
func (t *T) ToSliceOfBytes() (b [][]byte) {
|
||||
return t.T
|
||||
// Contains returns true if the provided element is found in the tag slice.
|
||||
func (t *T) Contains(s []byte) (b bool) {
|
||||
for i := range t.T {
|
||||
if utils.FastEqual(t.T[i], s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Marshal encodes a tag.T as standard minified JSON array of strings.
|
||||
func (t *T) Marshal(dst []byte) (b []byte) {
|
||||
dst = append(dst, '[')
|
||||
b = dst
|
||||
b = append(b, '[')
|
||||
for i, s := range t.T {
|
||||
dst = text.AppendQuote(dst, s, text.NostrEscape)
|
||||
b = text.AppendQuote(b, s, text.NostrEscape)
|
||||
if i < len(t.T)-1 {
|
||||
dst = append(dst, ',')
|
||||
b = append(b, ',')
|
||||
}
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
return dst
|
||||
b = append(b, ']')
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON encodes a tag.T as standard minified JSON array of strings.
|
||||
|
||||
@@ -44,17 +44,26 @@ func (s *S) Append(t ...*T) {
|
||||
*s = append(*s, t...)
|
||||
}
|
||||
|
||||
func (s *S) ToSliceOfTags() (t []T) {
|
||||
if s == nil {
|
||||
return
|
||||
// ContainsAny returns true if any of the values given in `values` matches any
|
||||
// of the tag elements.
|
||||
func (s *S) ContainsAny(tagName []byte, values [][]byte) bool {
|
||||
if len(tagName) < 1 {
|
||||
return false
|
||||
}
|
||||
for _, tt := range *s {
|
||||
if tt == nil {
|
||||
for _, v := range *s {
|
||||
if v.Len() < 2 {
|
||||
continue
|
||||
}
|
||||
t = append(t, *tt)
|
||||
if !utils.FastEqual(v.Key(), tagName) {
|
||||
continue
|
||||
}
|
||||
for _, candidate := range values {
|
||||
if bytes.HasPrefix(v.Value(), candidate) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// MarshalJSON encodes a tags.T appended to a provided byte slice in JSON form.
|
||||
@@ -74,7 +83,8 @@ func (s *S) MarshalJSON() (b []byte, err error) {
|
||||
}
|
||||
|
||||
func (s *S) Marshal(dst []byte) (b []byte) {
|
||||
b = append(dst, '[')
|
||||
b = dst
|
||||
b = append(b, '[')
|
||||
for i, ss := range *s {
|
||||
b = ss.Marshal(b)
|
||||
if i < len(*s)-1 {
|
||||
|
||||
Reference in New Issue
Block a user