mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-08-03 00:12:17 +02:00
nson: assign constants and fix bugs so tests pass.
This commit is contained in:
72
nson/nson.go
72
nson/nson.go
@@ -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":"`)
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user