binary event encoding
This commit is contained in:
106
event/binary.go
Normal file
106
event/binary.go
Normal file
@@ -0,0 +1,106 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// 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.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)))
|
||||
for _, y := range x {
|
||||
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
|
||||
}
|
||||
|
||||
func (ev *E) UnmarshalBinary(r io.Reader) (err error) {
|
||||
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
|
||||
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 = 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
|
||||
}
|
||||
43
event/binary_test.go
Normal file
43
event/binary_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/event/examples"
|
||||
)
|
||||
|
||||
func TestTMarshalBinary_UnmarshalBinary(t *testing.T) {
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
var rem, out []byte
|
||||
var err error
|
||||
buf := new(bytes.Buffer)
|
||||
ea, eb := New(), New()
|
||||
now := time.Now()
|
||||
var counter int
|
||||
for scanner.Scan() {
|
||||
b := scanner.Bytes()
|
||||
c := make([]byte, 0, len(b))
|
||||
c = append(c, b...)
|
||||
if 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)
|
||||
}
|
||||
ea.MarshalBinary(buf)
|
||||
buf2 := bytes.NewBuffer(buf.Bytes())
|
||||
if err = eb.UnmarshalBinary(buf2); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
counter++
|
||||
out = out[:0]
|
||||
}
|
||||
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))
|
||||
}
|
||||
18
main.go
18
main.go
@@ -3,8 +3,12 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
"x.realy.lol/bech32encoding"
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/config"
|
||||
"x.realy.lol/hex"
|
||||
"x.realy.lol/log"
|
||||
"x.realy.lol/p256k"
|
||||
"x.realy.lol/version"
|
||||
)
|
||||
|
||||
@@ -15,4 +19,18 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
log.I.F("starting %s version %s", version.Name, version.Version)
|
||||
a := cfg.Superuser
|
||||
var err error
|
||||
var dst []byte
|
||||
if dst, err = bech32encoding.NpubToBytes([]byte(a)); chk.E(err) {
|
||||
if _, err = hex.DecBytes(dst, []byte(a)); chk.E(err) {
|
||||
log.F.F("SUPERUSER is invalid: %s", a)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
super := &p256k.Signer{}
|
||||
if err = super.InitPub(dst); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
45
varint/varint.go
Normal file
45
varint/varint.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Package varint is a variable integer encoding that works in reverse compared to the stdlib
|
||||
// binary Varint. The terminal byte in the encoding is the one with the 8th bit set. This is
|
||||
// basically like a base 128 encoding. It reads forward using an io.Reader and writes forward
|
||||
// using an io.Writer.
|
||||
package varint
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
)
|
||||
|
||||
func Encode(w io.Writer, v uint64) {
|
||||
x := []byte{0}
|
||||
for {
|
||||
x[0] = byte(v) & 127
|
||||
v >>= 7
|
||||
if v == 0 {
|
||||
x[0] |= 128
|
||||
_, _ = w.Write(x)
|
||||
break
|
||||
} else {
|
||||
_, _ = w.Write(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Decode(r io.Reader) (v uint64, err error) {
|
||||
x := []byte{0}
|
||||
v += uint64(x[0])
|
||||
var i uint64
|
||||
for {
|
||||
if _, err = r.Read(x); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if x[0] >= 128 {
|
||||
v += uint64(x[0]&127) << (i * 7)
|
||||
return
|
||||
} else {
|
||||
v += uint64(x[0]) << (i * 7)
|
||||
i++
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
29
varint/varint_test.go
Normal file
29
varint/varint_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package varint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"lukechampine.com/frand"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
)
|
||||
|
||||
func TestEncode_Decode(t *testing.T) {
|
||||
var v uint64
|
||||
for range 10000000 {
|
||||
v = uint64(frand.Intn(math.MaxInt64))
|
||||
buf1 := new(bytes.Buffer)
|
||||
Encode(buf1, v)
|
||||
buf2 := bytes.NewBuffer(buf1.Bytes())
|
||||
u, err := Decode(buf2)
|
||||
if chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if u != v {
|
||||
t.Fatalf("expected %d got %d", v, u)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user