types, pubkey and signature codec
This commit is contained in:
@@ -14,12 +14,12 @@ So, this is how realy events look:
|
||||
<unix second precision timestamp in decimal ascii>\n
|
||||
key:value;extra;...\n // zero or more line separated, fields cannot contain a semicolon, end with newline instead of semicolon, key lowercase alphanumeric, first alpha, only key is mandatory, only reserved is `content`
|
||||
content: // literally this word on one line
|
||||
<content>\n // any number of further line breaks, last line without break is signature
|
||||
<bip-340 schnorr signature encoded in URL-base64>
|
||||
<content>\n // any number of further line breaks, last line is signature
|
||||
<bip-340 schnorr signature encoded in URL-base64>\n
|
||||
```
|
||||
|
||||
The canonical form is exactly this, except the last linebreak and the base64 encoded signature, hashed using SHA256. There is no need for placing the event ID in the wire format either, so this is also the wire format.
|
||||
The canonical form is exactly this, except for the signature and following linebreak, hashed with Blake2b
|
||||
|
||||
The database stored form of this event should make use of an event ID hash to monotonic collision free serial table and an event table.
|
||||
|
||||
Event ID hashes will be encoded in URL-base64 where used in tags or mentioned in content with the prefix `event:`. Public keys must be prefixed with `pubkey:` Tag keys should be intelligible words and a specification for their structure should be befined by users of them and shared with other REALY devs.
|
||||
Event ID hashes will be encoded in URL-base64 where used in tags or mentioned in content with the prefix `event:`. Public keys must be prefixed with `pubkey:` Tag keys should be intelligible words and a specification for their structure should be defined by users of them and shared with other REALY devs.
|
||||
13
pkg/codec/codec.go
Normal file
13
pkg/codec/codec.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package codec
|
||||
|
||||
// C is an interface for encoding and decoding that allows embedding encoders
|
||||
// inside other encoders by the use of append for Marshal and slice for
|
||||
// Unmarshal.
|
||||
type C interface {
|
||||
// Marshal data by appending it to the provided destination, and return the
|
||||
// resultant slice.
|
||||
Marshal(dst []byte) (result []byte, err error)
|
||||
// Unmarshal the next expected data element from the provided slice and return
|
||||
// the remainder after the expected separator.
|
||||
Unmarshal(data []byte) (rem []byte, err error)
|
||||
}
|
||||
14
pkg/event/event.go
Normal file
14
pkg/event/event.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"protocol.realy.lol/pkg/event/types"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Type types.T
|
||||
Pubkey []byte
|
||||
Timestamp int64
|
||||
Tags [][]byte
|
||||
Content []byte
|
||||
Signature []byte
|
||||
}
|
||||
9
pkg/event/log.go
Normal file
9
pkg/event/log.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"protocol.realy.lol/pkg/lol"
|
||||
)
|
||||
|
||||
var (
|
||||
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
|
||||
)
|
||||
9
pkg/event/types/log.go
Normal file
9
pkg/event/types/log.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"protocol.realy.lol/pkg/lol"
|
||||
)
|
||||
|
||||
var (
|
||||
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
|
||||
)
|
||||
44
pkg/event/types/types.go
Normal file
44
pkg/event/types/types.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// A T is a type descriptor, that is terminated by a newline.
|
||||
type T []byte
|
||||
|
||||
// Marshal append the T to a slice and appends a terminal newline, and returns
|
||||
// the result.
|
||||
func (t *T) Marshal(dst []byte) (result []byte, err error) {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
result = append(append(dst, []byte(*t)...), '\n')
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal expects an identifier followed by a newline. If the buffer ends
|
||||
// without a newline an EOF is returned.
|
||||
func (t *T) Unmarshal(data []byte) (rem []byte, err error) {
|
||||
rem = data
|
||||
if t == nil {
|
||||
err = errorf.E("can't unmarshal into nil types.T")
|
||||
return
|
||||
}
|
||||
if len(rem) < 2 {
|
||||
err = errorf.E("can't unmarshal nothing")
|
||||
return
|
||||
}
|
||||
for i := range rem {
|
||||
if rem[i] == '\n' {
|
||||
// write read data up to the newline and return the remainder after
|
||||
// the newline.
|
||||
*t = rem[:i]
|
||||
rem = rem[i+1:]
|
||||
return
|
||||
}
|
||||
}
|
||||
// a T must end with a newline or an io.EOF is returned.
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
28
pkg/event/types/types_test.go
Normal file
28
pkg/event/types/types_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestT_Marshal_Unmarshal(t *testing.T) {
|
||||
typ := T("note")
|
||||
var err error
|
||||
var res []byte
|
||||
if res, err = typ.Marshal(nil); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.S(res)
|
||||
t2 := new(T)
|
||||
var rem []byte
|
||||
if rem, err = t2.Unmarshal(res); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rem) > 0 {
|
||||
log.I.S(rem)
|
||||
}
|
||||
log.I.S(t2)
|
||||
if !bytes.Equal(typ, *t2) {
|
||||
t.Fatal("types.T did not encode/decode faithfully")
|
||||
}
|
||||
}
|
||||
9
pkg/pubkey/log.go
Normal file
9
pkg/pubkey/log.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package pubkey
|
||||
|
||||
import (
|
||||
"protocol.realy.lol/pkg/lol"
|
||||
)
|
||||
|
||||
var (
|
||||
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
|
||||
)
|
||||
74
pkg/pubkey/pubkey.go
Normal file
74
pkg/pubkey/pubkey.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package pubkey
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
const Len = 44
|
||||
|
||||
type P struct{ ed25519.PublicKey }
|
||||
|
||||
func New(pk []byte) (p *P, err error) {
|
||||
if len(pk) != ed25519.PublicKeySize {
|
||||
err = errorf.E("invalid public key size: %d; require %d",
|
||||
len(pk), ed25519.PublicKeySize)
|
||||
return
|
||||
}
|
||||
p = &P{pk}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *P) Marshal(dst []byte) (result []byte, err error) {
|
||||
result = dst
|
||||
if p == nil || p.PublicKey == nil || len(p.PublicKey) == 0 {
|
||||
err = errorf.E("nil/zero length pubkey")
|
||||
return
|
||||
}
|
||||
if len(p.PublicKey) != ed25519.PublicKeySize {
|
||||
err = errorf.E("invalid public key length %d; require %d '%0x'",
|
||||
len(p.PublicKey), ed25519.PublicKeySize, p.PublicKey)
|
||||
return
|
||||
}
|
||||
buf := bytes.NewBuffer(result)
|
||||
w := base64.NewEncoder(base64.URLEncoding, buf)
|
||||
if _, err = w.Write(p.PublicKey); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if err = w.Close(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
result = append(buf.Bytes(), '\n')
|
||||
return
|
||||
}
|
||||
|
||||
func (p *P) Unmarshal(data []byte) (rem []byte, err error) {
|
||||
rem = data
|
||||
if p == nil {
|
||||
err = errorf.E("can't unmarshal into nil types.T")
|
||||
return
|
||||
}
|
||||
if len(rem) < 2 {
|
||||
err = errorf.E("can't unmarshal nothing")
|
||||
return
|
||||
}
|
||||
for i := range rem {
|
||||
if rem[i] == '\n' {
|
||||
if i != Len {
|
||||
err = errorf.E("invalid encoded pubkey length %d; require %d '%0x'",
|
||||
i, Len, rem[:i])
|
||||
return
|
||||
}
|
||||
p.PublicKey = make([]byte, ed25519.PublicKeySize)
|
||||
if _, err = base64.URLEncoding.Decode(p.PublicKey, rem[:i]); chk.E(err) {
|
||||
return
|
||||
}
|
||||
rem = rem[i+1:]
|
||||
return
|
||||
}
|
||||
}
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
38
pkg/pubkey/pubkey_test.go
Normal file
38
pkg/pubkey/pubkey_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package pubkey
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestP_Marshal_Unmarshal(t *testing.T) {
|
||||
pk := make([]byte, ed25519.PublicKeySize)
|
||||
var err error
|
||||
if _, err = rand.Read(pk); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.S(pk)
|
||||
var p *P
|
||||
if p, err = New(pk); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var o []byte
|
||||
if o, err = p.Marshal(nil); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%d %s", len(o), o)
|
||||
p2 := &P{}
|
||||
var rem []byte
|
||||
if rem, err = p2.Unmarshal(o); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rem) > 0 {
|
||||
log.I.F("%d %s", len(rem), rem)
|
||||
}
|
||||
log.I.S(p2.PublicKey)
|
||||
if !bytes.Equal(pk, p2.PublicKey) {
|
||||
t.Fatal("public key did not encode/decode faithfully")
|
||||
}
|
||||
}
|
||||
9
pkg/signature/log.go
Normal file
9
pkg/signature/log.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"protocol.realy.lol/pkg/lol"
|
||||
)
|
||||
|
||||
var (
|
||||
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
|
||||
)
|
||||
74
pkg/signature/signature.go
Normal file
74
pkg/signature/signature.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
const Len = 88
|
||||
|
||||
type S struct{ Signature []byte }
|
||||
|
||||
func New(sig []byte) (p *S, err error) {
|
||||
if len(sig) != ed25519.SignatureSize {
|
||||
err = errorf.E("invalid signature size: %d; require %d",
|
||||
len(sig), ed25519.SignatureSize)
|
||||
return
|
||||
}
|
||||
p = &S{sig}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *S) Marshal(dst []byte) (result []byte, err error) {
|
||||
result = dst
|
||||
if p == nil || p.Signature == nil || len(p.Signature) == 0 {
|
||||
err = errorf.E("nil/zero length signature")
|
||||
return
|
||||
}
|
||||
if len(p.Signature) != ed25519.SignatureSize {
|
||||
err = errorf.E("invalid signature length %d; require %d '%0x'",
|
||||
len(p.Signature), ed25519.SignatureSize, p.Signature)
|
||||
return
|
||||
}
|
||||
buf := bytes.NewBuffer(result)
|
||||
w := base64.NewEncoder(base64.URLEncoding, buf)
|
||||
if _, err = w.Write(p.Signature); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if err = w.Close(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
result = append(buf.Bytes(), '\n')
|
||||
return
|
||||
}
|
||||
|
||||
func (p *S) Unmarshal(data []byte) (rem []byte, err error) {
|
||||
rem = data
|
||||
if p == nil {
|
||||
err = errorf.E("can't unmarshal into nil types.T")
|
||||
return
|
||||
}
|
||||
if len(rem) < 2 {
|
||||
err = errorf.E("can't unmarshal nothing")
|
||||
return
|
||||
}
|
||||
for i := range rem {
|
||||
if rem[i] == '\n' {
|
||||
if i != Len {
|
||||
err = errorf.E("invalid encoded signature length %d; require %d '%0x'",
|
||||
i, Len, rem[:i])
|
||||
return
|
||||
}
|
||||
p.Signature = make([]byte, ed25519.SignatureSize)
|
||||
if _, err = base64.URLEncoding.Decode(p.Signature, rem[:i]); chk.E(err) {
|
||||
return
|
||||
}
|
||||
rem = rem[i+1:]
|
||||
return
|
||||
}
|
||||
}
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
38
pkg/signature/signature_test.go
Normal file
38
pkg/signature/signature_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestS_Marshal_Unmarshal(t *testing.T) {
|
||||
sig := make([]byte, ed25519.SignatureSize)
|
||||
var err error
|
||||
if _, err = rand.Read(sig); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.S(sig)
|
||||
var s *S
|
||||
if s, err = New(sig); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var o []byte
|
||||
if o, err = s.Marshal(nil); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%d %s", len(o), o)
|
||||
p2 := &S{}
|
||||
var rem []byte
|
||||
if rem, err = p2.Unmarshal(o); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rem) > 0 {
|
||||
log.I.F("%d %s", len(rem), rem)
|
||||
}
|
||||
log.I.S(p2.Signature)
|
||||
if !bytes.Equal(sig, p2.Signature) {
|
||||
t.Fatal("signature did not encode/decode faithfully")
|
||||
}
|
||||
}
|
||||
3
repos/readme.md
Normal file
3
repos/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# relays
|
||||
|
||||
relay implementations for various subprotocols
|
||||
Reference in New Issue
Block a user