Migrate package imports from next.orly.dev to new orly domain structure; add new varint and binary encoders with comprehensive tests; enhance existing tag and envelope implementations with additional methods, validations, and test coverage; introduce shared test.sh script for streamlined testing across modules.

This commit is contained in:
2025-08-31 16:52:24 +01:00
parent 94383f29e9
commit 91d95c6f1a
202 changed files with 12803 additions and 420 deletions

View File

@@ -0,0 +1,102 @@
package event
import (
"io"
"crypto.orly/ec/schnorr"
"encoders.orly/tag"
"encoders.orly/varint"
"lol.mleku.dev/chk"
)
// MarshalBinary writes a binary encoding of an event.
//
// [ 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) MarshalBinary(w io.Writer) {
_, _ = w.Write(ev.ID)
_, _ = w.Write(ev.Pubkey)
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() {
varint.Encode(w, uint64(x.Len()))
for _, y := range x.ToSliceOfBytes() {
varint.Encode(w, uint64(len(y)))
_, _ = w.Write(y)
}
}
varint.Encode(w, uint64(len(ev.Content)))
_, _ = w.Write(ev.Content)
_, _ = w.Write(ev.Sig)
return
}
func (ev *E) UnmarshalBinary(r io.Reader) (err error) {
ev.ID = make([]byte, 32)
if _, err = r.Read(ev.ID); chk.E(err) {
return
}
ev.Pubkey = make([]byte, 32)
if _, err = r.Read(ev.Pubkey); chk.E(err) {
return
}
var ca uint64
if ca, err = varint.Decode(r); chk.E(err) {
return
}
ev.CreatedAt = int64(ca)
var k uint64
if k, err = varint.Decode(r); chk.E(err) {
return
}
ev.Kind = uint16(k)
var nTags uint64
if nTags, err = varint.Decode(r); chk.E(err) {
return
}
ev.Tags = tag.NewSWithCap(int(nTags))
for range nTags {
var nField uint64
if nField, err = varint.Decode(r); chk.E(err) {
return
}
t := tag.NewWithCap(int(nField))
for range nField {
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
}
t.T = append(t.T, field)
}
*ev.Tags = append(*ev.Tags, t)
}
var cLen uint64
if cLen, err = varint.Decode(r); chk.E(err) {
return
}
ev.Content = make([]byte, cLen)
if _, err = r.Read(ev.Content); chk.E(err) {
return
}
ev.Sig = make([]byte, schnorr.SignatureSize)
if _, err = r.Read(ev.Sig); chk.E(err) {
return
}
return
}

View File

@@ -0,0 +1,68 @@
package event
import (
"bufio"
"bytes"
"testing"
"time"
"encoders.orly/event/examples"
"lol.mleku.dev/chk"
"utils.orly"
)
func TestTMarshalBinary_UnmarshalBinary(t *testing.T) {
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
var rem, out []byte
var err error
now := time.Now()
var counter int
for scanner.Scan() {
// Create new event objects and buffer for each iteration
buf := new(bytes.Buffer)
ea, eb := New(), New()
chk.E(scanner.Err())
b := scanner.Bytes()
c := make([]byte, 0, len(b))
c = append(c, b...)
if rem, err = ea.Unmarshal(c); chk.E(err) {
t.Fatal(err)
}
if len(rem) != 0 {
t.Fatalf(
"some of input remaining after marshal/unmarshal: '%s'",
rem,
)
}
// Reset buffer before marshaling
buf.Reset()
ea.MarshalBinary(buf)
// Create a new buffer for unmarshaling
buf2 := bytes.NewBuffer(buf.Bytes())
if err = eb.UnmarshalBinary(buf2); chk.E(err) {
t.Fatal(err)
}
// Marshal unmarshaled binary event back to JSON
unmarshaledJSON := eb.Serialize()
// Compare the two JSON representations
if !utils.FastEqual(b, unmarshaledJSON) {
t.Fatalf(
"JSON representations don't match after binary marshaling/unmarshaling:\nOriginal: %s\nUnmarshaled: %s",
b, unmarshaledJSON,
)
}
counter++
out = out[:0]
}
chk.E(scanner.Err())
t.Logf(
"unmarshaled json, marshaled binary, unmarshaled binary, %d events in %v av %v per event",
counter, time.Since(now), time.Since(now)/time.Duration(counter),
)
}

View File

@@ -1,10 +1,10 @@
package event
import (
"next.orly.dev/pkg/crypto/sha256"
"next.orly.dev/pkg/encoders/hex"
"next.orly.dev/pkg/encoders/ints"
"next.orly.dev/pkg/encoders/text"
"crypto.orly/sha256"
"encoders.orly/hex"
"encoders.orly/ints"
"encoders.orly/text"
)
// ToCanonical converts the event to the canonical encoding used to derive the

View File

