Wire serde_2026 serialization into chia_rs - #1437
Conversation
There was a problem hiding this comment.
Pull request overview
This PR wires the new serde_2026 serialization format into chia_rs/chia-consensus to support upcoming post-HF2 INTERNED_GENERATOR work, including a new generator serializer and Python-facing APIs/constants.
Changes:
- Add
solution_generator_2026()(Rust + Python binding) that serializes generators viaserialize_2026. - Introduce
chia_consensus::serde_2026::node_from_bytes_auto()with consensus-oriented caps and aprogram_bytesre-export for downstream consumers. - Pin
clvmrto a specific git revision (via[patch.crates-io]) to access the new serde_2026 functionality, and exposeSERDE_2026_MAGIC_PREFIXto Python.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
wheel/src/api.rs |
Adds Python binding for solution_generator_2026() and exports SERDE_2026_MAGIC_PREFIX. |
wheel/python/chia_rs/chia_rs.pyi |
Updates Python type stubs for the new function and magic-prefix constant. |
wheel/generate_type_stubs.py |
Updates stub generator to include the new solution_generator_2026() function. |
crates/chia-consensus/src/solution_generator.rs |
Adds Rust solution_generator_2026() using serialize_2026. |
crates/chia-consensus/src/serde_2026.rs |
Adds consensus-tuned auto-deserializer dispatching between classic/backrefs/serde_2026. |
crates/chia-consensus/src/program_bytes.rs |
Re-exports node_from_bytes_auto() for easier cross-crate consumption. |
crates/chia-consensus/src/lib.rs |
Exposes the new program_bytes and serde_2026 modules. |
Cargo.toml |
Adds a [patch.crates-io] pin for clvmr to the serde_2026 git revision. |
Cargo.lock |
Reflects clvmr being sourced from the pinned git revision. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Coverage Report for CI Build 29473136351Coverage decreased (-0.08%) to 81.815%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
4016bb3 to
66bb8aa
Compare
5086dce to
72bb358
Compare
72bb358 to
7ace3d5
Compare
b919dca to
90286c2
Compare
732e071 to
fbdd7c3
Compare
fbdd7c3 to
a3019b8
Compare
a3019b8 to
4ddf1d4
Compare
a4a3ce7 to
1d716da
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1d716da. Configure here.
- Pin clvmr to clvm_rs PR #708 (serde_2026 git rev) via [patch.crates-io] - Add chia_consensus::serde_2026: node_from_bytes_auto with consensus caps (CONSENSUS_MAX_ATOM_LEN = 1 MiB, CONSENSUS_MAX_BLOB_SIZE = 10 MiB) - Add chia_consensus::program_bytes: re-exports node_from_bytes_auto - Add solution_generator_2026() using serialize_2026 (serde_2026 format) - Expose solution_generator_2026() and SERDE_2026_MAGIC_PREFIX to Python
1d716da to
fa3d73e
Compare
Add max_canonical_blob_size(max_cost, cost_per_byte), which computes the largest canonical serde_2026 wire size any within-cost generator can have (max_cost / cost_per_byte + 5 + prefix). The proof lives in its doc comment: the canonical encoding never exceeds interned_vbytes + 5 (tight at atom length 2^20; requires atom lengths < 2^27, guaranteed by CONSENSUS_MAX_ATOM_LEN), and the cost ceiling bounds interned_vbytes. At mainnet constants the bound is 916,677 bytes. CONSENSUS_MAX_BLOB_SIZE stays 10 MiB but its comment now states the principled floor (~917 KB), that the value was inherited from clvm_rs's DeserializeLimits default, and that tightening it (and consensus-vs-peer layering) is deliberately left open for review. Tests: - adversarial shapes where the bound's slack is smallest (max-length atom, >63 distinct atom lengths, >4096 unique pairs, doubling DAG), including exact tightness at the 2^20 atom - generality across arbitrary cost constants, (0, 7) to (u64::MAX, u64::MAX) - the bound is a theorem parameterized by the constants, not a property of 11e9/12000 - new fuzz target serde-2026-size-bound: arbitrary trees AND arbitrary cost constants per run; checks the encoding bound, the derived cap for affordable trees, and blob round-trip identity. Seeded with inputs at 0.98-0.999 of the bound (fuzz/seeds/, corpus/ is gitignored). Fuzzing already caught an overflow in the bound function at extreme constants (fixed with saturating_add). Also: solution_generator_2026 round-trip test, auto-dispatch and size-cap tests for node_from_bytes_auto, and a named SERDE_2026_COMPRESSION_LEVEL constant replacing the bare 0 at the serialize_2026 callsite. Derivation provenance: SERDE2026_UPPER_BOUND.md and SERIALIZATION_DOS_ANALYSIS.md in the generator-identity-hf-analysis repo.
Drop CONSENSUS_MAX_BLOB_SIZE (10 MiB, inherited from clvm_rs's DeserializeLimits default, unconnected to any consensus property). node_from_bytes_auto now takes max_blob_size, which callers derive from the network's cost constants via max_canonical_blob_size — the limit is a property of max_block_cost_clvm / cost_per_byte and the proof, not a standalone number. Any headroom multiplier (e.g. to tolerate the non-minimal encodings that strict=false admits) is caller policy.
Drop the 9 binary near-bound seed files in favor of a gen_serde_2026_fuzz_seeds example that reproduces them (fixed RNG seed): a fullness-maximizing search over the fuzz target's input format, writing seeds at 0.98-0.999 of the proven bound directly into the gitignored corpus dir. Reviewable and regenerable instead of opaque blobs in git.
fa3d73e to
80131ea
Compare
Per review: rand (with small_rng) is already a dev-dependency. The corpus is regenerable scratch, so cross-version drift of seeded SmallRng output is acceptable; a fixed seed keeps runs reproducible per rand version. Co-authored-by: Cursor <cursoragent@cursor.com>
b61c6c5 to
9a438ab
Compare
There is no independent max-atom-length constant: atoms appear as literals in the canonical serialization, so an atom of length L forces a canonical blob of at least L bytes. Any atom too big for the caller-derived blob cap therefore cannot be part of a cost-valid generator, and max_blob_size doubles as the per-atom cap passed to deserialize_2026. Per review feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
9a438ab to
2cd665e
Compare

Summary
Wire the serde_2026 serialization format into chia_rs as a foundation for the post-HF2
INTERNED_GENERATORcost model. This PR ships the producer/consumer surface only — nothing here changes consensus behavior until #1438 wires it into validation.Depends on clvmr ≥ 0.18.0 (serde_2026 shipped to crates.io in June), which
mainalready uses — no Cargo.toml changes.Changes
chia_consensus::serde_2026::node_from_bytes_auto: deserialize CLVM bytes, auto-detecting classic / backrefs / serde_2026 by magic prefix. Callers supply the max blob size; the per-atom cap (1 MiB) is fixed. Accepts non-minimal encodings (strict = false) — rationale documented inline: post-HF2, identity and cost come from the interned tree, so overlong varints only hurt their sender.chia_consensus::serde_2026::max_canonical_blob_size(max_cost, cost_per_byte): the provable upper bound on the canonical serde_2026 wire size of any within-cost generator (max_cost / cost_per_byte + 5 + prefix). The proof is in its doc comment; at mainnet constants the bound is 916,677 bytes. This is what callers should derive thenode_from_bytes_autosize cap from — the limit is a property of the cost constants, not a hardcoded number. How much headroom to allow for non-minimal encodings is caller policy (input welcome).solution_generator_2026(): builds a generator usingserialize_2026, at a namedSERDE_2026_COMPRESSION_LEVEL(level 0; levels are producer-side only, all decodable by the one deserializer).solution_generator_2026()andSERDE_2026_MAGIC_PREFIX, stub generator updated.Tests
solution_generator_2026round-trip throughnode_from_bytes_auto.(0, 7)to(u64::MAX, u64::MAX)— not just mainnet's 11e9 / 12,000.serde-2026-size-bound: arbitrary trees and arbitrary cost constants per run; asserts the encoding bound, the derived cap for affordable trees, and round-trip identity. A deterministic seed generator (gen_serde_2026_fuzz_seedsexample) produces corpus entries at 0.98–0.999 of the bound so mutation starts at the boundary. Fuzzing already earned its keep: it caught an integer overflow in the bound function at extreme constants (fixed with saturation).Depends on
#1436 (
InternedBlockBuilder) — merged; this PR is based on currentmain.Follow-up (#1438 / FullBlock #1456)
Arvid's merged FullBlock format (#1456) adds v1 blocks that store the generator as a raw length-prefixed
Vec<u8>(transactions_generator_buffer), giving a place to inspect bytes forSERDE_2026_MAGIC_PREFIXbefore HF2 and reject serde_2026 generators whileINTERNED_GENERATORis inactive — without parsing CLVM first. This PR does not wire that guard; #1438 addscheck_generator_quote/ consensus-path checks, applying the prefix check to both legacyProgrambytes and the v1 raw buffer path.Test plan
from chia_rs import solution_generator_2026, SERDE_2026_MAGIC_PREFIXworkssolution_generator_2026(...)output starts withSERDE_2026_MAGIC_PREFIX(unit-tested)Note
Medium Risk
New deserialization and provable size-bound logic touch generator wire format and pre-charge decoding limits; behavior is not yet on the consensus path but mistakes here would matter for the HF2 follow-up.
Overview
Adds serde_2026 as a producer/consumer surface ahead of post-HF2 interned-generator work, without changing live consensus validation in this PR.
New
chia_consensus::serde_2026exposesnode_from_bytes_auto(magic-prefix dispatch to classic/backrefs/serde_2026 with caller-suppliedmax_blob_size, non-minimal encodings allowed viastrict = false),max_canonical_blob_size(cost-derived wire cap, ~916 677 bytes at mainnet constants), andSERDE_2026_COMPRESSION_LEVEL.solution_generator_2026builds generators withserialize_2026.Python
chia_rsgainssolution_generator_2026andSERDE_2026_MAGIC_PREFIX(stubs updated). Coverage includes unit tests on tight encoding shapes and format dispatch, aserde-2026-size-boundfuzz target, andgen_serde_2026_fuzz_seedsto seed near-bound corpora.Reviewed by Cursor Bugbot for commit 2cd665e. Bugbot is set up for automated code reviews on this repo. Configure here.