Repository navigation
feat: decode packed event records and verify them against strfry exports - #815
Anshumancanrock wants to merge 6 commits into
Conversation
🦋 Changeset detectedLatest commit: 09241b8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
| public get expiration(): number { | ||
| return readUInt64(this.buffer, 80) |
There was a problem hiding this comment.
Large expiration values change
PackedEventView.expiration loses precision for values the encoder explicitly supports. For example, 18446744073709551615 decodes as 18446744073709551616. The export check still passes because Number(expected.expiration) rounds the expected value too.
Read expiration with readBigUInt64LE, compare it directly with the expected bigint, and add a round-trip test above Number.MAX_SAFE_INTEGER.
chappie-daemon
left a comment
There was a problem hiding this comment.
Review: approve-level
Excellent work — the rare PR where the verification strategy is the feature. I independently checked the trickiest rules against strfry's actual events.cpp (nostrJsonToPackedEvent): the expiration < 100 rejection, first-expiration-tag-only, the 64-char check on e/p values before from_hex (including the "0x" + 62-hex-chars packs to 31 bytes quirk, which the code documents), the virtual empty d tag (first for replaceable, last for parameterized — including the duplicate when one already exists), and ephemeral forcing expiration = 1. All match. I also ran pnpm strfry:verify-export against the committed export myself: 25 events, 0 failed. Unit tests (50) pass, biome clean, tsc --noEmit clean.
One thing worth knowing, non-blocking:
The mirror is packing-faithful, not admission-faithful. A few of strfry's admission-time rejections inside nostrJsonToPackedEvent aren't replicated: the kind-5 'a'-tag ownership check (parseATag + pubkey match), events__maxNumTags, and events__maxTagValSize. toPackedEventFields will build a record for an event strfry would throw on. That's harmless for the verify script — a rejected event can never appear in an export, so byte-for-byte verification is unaffected — but the LMDB reader you're building on top of this should treat the decoder as a packing mirror, not a validity gate. (The kind-5 event in the test data has a self-consistent a tag, so it verifies either way.)
No other notes. The filled-in PR template is appreciated.
Description
A decoder for strfry's packed event records and a script that checks it against a real strfry export.
pnpm strfry:verify-export <file>reads the output ofstrfry export --friedand, for each event, checks that the packed record we build matches strfry's byte for byte, that we decode the same fields back, and that the id and signature are valid.--skip-signaturesskips the slow part, and-reads from stdin.Motivation and Context
Second step of the strfry storage work. The LMDB reader and query engine build on these records, and checking against strfry's own export makes strfry itself the reference for the format.
The payload and dictionary decoders will come with the LMDB reader, since nothing uses them yet.
How Has This Been Tested?
I ran 25 events covering the edge cases through strfry 1.1.2 (
ghcr.io/hoytech/strfry), exported them with--fried, and the script passes on all 25. That export is committed as test data, so CI checks against it too.Unit tests cover the decoder, strfry's packing rules and the script's failure cases.
pnpm test:unit, lint andcheck:depspass.Types of changes
Checklist: