Skip to content

Add the hash datatype - #65

Open
u9g wants to merge 4 commits into
masterfrom
hash-type
Open

u9g wants to merge 4 commits into
masterfrom
hash-type

Conversation

@u9g

@u9g u9g commented Sep 6, 2026

Copy link
Copy Markdown

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)

u9g added 3 commits September 12, 2026 14:11
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 rom1504 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread doc/datatypes/utils.md
### **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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants