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:
102
pkg/encoders/event/binary.go
Normal file
102
pkg/encoders/event/binary.go
Normal 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
|
||||
}
|
||||
68
pkg/encoders/event/binary_test.go
Normal file
68
pkg/encoders/event/binary_test.go
Normal 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),
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user