nson: assign constants and fix bugs so tests pass.

This commit is contained in:
fiatjaf
2023-07-04 20:32:06 -03:00
parent f55bb2f07b
commit db22f132f4
2 changed files with 50 additions and 69 deletions

View File

@@ -25,6 +25,25 @@ import (
"nson":"xxkkccccttnn111122223333nn11112222"
*/
const (
ID_START = 7
ID_END = 7 + 64
PUBKEY_START = 83
PUBKEY_END = 83 + 64
SIG_START = 156
SIG_END = 156 + 128
CREATED_AT_START = 299
CREATED_AT_END = 299 + 10
NSON_STRING_START = 318 // the actual json string for the "nson" field
NSON_VALUES_START = 318 + 2 // skipping the first byte which delimits the nson size
NSON_MARKER_START = 309 // this is used just to determine if an event is nson or not
NSON_MARKER_END = 317 // it's just the `,"nson":` (including ,": garbage to reduce false positives) part
)
var NotNSON = fmt.Errorf("not nson")
// Unmarshal turns a NSON string into a nostr.Event struct
func Unmarshal(data string) (evt *nostr.Event, err error) {
defer func() {
@@ -34,28 +53,26 @@ func Unmarshal(data string) (evt *nostr.Event, err error) {
}()
// check if it's nson
if data[311:315] != "nson" {
return nil, fmt.Errorf("not nson")
if data[NSON_MARKER_START:NSON_MARKER_END] != ",\"nson\":" {
return nil, NotNSON
}
// nson values
nsonSizeBytes, _ := hex.DecodeString(data[318 : 318+2])
nsonSize := int(nsonSizeBytes[0]) * 2 // number of bytes is given, we x2 because the string is in hex
nsonDescriptors, _ := hex.DecodeString(data[320 : 320+nsonSize])
nsonSize, nsonDescriptors := parseDescriptors(data)
evt = &nostr.Event{}
// static fields
evt.ID = data[7 : 7+64]
evt.PubKey = data[83 : 83+64]
evt.Sig = data[156 : 156+128]
ts, _ := strconv.ParseInt(data[299:299+10], 10, 64)
evt.ID = data[ID_START:ID_END]
evt.PubKey = data[PUBKEY_START:PUBKEY_END]
evt.Sig = data[SIG_START:SIG_END]
ts, _ := strconv.ParseUint(data[CREATED_AT_START:CREATED_AT_END], 10, 64)
evt.CreatedAt = nostr.Timestamp(ts)
// dynamic fields
// kind
kindChars := int(nsonDescriptors[0])
kindStart := 320 + nsonSize + 9 // len(`","kind":`)
kindStart := NSON_VALUES_START + nsonSize + 9 // len(`","kind":`)
evt.Kind, _ = strconv.Atoi(data[kindStart : kindStart+kindChars])
// content
@@ -90,7 +107,7 @@ func Unmarshal(data string) (evt *nostr.Event, err error) {
return evt, err
}
func Marshal(evt *nostr.Event) (string, error) {
func Marshal(evt nostr.Event) (string, error) {
// start building the nson descriptors (without the first byte that represents the nson size)
nsonBuf := make([]byte, 256)
@@ -136,7 +153,7 @@ func Marshal(evt *nostr.Event) (string, error) {
// actually build the json
base := strings.Builder{}
base.Grow(320 + // everything up to "nson":
base.Grow(NSON_VALUES_START + // everything up to "nson":
2 + len(nsonBuf)*2 + // nson
9 + kindChars + // kind and its label
12 + contentChars + // content and its label
@@ -158,6 +175,13 @@ func Marshal(evt *nostr.Event) (string, error) {
return base.String(), nil
}
func parseDescriptors(data string) (int, []byte) {
nsonSizeBytes, _ := hex.DecodeString(data[NSON_STRING_START:NSON_VALUES_START])
size := int(nsonSizeBytes[0]) * 2 // number of bytes is given, we x2 because the string is in hex
values, _ := hex.DecodeString(data[NSON_VALUES_START : NSON_VALUES_START+size])
return size, values
}
// A nson.Event is basically a wrapper over the string that makes it easy to get each event property (except tags).
type Event struct {
data string
@@ -172,25 +196,15 @@ func New(nsonText string) Event {
func (ne *Event) parseDescriptors() {
if ne.descriptors == nil {
nsonSizeBytes, _ := hex.DecodeString(ne.data[318 : 318+2])
ne.descriptorsSize = int(nsonSizeBytes[0])
ne.descriptors, _ = hex.DecodeString(ne.data[320 : 320+ne.descriptorsSize])
ne.descriptorsSize, ne.descriptors = parseDescriptors(ne.data)
}
}
func (ne *Event) parseKind() {
if ne.descriptors == nil {
nsonSizeBytes, _ := hex.DecodeString(ne.data[318 : 318+2])
ne.descriptorsSize = int(nsonSizeBytes[0])
ne.descriptors, _ = hex.DecodeString(ne.data[320 : 320+ne.descriptorsSize])
}
}
func (ne Event) GetID() string { return ne.data[7 : 7+64] }
func (ne Event) GetPubkey() string { return ne.data[83 : 83+64] }
func (ne Event) GetSig() string { return ne.data[156 : 156+128] }
func (ne Event) GetID() string { return ne.data[ID_START:ID_END] }
func (ne Event) GetPubkey() string { return ne.data[PUBKEY_START:PUBKEY_END] }
func (ne Event) GetSig() string { return ne.data[SIG_START:SIG_END] }
func (ne Event) GetCreatedAt() nostr.Timestamp {
ts, _ := strconv.ParseInt(ne.data[299:299+10], 10, 64)
ts, _ := strconv.ParseUint(ne.data[CREATED_AT_START:CREATED_AT_END], 10, 64)
return nostr.Timestamp(ts)
}
@@ -198,7 +212,7 @@ func (ne *Event) GetKind() int {
ne.parseDescriptors()
kindChars := int(ne.descriptors[0])
kindStart := 320 + ne.descriptorsSize + 9 // len(`","kind":`)
kindStart := NSON_VALUES_START + ne.descriptorsSize + 9 // len(`","kind":`)
kind, _ := strconv.Atoi(ne.data[kindStart : kindStart+kindChars])
return kind
@@ -208,7 +222,7 @@ func (ne *Event) GetContent() string {
ne.parseDescriptors()
kindChars := int(ne.descriptors[0])
kindStart := 320 + ne.descriptorsSize + 9 // len(`","kind":`)
kindStart := NSON_VALUES_START + ne.descriptorsSize + 9 // len(`","kind":`)
contentChars := int(binary.BigEndian.Uint16(ne.descriptors[1:3]))
contentStart := kindStart + kindChars + 12 // len(`,"content":"`)

View File

@@ -9,9 +9,7 @@ import (
)
var nsonTestEvents = []string{
`{"id":"ae1fc7154296569d87ca4663f6bdf448c217d1590d28c85d158557b8b43b4d69","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","sig":"94e10947814b1ebe38af42300ecd90c7642763896c4f69506ae97bfdf54eec3c0c21df96b7d95daa74ff3d414b1d758ee95fc258125deebc31df0c6ba9396a51","created_at":1683660344,"nson":"1405000b0203000100400005040001004000000014","kind":30023,"content":"hello hello","tags":[["e","b6de44a9dd47d1c000f795ea0453046914f44ba7d5e369608b04867a575ea83e","reply"],["p","c26f7b252cea77a5b94f42b1a4771021be07d4df766407e47738605f7e3ab774","","wss://relay.damus.io"]]}`,
`{"id":"ae1fc7154296569d87ca4663f6bdf448c217d1590d28c85d158557b8b43b4d69","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","sig":"94e10947814b1ebe38af42300ecd90c7642763896c4f69506ae97bfdf54eec3c0c21df96b7d95daa74ff3d414b1d758ee95fc258125deebc31df0c6ba9396a51","created_at":1683660344,"nson":"140500100203000100400005040001004000000014","kind":30023,"content":"hello\n\"hello\"","tags":[["e","b6de44a9dd47d1c000f795ea0453046914f44ba7d5e369608b04867a575ea83e","reply"],["p","c26f7b252cea77a5b94f42b1a4771021be07d4df766407e47738605f7e3ab774","","wss://relay.damus.io"]]}`,
`{"id":"a235323ad6ae7032667330c4d52def2d6be67a973d71f2f1784b2b5b01d57026","pubkey":"69aeace80672c08ef7729a03e597ed4e9dd5ddaa7c457349d55d12c043e8a7ab","sig":"7e5fedc3c1c16abb95d207b73a689b5f17ab039cffd0b6bea62dcbfb607a27d38c830542df5ce762685a0da4e8edd28beab0c9a8f47e3037ff6e676ea6297bfa","created_at":1680277541,"nson":"0401049600","kind":1,"content":"Hello #Plebstrs 🤙\n\nExcited to share with you the latest improvements we've designed to enhance your user experience when creating new posts on #Plebstr.\n\nMain UX improvements include:\n— The ability to mention anyone directly in your new post 🔍\n— Real-time previews of all attachments while creating your post \U0001fa7b\n— Minimizing the new post window to enable #multitasking and easy access to your draft 📝\n\nThis is the first design mockup and we can't wait to bring it to you in an upcoming updates. Our amazing developers are currently working on it, together with other small improvements. Stay tuned! 🚀👨\u200d💻\n\n*Some details may change so keep your fingers crossed 😄🤞\n\n#comingsoon #maybe #insomeshapeorform\n\nhttps://nostr.build/i/nostr.build_2b337d24f0cd19eff0678893ac93d58ee374ca8c3b9215516aa76a05856ec9c0.png\nhttps://nostr.build/i/nostr.build_f79b14687a1f6e7275192554722a85be815219573d824e381c4913715644e10d.png\nhttps://nostr.build/i/nostr.build_009fa068a32383f88e19763fa22e16142cf44166cb097293f7082e7bf4a38eed.png\nhttps://nostr.build/i/nostr.build_47ecdd4867808b3f3af2620ac4bf40aefcc9022c4ba26762567012c22d636487.png","tags":[]}}`,
`{"id":"192eaf31bd20476bbe9265a3667cfef6410dfd563c02a64cb15d6fa8efec0ed6","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","sig":"5b9051596a5ba0619fd5fd7d2766b8aeb0cc398f1d1a0804f4b4ed884482025b3d4888e4c892f2fc437415bfc121482a990fad30f5cd9e333e55364052f99bbc","created_at":1688505641,"nson":"0401000500","kind":1,"content":"hello","tags":[]}`,
}
func TestBasicNsonParse(t *testing.T) {
@@ -24,60 +22,29 @@ func TestBasicNsonParse(t *testing.T) {
func TestNsonPartialGet(t *testing.T) {
for _, jevt := range nsonTestEvents {
evt, _ := Unmarshal(jevt)
wrapper := New(jevt)
if id := wrapper.GetID(); id != evt.ID {
t.Error("partial id wrong")
t.Errorf("partial id wrong. got %v, expected %v", id, evt.ID)
}
if pubkey := wrapper.GetPubkey(); pubkey != evt.PubKey {
t.Error("partial pubkey wrong")
t.Errorf("partial pubkey wrong. got %v, expected %v", pubkey, evt.PubKey)
}
if sig := wrapper.GetSig(); sig != evt.Sig {
t.Error("partial sig wrong")
t.Errorf("partial sig wrong. got %v, expected %v", sig, evt.Sig)
}
if createdAt := wrapper.GetCreatedAt(); createdAt != evt.CreatedAt {
t.Error("partial created_at wrong")
t.Errorf("partial created_at wrong. got %v, expected %v", createdAt, evt.CreatedAt)
}
if kind := wrapper.GetKind(); kind != evt.Kind {
t.Error("partial kind wrong")
t.Errorf("partial kind wrong. got %v, expected %v", kind, evt.Kind)
}
if content := wrapper.GetContent(); content != evt.Content {
t.Error("partial content wrong")
t.Errorf("partial content wrong. got %v, expected %v", content, evt.Content)
}
}
}
func TestEncodeNson(t *testing.T) {
jevt := `{
"content": "hello world",
"created_at": 1683762317,
"id": "57ff66490a6a2af3992accc26ae95f3f60c6e5f84ed0ddf6f59c534d3920d3d2",
"kind": 1,
"pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"sig": "504d142aed7fa7e0f6dab5bcd7eed63963b0277a8e11bbcb03b94531beb4b95a12f1438668b02746bd5362161bc782068e6b71494060975414e793f9e19f57ea",
"tags": [
[
"e",
"b6de44a9dd47d1c000f795ea0453046914f44ba7d5e369608b04867a575ea83e",
"reply"
],
[
"p",
"c26f7b252cea77a5b94f42b1a4771021be07d4df766407e47738605f7e3ab774",
"",
"wss://relay.damus.io"
]
]
}`
evt := &nostr.Event{}
json.Unmarshal([]byte(jevt), evt)
nevt, _ := Marshal(evt)
fmt.Println(nevt)
}
func checkParsedCorrectly(t *testing.T, evt *nostr.Event, jevt string) (isBad bool) {
var canonical nostr.Event
err := json.Unmarshal([]byte(jevt), &canonical)