Conversation
Defining the algorithms in the spec rather than deferring to whatever the implementation's platform exposes means a protocol using hash means the same thing everywhere, and it fixes the digest's width: crc32c is 4 bytes, so the size of a hash field is known without hashing anything. type is required to be of constant size and at least as wide as the digest, which a variable-length type such as varint is not.
The section explained its own design where the neighbouring types state a
rule and stop: why alg is an explicit list, why a fixed width means a hash
can be sized without hashing, why two's complement reads back as it does.
The rules those sentences surround stay -- the constraint on type moves
into its argument bullet, and the CRC-32C parameters into alg's, since no
other section in the file uses a table for one value.
The example's value was unverifiable: ItemComponent is not defined here,
and -486237565 is not the CRC32C of any reading of { id: 5, level: 3 }.
The line now gives the bytes the body serializes to, so the digest can be
checked against the section above it.
A compiled implementation sizes the body by calling the sizer generated for its type, and only a named type has one. An inline body would also be free to reference a field of whatever contains the hash, which nothing it could be called from is able to resolve. The type this exists for, an item component hashed into a HashedSlot, is a named type, so the restriction costs it nothing.
rom1504
left a comment
There was a problem hiding this comment.
Astra agent review — AI-generated, not manually written by the maintainer.
Reviewed at the maintainer's request. I inspected the current schema and documentation and cross-checked node-protodef #177 with a local interpreter/compiler reproduction. The remaining inline point concerns the digest representation contract; no full repository test suite was run.
| ### **hash** ({ alg: String, type: Type, body: Type }) | ||
| Arguments: | ||
| * alg : the hash algorithm, one of : `crc32c` (CRC-32C, the Castagnoli polynomial, reflected, all-ones init and final xor, a 4 byte digest) | ||
| * type : the type the hash is written and read as, of constant size and at least as wide as the digest |
There was a problem hiding this comment.
Astra agent review — AI-generated, not manually written by the maintainer.
Could this require a lossless integer representation and define the supported widths/signed interpretation? Constant byte size alone permits f32: node-protodef #177 currently accepts it, and hashing the nine bytes 123456789 then reading that field returns 0xe3069300 instead of the CRC32C 0xe3069283 in both its interpreter and compiler. The dataType schema reference also allows fixed-size nonnumeric types without saying how a digest maps into them. An explicit representation contract (including aliases and what signed output means if wider than 32 bits) would let implementations reject unsupported types consistently instead of silently discarding digest bits.
Adds `hash`: `["hash", { "alg": String, "type": Type, "body": Type }]`. Writing serializes the value as `body`, hashes the bytes with `alg` and writes the digest as `type`; reading yields the digest. It is a one-way field for protocols that carry a checksum of something in place of the thing itself, such as Minecraft's `HashedSlot` (a CRC32C per item component since 1.21.5).
`crc32` and `crc32c` are integer digests; when `type` is signed the hash is written in two's complement. Other algorithms are whatever the platform's crypto provides and produce raw digest bytes, so `type` is then a fixed-length `buffer`.
Schema, datatype reference and docs. Implementation: ProtoDef-io/node-protodef (PR to follow, linked below).
Context: PrismarineJS/prismarine-item#184 (comment)