docs(book): add document serialization wire format chapter#3392
docs(book): add document serialization wire format chapter#3392QuantumExplorer wants to merge 4 commits intov3.1-devfrom
Conversation
Describes the binary wire format for serialized documents, including the version varint, field layout, time fields bitfield, property encoding rules, and common pitfalls for third-party deserializers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
📖 Book Preview built successfully. Download the preview from the workflow artifacts. Updated at 2026-03-20T16:59:57.877Z |
📝 WalkthroughWalkthroughAdded a new documentation chapter describing the Dash Platform document binary serialization format (v0–v2) and updated the book navigation to include Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~2 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@book/src/serialization/document-serialization.md`:
- Line 11: Fenced code blocks in document-serialization.md are missing language
identifiers (causing markdownlint MD040); update each unnamed triple-backtick
block that contains the ASCII art/hex examples (e.g., the block starting with
"┌──────────────────────┐", the block with "0x01 [32 bytes creatorId]", the
block with "0x01 [8 bytes big-endian u64]", and the block starting "02 ←
serialization version") by adding a language tag such as text (i.e., change ```
to ```text) for all four occurrences so the lint rule is satisfied.
- Around line 180-181: The example shows an invalid two-byte varint for
serialization version 2; update the worked example so the serialization version
is represented by a single byte 0x02 and remove the spurious continuation line
that reads "02 ← (continuation of varint, actual value = 2)". Locate the lines
containing "02 ← serialization version (varint: 2)" and the following
continuation line and delete or replace the continuation line so the example
only shows the single-byte varint representation for version 2.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7fb5ec55-d609-4126-862c-162ceaa2e1f4
📒 Files selected for processing (2)
book/src/SUMMARY.mdbook/src/serialization/document-serialization.md
Address CodeRabbit review feedback: - Add `text` language tags to fenced code blocks (MD040) - Fix incorrect varint continuation line — version 2 is a single-byte varint, the second 0x02 was the first byte of $id - Rewrite worked example with correct byte offsets and add decoded tool output for clarity Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
The most critical finding is confirmed: the documentation incorrectly states properties are serialized in BTreeMap/alphabetical order, when the code uses IndexMap sorted by schema-defined position order. This would cause third-party deserializers to misalign fields. Two additional documentation gaps are validated: optional date encoding has an undocumented 0xff prefix, and array types are missing from the type table. The remaining findings are minor but valid.
Reviewed commit: d61d2fb
🔴 1 blocking | 🟡 3 suggestion(s) | 💬 2 nitpick(s)
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [BLOCKING] line 129: User properties are serialized in schema position order, not alphabetical/BTreeMap order
Line 129 states `document_type.properties()` returns a `BTreeMap`, so properties are in alphabetical order. This is incorrect. The trait `DocumentTypeV0Getters::properties()` returns `&IndexMap<String, DocumentProperty>` (see `accessors/v0/mod.rs:43`). Properties are inserted into the IndexMap sorted by their `position` field from the JSON schema (`try_from_schema/mod.rs:195-202`). For the withdrawals schema, position order is: transactionIndex(0), transactionSignHeight(1), amount(2), coreFeePerByte(3), pooling(4), outputScript(5), status(6) — which differs significantly from alphabetical. The same error recurs on line 163 ('in alphabetical/BTreeMap order') and line 247 in Common Pitfalls item 4 ('Property order is alphabetical (BTreeMap order), not declaration order'). Notably, line 32 in the high-level diagram correctly says '(in schema order)', creating an internal contradiction. A third-party decoder following alphabetical-order guidance will misalign every field.
- [SUGGESTION] lines 135-156: Optional date (and potentially other types) encoding has undocumented 0xff value prefix
The doc says optional fields have a 1-byte presence flag (0x00/0x01) then the value. However, the serializer calls `property_type.encode_value_ref_with_size(value, property.required)` with `required=false` for optional fields. For `DocumentPropertyType::Date`, this prepends an additional `0xff` byte before the 8-byte f64 payload (`property/mod.rs:1008-1019`, confirmed by test at line 3675). So the actual wire format for a present optional date is: `0x01` (presence from serialize.rs) + `0xff` (from encode_value_ref_with_size) + 8 bytes f64 = 10 bytes total. The doc's type table on line 156 only shows '8 bytes big-endian f64' with no mention of the extra prefix byte when the field is optional.
- [SUGGESTION] lines 131-137: Transient fields get a presence byte even when required
The serialization code at `serialize.rs:232` checks `if !property.required || property.transient { buffer.push(1); }` — meaning a field that is both `required` AND `transient` receives a 1-byte presence indicator, just like optional fields. The documentation only describes the required/optional distinction (lines 133-137) and does not mention the transient exception. While transient fields may be uncommon, a third-party implementer could encounter them.
- [SUGGESTION] lines 143-157: Array type encoding is missing from the type table
The type encoding table omits `array`. The serializer handles `DocumentPropertyType::Array` by writing a varint element count followed by each element encoded in sequence (`property/mod.rs:1118-1132`). `VariableTypeArray` is rejected during serialization. Both array types are rejected during tree-key deserialization. This is a documentation gap for any implementer who encounters an array-typed property.
|
|
||
| ### User-defined properties | ||
|
|
||
| Properties are serialized **in the order defined by the document type's schema** (`document_type.properties()` returns a `BTreeMap`, so properties are in alphabetical order by field name). |
There was a problem hiding this comment.
🔴 Blocking: User properties are serialized in schema position order, not alphabetical/BTreeMap order
Line 129 states document_type.properties() returns a BTreeMap, so properties are in alphabetical order. This is incorrect. The trait DocumentTypeV0Getters::properties() returns &IndexMap<String, DocumentProperty> (see accessors/v0/mod.rs:43). Properties are inserted into the IndexMap sorted by their position field from the JSON schema (try_from_schema/mod.rs:195-202). For the withdrawals schema, position order is: transactionIndex(0), transactionSignHeight(1), amount(2), coreFeePerByte(3), pooling(4), outputScript(5), status(6) — which differs significantly from alphabetical. The same error recurs on line 163 ('in alphabetical/BTreeMap order') and line 247 in Common Pitfalls item 4 ('Property order is alphabetical (BTreeMap order), not declaration order'). Notably, line 32 in the high-level diagram correctly says '(in schema order)', creating an internal contradiction. A third-party decoder following alphabetical-order guidance will misalign every field.
source: ['codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [BLOCKING] line 129: User properties are serialized in schema position order, not alphabetical/BTreeMap order
Line 129 states `document_type.properties()` returns a `BTreeMap`, so properties are in alphabetical order. This is incorrect. The trait `DocumentTypeV0Getters::properties()` returns `&IndexMap<String, DocumentProperty>` (see `accessors/v0/mod.rs:43`). Properties are inserted into the IndexMap sorted by their `position` field from the JSON schema (`try_from_schema/mod.rs:195-202`). For the withdrawals schema, position order is: transactionIndex(0), transactionSignHeight(1), amount(2), coreFeePerByte(3), pooling(4), outputScript(5), status(6) — which differs significantly from alphabetical. The same error recurs on line 163 ('in alphabetical/BTreeMap order') and line 247 in Common Pitfalls item 4 ('Property order is alphabetical (BTreeMap order), not declaration order'). Notably, line 32 in the high-level diagram correctly says '(in schema order)', creating an internal contradiction. A third-party decoder following alphabetical-order guidance will misalign every field.
| **Optional fields**: A 1-byte presence flag is written first: | ||
| - `0x01` followed by the encoded value — field is present | ||
| - `0x00` — field is absent | ||
|
|
||
| #### Value encoding by type | ||
|
|
||
| All numeric values use **big-endian** byte order. | ||
|
|
||
| | Type | Encoding | | ||
| |------|----------| | ||
| | `u8` / `i8` | 1 byte | | ||
| | `u16` / `i16` | 2 bytes big-endian | | ||
| | `u32` / `i32` | 4 bytes big-endian | | ||
| | `u64` / `i64` | 8 bytes big-endian | | ||
| | `u128` / `i128` | 16 bytes big-endian | | ||
| | `f64` | 8 bytes big-endian IEEE 754 | | ||
| | `boolean` | 1 byte: `0x01` = true, `0x00` = false | | ||
| | `string` | varint length prefix + UTF-8 bytes | | ||
| | `byteArray` (fixed size) | raw bytes (no length prefix if min_size == max_size) | | ||
| | `byteArray` (variable size) | varint length prefix + raw bytes | | ||
| | `identifier` | 32 bytes raw | | ||
| | `date` | 8 bytes big-endian f64 | |
There was a problem hiding this comment.
🟡 Suggestion: Optional date (and potentially other types) encoding has undocumented 0xff value prefix
The doc says optional fields have a 1-byte presence flag (0x00/0x01) then the value. However, the serializer calls property_type.encode_value_ref_with_size(value, property.required) with required=false for optional fields. For DocumentPropertyType::Date, this prepends an additional 0xff byte before the 8-byte f64 payload (property/mod.rs:1008-1019, confirmed by test at line 3675). So the actual wire format for a present optional date is: 0x01 (presence from serialize.rs) + 0xff (from encode_value_ref_with_size) + 8 bytes f64 = 10 bytes total. The doc's type table on line 156 only shows '8 bytes big-endian f64' with no mention of the extra prefix byte when the field is optional.
source: ['codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [SUGGESTION] lines 135-156: Optional date (and potentially other types) encoding has undocumented 0xff value prefix
The doc says optional fields have a 1-byte presence flag (0x00/0x01) then the value. However, the serializer calls `property_type.encode_value_ref_with_size(value, property.required)` with `required=false` for optional fields. For `DocumentPropertyType::Date`, this prepends an additional `0xff` byte before the 8-byte f64 payload (`property/mod.rs:1008-1019`, confirmed by test at line 3675). So the actual wire format for a present optional date is: `0x01` (presence from serialize.rs) + `0xff` (from encode_value_ref_with_size) + 8 bytes f64 = 10 bytes total. The doc's type table on line 156 only shows '8 bytes big-endian f64' with no mention of the extra prefix byte when the field is optional.
| Each property is encoded based on its type and whether it is required: | ||
|
|
||
| **Required fields**: The value is written directly with no prefix byte. | ||
|
|
||
| **Optional fields**: A 1-byte presence flag is written first: | ||
| - `0x01` followed by the encoded value — field is present | ||
| - `0x00` — field is absent |
There was a problem hiding this comment.
🟡 Suggestion: Transient fields get a presence byte even when required
The serialization code at serialize.rs:232 checks if !property.required || property.transient { buffer.push(1); } — meaning a field that is both required AND transient receives a 1-byte presence indicator, just like optional fields. The documentation only describes the required/optional distinction (lines 133-137) and does not mention the transient exception. While transient fields may be uncommon, a third-party implementer could encounter them.
source: ['claude']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [SUGGESTION] lines 131-137: Transient fields get a presence byte even when required
The serialization code at `serialize.rs:232` checks `if !property.required || property.transient { buffer.push(1); }` — meaning a field that is both `required` AND `transient` receives a 1-byte presence indicator, just like optional fields. The documentation only describes the required/optional distinction (lines 133-137) and does not mention the transient exception. While transient fields may be uncommon, a third-party implementer could encounter them.
| | Type | Encoding | | ||
| |------|----------| | ||
| | `u8` / `i8` | 1 byte | | ||
| | `u16` / `i16` | 2 bytes big-endian | | ||
| | `u32` / `i32` | 4 bytes big-endian | | ||
| | `u64` / `i64` | 8 bytes big-endian | | ||
| | `u128` / `i128` | 16 bytes big-endian | | ||
| | `f64` | 8 bytes big-endian IEEE 754 | | ||
| | `boolean` | 1 byte: `0x01` = true, `0x00` = false | | ||
| | `string` | varint length prefix + UTF-8 bytes | | ||
| | `byteArray` (fixed size) | raw bytes (no length prefix if min_size == max_size) | | ||
| | `byteArray` (variable size) | varint length prefix + raw bytes | | ||
| | `identifier` | 32 bytes raw | | ||
| | `date` | 8 bytes big-endian f64 | | ||
| | `object` | Nested fields serialized recursively in their schema order | |
There was a problem hiding this comment.
🟡 Suggestion: Array type encoding is missing from the type table
The type encoding table omits array. The serializer handles DocumentPropertyType::Array by writing a varint element count followed by each element encoded in sequence (property/mod.rs:1118-1132). VariableTypeArray is rejected during serialization. Both array types are rejected during tree-key deserialization. This is a documentation gap for any implementer who encounters an array-typed property.
source: ['codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [SUGGESTION] lines 143-157: Array type encoding is missing from the type table
The type encoding table omits `array`. The serializer handles `DocumentPropertyType::Array` by writing a varint element count followed by each element encoded in sequence (`property/mod.rs:1118-1132`). `VariableTypeArray` is rejected during serialization. Both array types are rejected during tree-key deserialization. This is a documentation gap for any implementer who encounters an array-typed property.
| 6de0 157e | ||
| c501 ← $revision (varint: 197) |
There was a problem hiding this comment.
💬 Nitpick: Worked example should note why $creatorId is absent despite v2
The example uses serialization version 2. The high-level diagram (line 20-21) states v2 includes $creatorId for document types supporting transfers/trading. The hex walkthrough jumps from $ownerId directly to $revision with no mention of $creatorId. The code (serialize.rs:498-500) shows the entire $creatorId section (including the flag byte) is omitted when trade_mode == None && !documents_transferable(). A brief note would help readers following along understand why the field is absent entirely rather than present with a 0x00 flag.
💡 Suggested change
| 6de0 157e | |
| c501 ← $revision (varint: 197) | |
| Add a comment line in the hex walkthrough, e.g.: '← $creatorId: omitted (withdrawal type is not transferable/tradeable)' |
source: ['claude']
| | `byteArray` (fixed size) | raw bytes (no length prefix if min_size == max_size) | | ||
| | `byteArray` (variable size) | varint length prefix + raw bytes | | ||
| | `identifier` | 32 bytes raw | | ||
| | `date` | 8 bytes big-endian f64 | |
There was a problem hiding this comment.
💬 Nitpick: User-property date type (f64) vs system timestamp type (u64) could be explicitly contrasted
System time fields ($createdAt, etc.) on line 112 are documented as u64, and user-property date on line 156 is documented as f64. Both are 8 bytes but different types. The information is technically present in both sections, but a reader implementing a deserializer might miss the distinction since both are 'timestamp-like' 8-byte values.
source: ['claude']
…details - Properties use schema position order (IndexMap), not alphabetical (BTreeMap) - Document transient fields always get a presence byte even when required - Add array type encoding to the type table - Note 0xff prefix for optional date fields - Clarify user-property date (f64) vs system timestamp (u64) encoding - Note why $creatorId is absent in the v2 worked example Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
book/src/serialization/document-serialization.md (1)
168-179:⚠️ Potential issue | 🟠 MajorProperty-order guidance is still contradictory and can misalign decoders.
Line 129 correctly says schema-position order, but Line 253 still says alphabetical/BTreeMap order. Please make this consistent and ensure the worked example list (Lines 168-179) matches actual schema
positionvalues.Suggested doc fix
-4. **Property order is alphabetical (BTreeMap order), not declaration order.** If you hard-code field offsets based on the JSON schema declaration order rather than alphabetical order, fields will be read from the wrong positions. +4. **Property order is schema position order (IndexMap insertion order), not alphabetical order.** If you parse properties using alphabetical/key order instead of each field’s schema `position`, fields will be read from the wrong offsets.Run this to verify the withdrawal property order from schema
positionvalues before finalizing the worked example list:#!/bin/bash set -euo pipefail python - <<'PY' import json, pathlib target_fields = { "transactionIndex", "transactionSignHeight", "amount", "coreFeePerByte", "pooling", "outputScript", "status", } def walk(obj): if isinstance(obj, dict): yield obj for v in obj.values(): yield from walk(v) elif isinstance(obj, list): for i in obj: yield from walk(i) for p in pathlib.Path(".").rglob("*.json"): try: data = json.loads(p.read_text(encoding="utf-8")) except Exception: continue for node in walk(data): props = node.get("properties") if isinstance(node, dict) else None if isinstance(props, dict) and target_fields.issubset(props.keys()): rows = [] for k in target_fields: v = props.get(k, {}) pos = v.get("position") if isinstance(v, dict) else None rows.append((pos, k)) rows.sort(key=lambda x: (x[0] is None, x[0], x[1])) print(f"\nSchema candidate: {p}") print("Ordered by position:") for pos, k in rows: print(f" {pos}: {k}") PYAlso applies to: 253-254
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@book/src/serialization/document-serialization.md` around lines 168 - 179, The worked example and prose for the withdrawals document are inconsistent: replace any mention that properties are ordered alphabetically/BTreeMap with a clear statement that properties must follow schema "position" order, then reorder the example table rows for the withdrawal document (the properties amount, coreFeePerByte, outputScript, pooling, status, transactionIndex, transactionSignHeight) to match the schema "position" values; verify the final ordering by running the provided JSON-walking verification script against the contract schemas and update the example/table to exactly match the discovered positions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@book/src/serialization/document-serialization.md`:
- Around line 168-179: The worked example and prose for the withdrawals document
are inconsistent: replace any mention that properties are ordered
alphabetically/BTreeMap with a clear statement that properties must follow
schema "position" order, then reorder the example table rows for the withdrawal
document (the properties amount, coreFeePerByte, outputScript, pooling, status,
transactionIndex, transactionSignHeight) to match the schema "position" values;
verify the final ordering by running the provided JSON-walking verification
script against the contract schemas and update the example/table to exactly
match the discovered positions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 30f8fa40-b068-437d-9c90-b7fa296a9f1c
📒 Files selected for processing (1)
book/src/serialization/document-serialization.md
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
The prior push correctly fixed the main body text (property ordering, transient fields, date encoding, array type). However, two ordering inconsistencies remain: pitfall #4 still references the old alphabetical/BTreeMap order, and the worked example table claims schema position order but is actually alphabetical. Both would mislead implementers.
Reviewed commit: 1425adf
🔴 2 blocking
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [BLOCKING] line 253: Pitfall #4 still says alphabetical/BTreeMap order — contradicts the corrected text at line 129
Line 129 was correctly updated to say 'Properties are serialized **in schema position order**', but pitfall #4 still reads 'Property order is alphabetical (BTreeMap order), not declaration order.' This directly contradicts the fix. The serializer at `packages/rs-dpp/src/document/v0/serialize.rs:213-216` iterates `document_type.properties()` which returns an `IndexMap` sorted by each property's `position` field (sorted at schema parse time in `try_from_schema/mod.rs:193-200`). Implementers who skip to the pitfalls section will get the wrong ordering rule.
- [BLOCKING] lines 168-178: Worked example property table is in alphabetical order, not schema position order
Line 168 says 'listed here in schema position order' but the table lists properties alphabetically: amount, coreFeePerByte, outputScript, pooling, status, transactionIndex, transactionSignHeight. The actual positions from `packages/withdrawals-contract/schema/v1/withdrawals-documents.json` are: transactionIndex=0, transactionSignHeight=1, amount=2, coreFeePerByte=3, pooling=4, outputScript=5, status=6. The current table order would cause an implementer to start the property payload with `amount` instead of `transactionIndex`, misaligning every subsequent field.
|
|
||
| 3. **The time fields bitfield is variable-length data.** The 2-byte bitfield tells you how many time fields follow. If you assume a fixed number of time fields, any document with a different set of time fields (e.g., one that includes `$transferredAt` or block heights) will be misaligned. | ||
|
|
||
| 4. **Property order is alphabetical (BTreeMap order), not declaration order.** If you hard-code field offsets based on the JSON schema declaration order rather than alphabetical order, fields will be read from the wrong positions. |
There was a problem hiding this comment.
🔴 Blocking: Pitfall #4 still says alphabetical/BTreeMap order — contradicts the corrected text at line 129
Line 129 was correctly updated to say 'Properties are serialized in schema position order', but pitfall #4 still reads 'Property order is alphabetical (BTreeMap order), not declaration order.' This directly contradicts the fix. The serializer at packages/rs-dpp/src/document/v0/serialize.rs:213-216 iterates document_type.properties() which returns an IndexMap sorted by each property's position field (sorted at schema parse time in try_from_schema/mod.rs:193-200). Implementers who skip to the pitfalls section will get the wrong ordering rule.
source: ['claude', 'codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [BLOCKING] line 253: Pitfall #4 still says alphabetical/BTreeMap order — contradicts the corrected text at line 129
Line 129 was correctly updated to say 'Properties are serialized **in schema position order**', but pitfall #4 still reads 'Property order is alphabetical (BTreeMap order), not declaration order.' This directly contradicts the fix. The serializer at `packages/rs-dpp/src/document/v0/serialize.rs:213-216` iterates `document_type.properties()` which returns an `IndexMap` sorted by each property's `position` field (sorted at schema parse time in `try_from_schema/mod.rs:193-200`). Implementers who skip to the pitfalls section will get the wrong ordering rule.
| The withdrawals contract defines a `withdrawal` document type with these properties (listed here in schema position order): | ||
|
|
||
| | Property | Type | Required | | ||
| |----------|------|----------| | ||
| | `amount` | integer (i64) | yes | | ||
| | `coreFeePerByte` | integer (i64) | yes | | ||
| | `outputScript` | byteArray (23–25 bytes, variable) | yes | | ||
| | `pooling` | integer (i64) | yes | | ||
| | `status` | integer (i64) | yes | | ||
| | `transactionIndex` | integer (i64) | no | | ||
| | `transactionSignHeight` | integer (i64) | no | |
There was a problem hiding this comment.
🔴 Blocking: Worked example property table is in alphabetical order, not schema position order
Line 168 says 'listed here in schema position order' but the table lists properties alphabetically: amount, coreFeePerByte, outputScript, pooling, status, transactionIndex, transactionSignHeight. The actual positions from packages/withdrawals-contract/schema/v1/withdrawals-documents.json are: transactionIndex=0, transactionSignHeight=1, amount=2, coreFeePerByte=3, pooling=4, outputScript=5, status=6. The current table order would cause an implementer to start the property payload with amount instead of transactionIndex, misaligning every subsequent field.
💡 Suggested change
| The withdrawals contract defines a `withdrawal` document type with these properties (listed here in schema position order): | |
| | Property | Type | Required | | |
| |----------|------|----------| | |
| | `amount` | integer (i64) | yes | | |
| | `coreFeePerByte` | integer (i64) | yes | | |
| | `outputScript` | byteArray (23–25 bytes, variable) | yes | | |
| | `pooling` | integer (i64) | yes | | |
| | `status` | integer (i64) | yes | | |
| | `transactionIndex` | integer (i64) | no | | |
| | `transactionSignHeight` | integer (i64) | no | | |
| The withdrawals contract defines a `withdrawal` document type with these properties (listed here in schema position order): | |
| | Property | Type | Required | | |
| |----------|------|----------| | |
| | `transactionIndex` | integer (i64) | no | | |
| | `transactionSignHeight` | integer (i64) | no | | |
| | `amount` | integer (i64) | yes | | |
| | `coreFeePerByte` | integer (i64) | yes | | |
| | `pooling` | integer (i64) | yes | | |
| | `outputScript` | byteArray (23\u201325 bytes, variable) | yes | | |
| | `status` | integer (i64) | yes | |
source: ['claude', 'codex']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `book/src/serialization/document-serialization.md`:
- [BLOCKING] lines 168-178: Worked example property table is in alphabetical order, not schema position order
Line 168 says 'listed here in schema position order' but the table lists properties alphabetically: amount, coreFeePerByte, outputScript, pooling, status, transactionIndex, transactionSignHeight. The actual positions from `packages/withdrawals-contract/schema/v1/withdrawals-documents.json` are: transactionIndex=0, transactionSignHeight=1, amount=2, coreFeePerByte=3, pooling=4, outputScript=5, status=6. The current table order would cause an implementer to start the property payload with `amount` instead of `transactionIndex`, misaligning every subsequent field.
…ition order - Reorder worked example property table to match actual schema positions - Fix pitfall #4 to say schema position order, not alphabetical/BTreeMap Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@book/src/serialization/document-serialization.md`:
- Line 158: Update the table row for `date` to clarify that an optional date
includes the standard presence byte plus the date-specific prefix and 8-byte f64
(i.e., present optional date is 0x01 (presence) + 0xff
(encode_value_ref_with_size prefix) + 8 bytes = 10 bytes), or add a short note
under the table stating the 0xff prefix is in addition to the 0x01 presence flag
described earlier (lines referring to the presence flag logic); reference
`encode_value_ref_with_size` and the presence-flag logic to make the relation
explicit.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e2eb556c-f3c5-4000-a708-ab907fe46a9d
📒 Files selected for processing (1)
book/src/serialization/document-serialization.md
| | `byteArray` (fixed size) | raw bytes (no length prefix if min_size == max_size) | | ||
| | `byteArray` (variable size) | varint length prefix + raw bytes | | ||
| | `identifier` | 32 bytes raw | | ||
| | `date` | 8 bytes big-endian f64 (when optional: `0xff` prefix + 8 bytes) | |
There was a problem hiding this comment.
Clarify the complete encoding for optional date fields.
The type table states that optional dates use "0xff prefix + 8 bytes," but this omits the initial presence flag described in lines 135–137. Based on the serialization logic, an optional date field that is present should be encoded as:
0x01(presence flag, per lines 135–137)0xff(date-type-specific prefix fromencode_value_ref_with_size)- 8 bytes (the f64 value)
Total: 10 bytes
The current table entry could mislead implementers into expecting 9 bytes.
📝 Suggested clarification
-| `date` | 8 bytes big-endian f64 (when optional: `0xff` prefix + 8 bytes) |
+| `date` | 8 bytes big-endian f64 (when optional: `0x01` presence + `0xff` + 8 bytes) |Alternatively, add a note below the table:
Note: The
0xffprefix for optional dates is in addition to the standard0x01presence byte described above, resulting in 10 bytes total for a present optional date field.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| | `date` | 8 bytes big-endian f64 (when optional: `0xff` prefix + 8 bytes) | | |
| | `date` | 8 bytes big-endian f64 (when optional: `0x01` presence + `0xff` + 8 bytes) | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@book/src/serialization/document-serialization.md` at line 158, Update the
table row for `date` to clarify that an optional date includes the standard
presence byte plus the date-specific prefix and 8-byte f64 (i.e., present
optional date is 0x01 (presence) + 0xff (encode_value_ref_with_size prefix) + 8
bytes = 10 bytes), or add a short note under the table stating the 0xff prefix
is in addition to the 0x01 presence flag described earlier (lines referring to
the presence flag logic); reference `encode_value_ref_with_size` and the
presence-flag logic to make the relation explicit.
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
Clean incremental push — the property table in the worked example now correctly uses schema position order (positions 0-6 verified against packages/withdrawals-contract/schema/v1/withdrawals-documents.json) and pitfall #4 accurately describes position-based serialization via IndexMap. Both independent reviewers (Claude + Codex) verified the positions against the withdrawal contract schema and the serialization code in packages/rs-dpp/src/document/v0/serialize.rs. No new issues found.
Reviewed commit: 6355f35
Summary
Test plan
mdbook build booksucceeds🤖 Generated with Claude Code
Summary by CodeRabbit