[CHIA-3823] Add INTERNED_GENERATOR consensus path + serde_2026 prefix guard - #1438
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates consensus-critical generator validation/execution to support the post-HF2 INTERNED_GENERATOR path and adds an explicit pre-activation guard rejecting serde_2026-prefixed generators unless INTERNED_GENERATOR is enabled. It also introduces a dedicated validation error code for invalid generator encoding and adds unit tests covering the relevant flag/prefix combinations.
Changes:
- Add
InvalidTransactionsGeneratorEncoding(mapped to error code150) and use it to explicitly rejectserde_2026generators beforeINTERNED_GENERATORactivation. - Implement the
INTERNED_GENERATORexecution path inrun_block_generator2by deserializing vianode_from_bytes_auto, interning the tree, and computing base cost viatotal_cost_from_tree. - Switch trusted-block generator parsing to
node_from_bytes_autoand add targeted tests forcheck_generator_quote/check_generator_node.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
crates/chia-consensus/src/validation_error.rs |
Adds a new consensus error code for invalid transactions generator encoding and maps it to numeric code 150. |
crates/chia-consensus/src/run_block_generator.rs |
Adds serde_2026 prefix guarding, implements INTERNED_GENERATOR handling in run_block_generator2, updates trusted parsing to auto-detect serialization, and adds tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Coverage Report for CI Build 30489236762Coverage increased (+0.08%) to 81.894%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
b5e3d34 to
a3743a9
Compare
72bb358 to
7ace3d5
Compare
732e071 to
fbdd7c3
Compare
fbdd7c3 to
a3019b8
Compare
9a438ab to
2cd665e
Compare
run_block_generator2: when INTERNED_GENERATOR flag is set, deserialize via node_from_bytes_auto (accepts classic, backrefs, or serde_2026), intern the tree, and compute cost via interned_vbytes * cost_per_byte instead of raw byte length. The deserialization blob cap is derived from the network cost constants via max_canonical_blob_size(). check_generator_quote: add CRITICAL guard that explicitly rejects any serde_2026-prefixed generator when INTERNED_GENERATOR is NOT active. Without this, a serde_2026 generator would be silently accepted on the pre-HF2 chain between code shipping and SIMPLE_GENERATOR activation. The trusted-block coinspend extractors also dispatch through node_from_bytes_auto so they can parse serde_2026 blocks post-HF2. Also adds ErrorCode::InvalidTransactionsGeneratorEncoding (150) for the new rejection path, and tests covering all four flag combinations: - pre-SIMPLE_GENERATOR + serde_2026 -> rejected - SIMPLE_GENERATOR only + serde_2026 -> rejected - INTERNED_GENERATOR + serde_2026 -> accepted - SIMPLE_GENERATOR | INTERNED_GENERATOR + serde_2026 -> accepted Co-authored-by: Cursor <cursoragent@cursor.com>
36f5311 to
25bb8cf
Compare
Per review: minimize new code paths when INTERNED_GENERATOR is not set. The serde_2026 magic prefix starts with 0xfd, an invalid header byte in classic CLVM serialization, so deployed nodes already reject such blobs via node_from_bytes_backrefs() -> GeneratorRuntimeError. Remove the explicit prefix guard and the InvalidTransactionsGeneratorEncoding error code, and gate the node_from_bytes_auto() call sites in the trusted-block helpers on the flag so the flag-off path is identical to main. Co-authored-by: Cursor <cursoragent@cursor.com>
The INTERNED_GENERATOR bypasses were too broad: they skipped the quote checks for every program, re-allowing complex generators after soft_fork9 whenever the interned flag is on. Narrow them: - check_generator_quote() only skips the byte-level check for blobs that actually carry the serde_2026 magic prefix (whose header cannot match the [0xff, 0x01] quote shape); classic/backrefs encodings keep the byte check post-activation. - check_generator_node() no longer skips on INTERNED_GENERATOR, restoring main's behavior. It is the quote enforcement point for serde_2026 blobs, which can only be checked post-deserialization. Co-authored-by: Cursor <cursoragent@cursor.com>
arvidn
left a comment
There was a problem hiding this comment.
the SIMPLE_GENERATOR flag was part of the 2.7.0 soft fork, and has activated. Since we used generator programs in the initial version of block compression we can't require simple generators throughout the whole chain, so we still need this flag to validate early blocks.
We don't need to support (and maybe even should fail) the combination INTERNED_GENERATOR set and SIMPLE_GENERATOR not set. If there's any opportunity to simplify anything.
One crazy idea is that the interned serialization could have an implicit quote around the whole expression.
We discussed this with Bram on the weekly architecture call a while back and he agreed to dropping the I'd propose that as a separate change on top of this one: it deletes the quote checks for this path entirely and changes what the generator tree hash commits to, so it touches the builders too. At the same time we could rename the flag to something like Or I can do it all in this change if you prefer, since the interaction is a bit intricate. Let me know. I can make INTERNED_GENERATOR without SIMPLE_GENERATOR a hard failure in this PR. |
92ea0bd to
bc87bba
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.
Reviewed by Cursor Bugbot for commit bc87bba. Configure here.
Per review: with INTERNED_GENERATOR active, serde_2026 is the only legal generator encoding. Consensus paths now parse via node_from_bytes_2026 (magic prefix mandatory, no classic/backrefs fallback); node_from_bytes_auto remains for non-consensus readers. InternedBlockBuilder::finalize() emits serde_2026 accordingly. The byte-level quote check has nothing left to do in this mode (the encoding is enforced at parse time and can't be examined for the quote shape), so it defers entirely to check_generator_node(). Co-authored-by: Cursor <cursoragent@cursor.com>
bc87bba to
4c9de44
Compare
Per review: propagate the deserializer's SerializationError (the same error the non-interned path produces for undecodable generators) instead of mapping to GeneratorRuntimeError at the call site. Co-authored-by: Cursor <cursoragent@cursor.com>
serde_2026 acceptance activates at exactly the same height as INTERNED_GENERATOR (single activation), and InternedBlockBuilder's cost model (charging by interned vbytes) is only correct once INTERNED_GENERATOR is active. There is no height at which "interned builder + classic emission" is valid, so the default-off constructor flag was a footgun: a default-off caller post-fork would emit blocks that fail #1438's strict validation. finalize() now unconditionally serializes with serialize_2026. Co-authored-by: Cursor <cursoragent@cursor.com>

Summary
Implement the
INTERNED_GENERATORconsensus path inrun_block_generator2.We also tweak
check_generator_quoteto skip checking serde_2026-serialized generators because it's more difficult to tell if they start with aq. We still check incheck_generator_node.Note
High Risk
This is consensus-critical generator serialization and validation; a mismatch with network rules would fork or reject valid blocks.
Overview
With
INTERNED_GENERATOR, block generators are serde_2026-only:InternedBlockBuilder::finalizenow writesserialize_2026instead of backrefs, andrun_block_generator2/ trusted-block coinspend helpers decode vianode_from_bytes_2026with amax_canonical_blob_sizecap, then charge byte cost from the interned tree instead of raw wire length.check_generator_quoteno longer inspects the[0xff, 0x01]prefix when the interned flag is set (serde_2026 bytes are not quote-shaped on the wire); simple-generator “quoted” shape is still enforced incheck_generator_nodeafter decode. Pre-fork behavior is unchanged: serde_2026 blobs fail parse or quote checks without the flag, and classic generators fail onceINTERNED_GENERATORis on.Adds
node_from_bytes_2026inserde_2026, fuzz round-trip coverage withINTERNED_GENERATOR, and unit tests for cross-encoding acceptance/rejection.Reviewed by Cursor Bugbot for commit 616e486. Bugbot is set up for automated code reviews on this repo. Configure here.