implemented event and req

This commit is contained in:
2025-09-02 20:32:53 +01:00
parent 76b251dea9
commit 51f04f5f60
104 changed files with 6368 additions and 125 deletions

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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

View 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...))
}

View File

@@ -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.

View File

@@ -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 {