@@ -4,18 +4,18 @@ import (
"fmt"
"io"
"crypto.orly/ec/schnorr"
"crypto.orly/sha256"
"encoders.orly/ints"
"encoders.orly/kind"
"encoders.orly/tag"
"encoders.orly/text"
"github.com/templexxx/xhex"
"lol.mleku.dev/chk"
"lol.mleku.dev/errorf"
"lol.mleku.dev/log"
"next.orly.dev/pkg/crypto/ec/schnorr"
"next.orly.dev/pkg/crypto/sha256"
"next.orly.dev/pkg/encoders/ints"
"next.orly.dev/pkg/encoders/kind"
"next.orly.dev/pkg/encoders/tag"
"next.orly.dev/pkg/encoders/text"
"next.orly.dev/pkg/utils"
"next.orly.dev/pkg/utils/bufpool"
"utils.orly"
"utils.orly/bufpool"
)
// E is the primary datatype of nostr. This is the form of the structure that
@@ -28,7 +28,7 @@ import (
// library. Either call MarshalJSON directly or use a json.Encoder with html
// escaping disabled.
//
// Or import "next.orly.dev/pkg/encoders/json" and use json.Marshal which is the
// Or import "encoders.orly/json" and use json.Marshal which is the
// same as go 1.25 json v1 except with this one stupidity removed.
type E struct {
@@ -173,58 +173,12 @@ func (ev *E) Marshal(dst []byte) (b []byte) {
func (ev *E) MarshalJSON() (b []byte, err error) {
b = bufpool.Get()
b = ev.Marshal(b[:0])
// b = b[:0]
// b = append(b, '{')
// b = append(b, '"')
// b = append(b, jId...)
// b = append(b, `":"`...)
// b = b[:len(b)+2*sha256.Size]
// xhex.Encode(b[len(b)-2*sha256.Size:], ev.ID)
// b = append(b, `","`...)
// b = append(b, jPubkey...)
// b = append(b, `":"`...)
// b = b[:len(b)+2*schnorr.PubKeyBytesLen]
// xhex.Encode(b[len(b)-2*schnorr.PubKeyBytesLen:], ev.Pubkey)
// b = append(b, `","`...)
// b = append(b, jCreatedAt...)
// b = append(b, `":`...)
// b = ints.New(ev.CreatedAt).Marshal(b)
// b = append(b, `,"`...)
// b = append(b, jKind...)
// b = append(b, `":`...)
// b = ints.New(ev.Kind).Marshal(b)
// b = append(b, `,"`...)
// b = append(b, jTags...)
// b = append(b, `":`...)
// if ev.Tags != nil {
// b = ev.Tags.Marshal(b)
// }
// b = append(b, `,"`...)
// b = append(b, jContent...)
// b = append(b, `":"`...)
// // it can happen the slice has insufficient capacity to hold the content AND
// // the signature at this point, because the signature encoder must have
// // sufficient capacity pre-allocated as it does not append to the buffer.
// // unlike every other encoding function up to this point. This also ensures
// // that since the bufpool defaults to 1kb, most events won't have a
// // re-allocation required, but if they do, it will be this next one, and it
// // integrates properly with the buffer pool, reducing GC pressure and
// // avoiding new heap allocations.
// if cap(b) < len(b)+len(ev.Content)+7+256+2 {
// b2 := make([]byte, len(b)+len(ev.Content)*2+7+256+2)
// copy(b2, b)
// b2 = b2[:len(b)]
// // return the old buffer to the pool for reuse.
// bufpool.PutBytes(b)
// b = b2
// }
// b = text.NostrEscape(b, ev.Content)
// b = append(b, `","`...)
// b = append(b, jSig...)
// b = append(b, `":"`...)
// b = b[:len(b)+2*schnorr.SignatureSize]
// xhex.Encode(b[len(b)-2*schnorr.SignatureSize:], ev.Sig)
// b = append(b, `"}`...)
return
}
func (ev *E) Serialize() (b []byte) {
b = bufpool.Get()
b = ev.Marshal(b[:0])
return
}

View File

@@ -6,16 +6,16 @@ import (
"testing"
"time"
"encoders.orly/event/examples"
"encoders.orly/hex"
"encoders.orly/json"
"encoders.orly/tag"
"lol.mleku.dev/chk"
"lol.mleku.dev/log"
"lukechampine.com/frand"
"next.orly.dev/pkg/encoders/event/examples"
"next.orly.dev/pkg/encoders/hex"
"next.orly.dev/pkg/encoders/json"
"next.orly.dev/pkg/encoders/tag"
"next.orly.dev/pkg/utils"
"next.orly.dev/pkg/utils/bufpool"
"next.orly.dev/pkg/utils/units"
"utils.orly"
"utils.orly/bufpool"
"utils.orly/units"
)
func TestMarshalJSONUnmarshalJSON(t *testing.T) {

View File

@@ -1,12 +1,12 @@
package event
import (
"crypto.orly/p256k"
"interfaces.orly/signer"
"lol.mleku.dev/chk"
"lol.mleku.dev/errorf"
"lol.mleku.dev/log"
"next.orly.dev/pkg/crypto/p256k"
"next.orly.dev/pkg/interfaces/signer"
"next.orly.dev/pkg/utils"
"utils.orly"
)
// Sign the event using the signer.I. Uses github.com/bitcoin-core/secp256k1 if