-
Notifications
You must be signed in to change notification settings - Fork 714
NIP-93: NSON #515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
NIP-93: NSON #515
Conversation
|
I like this alot, it feels like Flatbuffers, but backward compatible, I wonder what @hoytech thinks. Maybe to make it even more Flatbuffers like,
|
|
|
||
| ### Open questions to be edited out of the NIP | ||
|
|
||
| - How to signal NSON support? I thought it would work to have an initial field `"n":1` (before `"id"`) on the JSON, which could be read very fast, but I don't know. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it simply be signaled in the relay info document once its listed as supporting this nip?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's too restrictive, introduces the need for clients to query these relays and keep track of these settings in the code. Also I was thinking relays should be able to mix both nson and non-nson events (for whatever reason).
Another possibility is signaling on the websocket handshake, but that is also kinda annoying for everybody.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually we don't need an initial field at all, the readers can just check if the static position x...y has the word "nson" in it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would have false positives in the case of a non-nson event where the content field or a tag spans x...y and happens to have nson in those positions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I think you should start with the signalling field of a fixed length to not screw the offsets. Then the variable length nson field after all the fixed length fields somewhere. I can't imagine how it could work otherwise.
|
This introduces quite a lot of attack vectors. I think using binary encoding is still simpler solution: by trying to avoid introduction of "complexity" of binary encoding (which is actually simpler than JSON) you actually add complexity and increase the attack surface. Just switch to CBOR or anything else out there with TLV support to remain flexible in message structure and plan depreciation of JSON encoding. I do not see how keeping JSON makes protocol simpler to use from a developer perspective. JSON is actually harder to use from my perspective. |
|
I did not ever claim binary encoding was more complex, in fact this own NIP mentions a binary encoding that is referred to as "simpler", but it turns out that most people are not comfortable with binary stuff and having to learn these things and write a decoder before starting to develop a Nostr app is a barrier probably too high. Also backwards-compatibility is very important, even though you don't care about it at all. I'm interested in learning what attacks this introduces. |
|
I do not understand why people would need to write binary encoders and decoders if there is a plenty of libraries and implementations for standards like CBOR which will do the job for them
Any increase of program complexity introduces attack vectors, especially the one which creates a lot of new conflicting conditions (what if different form of data will be specified in a wrong way - how the software will behave? most of implementation may do a random memory access, overflows etc). I am not doing any formal analysis of the attacks possible with the proposal, but taking into an account how many attack vectors exists for JSON (and how it is hard to do a JSON parser in a secure way) I think NSON approach doubles on that. If interested, you may see https://developer.apple.com/videos/play/wwdc2018/222/ which provides good introduction into the question |
|
ACK. I think @dr-orlovsky has some very real concerns/points. JSON is easier to wrap the head around, but obviously makes great sacrifices on scalability I think NSON is a nice marginal move in the right direction vs JSON, and can keep the binary encoding convo ongoing |
|
Concept ACK. This looks like a good approach in the right direction. Did you just come up with this on your own?
I never heard of CBOR and I think many other people also didn't. JSON has become the de-facto standard for data exchange between languages, for configs, for APIs (
|
Maybe we just need ASICs to decode/encode JSON, lol (not sure if serious) |
|
In order to keep back-compat, the nson field is not signed, right? This means that relays and other middle-men can alter it. Now we need to worry about how clients handle nson that is inconsistent with the normal event structure. Just off the top of my head: what happens if a decoder trusts the nson's number of tags, and the middle-man reduces the number of tags in the nson by 1: Then the attacker would've successfully "deleted" the final tag from the message. What are we even trying to solve here though? Yes, JSON parsing is slow, but compared to verifying the signature it's insignificant. Since we're talking about the external format of nostr events, everyone is verifying signatures upon receipt, right? After you've validated it, you're free to store it in whatever efficient format you want for later access. strfry uses flatbuffers, and yes @Nuhvi is right -- take me out for a beer and I'll be happy to evangelise that format to you at length. :) If we were going to create a new incompatible external format (which, to be clear, I don't think we should do, it would be a disaster for the ecosystem), I would focus on reducing the payload size. I believe the absolute minimal valid nostr event is 342 bytes (pre-compression). Especially since a lot of events are literally like 1-4 bytes of payload (an emoji reaction or whatever), this is a ton of overhead. Most of the suggested binary formats don't even improve this very much, especially when factoring in compression. Things like msgpack and CBOR (which is basically just a rip-off of msgpack that a guy named C. Bormann named after himself) are especially pointless because they are schema-less, which means just pure overhead for a static format like this. Here's what I would do in a brand-new format:
These 2 changes would cut message overhead by 40% with no additional processing overhead (again, you are validating sigs on message receipt right?) Note: I think schnorr signatures in theory do allow some cool batch verification stuff, which might actually be a big win when importing many events in a batch. |
This will be spotted once the receiver checks the id. The attacker can already modify the JSON directly, no need to modify the NSON field.
But the purpose of NSON is to be compatible!
Me neither!
I was think shrinking the event size is useless as the
Although I think validating everything is the default behavior I think it is not a totally bad approach for clients to assume relays are verifying, then sample event id and signature validation in the background in order to detect if there is some relay relaying fake messages eventually. If that happens the bad relay will be caught and expunged from society. |
These are like 50% of all events now! OK now I understand: You are optimising for the case where the client trusts its relays and isn't validating signatures, and the JSON decoding could plausibly be a bottleneck. Re-reading my message, I kind of came off a condescending jerk about that use-case, sorry about that hehe.
If JSON decode is a bottleneck, TBH I would push the libraries harder. Can you add rapidjson to your benchmarks too? I see there are some go bindings, but they will have some overhead too of course. There is a lot of room for optimising this. Previously I have worked on a code-base that parsed JSON (mostly flat with static fields, similar to nostr) using a custom ragel-based parser that was faster than rapidjson still. |
|
ACK in principle. IMHO JSON is actually harder than binary in non-javascript languages, but we could squabble about that all day so I'm not here to debate it. But getting people and lots and lots of installed code base to all switch to binary is fucking impossible. So no switch to binary. But if we were going to do that, please involve me I have written a lot about what nostr should change if it could start over (in cold storage). And of course include @hoytech. The real world is not where you get to create your idealistic perfect notions of how things should be, it is a messy place where you have to navigate the best imperfect path. |
|
Concept ACK. Can we agree to merge this only after two relay implementations picked up on it? What does @jb55 say? |
|
@fiatjaf - I decided to verify your assumption that web clients check the
So at least snort is not bothering to validate the However, consider the following case:
A malicious user could take a legit event from a popular user, add or modify the There are several possible mitigations:
Any of the above mitigations could work, but in general I think using unauthenticated data at all is a bad idea. It's a variation of the cryptographic doom principle. |
if anything it should just be a different wire format that is just TLVs with null-terminated strings (like flatbuffers but we don't need that level of complexity). I'm not sure I understand the point of putting nson in the json. The biggest slowness in json is dealing with escaping and copying. If it was binary zero-copy over the wire then it would be insanely trivial to decode, and this would be backwards compatible and optional since you could signal it in the websocket request. |
It makes decoding the JSON insanely faster than using normal JSON libraries. Check the benchmarks. That includes copying, unescaping strings and parsing hex. |
I agree with this goal but it's still using JSON. why not just make this an optional negotiated wire format that is pure binary? |
|
this was discussed 3 weeks ago on nostr as well: note1rtra39wn6ke985uzsxj5y0xpyfgjtvc2e4g6jg4ghqgctmnj8m3sp3753q |
|
I put my vote behind this:
That is, in the NSON NIP, it should specify that clients MUST validate the id. This isn't a must if they don't use nson. |
|
@jb55 I proposed a binary encoding some time ago with #512, but we came to conclusion that it is better to experiment with it in a dedicated protocol. That's why we have started http://github.com/renostr initiative. It will provide:
All of these will be backward-compatible, i.e. it is an extension protocol over nostr. Main goal - help mobiles reduce power consumption, increase speed, better & standardized protection from DoS attacks. Announcement: https://damus.io/note1d2y8jyrkzu46neuk8806nghx8vdutqznxkmglvqytzh0s7xtrjyq5rlnxa |
Because the speed gains of binary over NSON do not justify the added complexity of keeping two formats around forever and the (very high) risk that some people decide to implement just the binary encoding, thus splitting the network. |
| NIP-93 | ||
| ====== | ||
|
|
||
| NSON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indexed JSON?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but it's specific to Nostr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOSN?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but that sounds bad
|
|
||
| ### Anatomy of the `"nson"` field | ||
|
|
||
| It is hex-encoded. Some fields are a single byte, others are two bytes (4 characters), big-endian. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why base16 (hex)? base64 would be more compact
VLQ would be more flexible, but slower
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base64 is also slower than hex
I think writing a custom JSON encoder is most likely a more complex task than concatenating bytes together. One of the problems with Secure Scuttlebutt that I perceived was serious was for example the need to reimplement the complex JSON-based event validation algorithm in every single implementation. This lead to people burning out before doing anything meaningful with the protocol and I know that what we are proposing is nowhere near that complex but concatenating bytes together still seems like an easier alternative. |
|
@boreq I know, the only point here is that this is backwards-compatible so you can keep calling |
|
|
||
| ### Open questions to be edited out of the NIP | ||
|
|
||
| - How to signal NSON support? I thought it would work to have an initial field `"n":1` (before `"id"`) on the JSON, which could be read very fast, but I don't know. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just check the presence of `"nson" field?
|
@fiatjaf this nip example is removed from go-nostr and i think it won't be logical to use this. i think we can close this pr. |

I am unsure if this is a really dumb idea and I'm missing some obvious issue with it, but anyway.
text: https://github.com/nostr-protocol/nips/blob/nip93-nson/93.md
update (Set 2024): I still think this is a valuable idea worth exploring, but I think specific encoding I used here could be improved.