Skip to content

[LIMIT_HEAP] Return error if LIMIT_HEAP passed to run_block_generator2 - #1396

Draft
richardkiss wants to merge 13 commits into
mainfrom
limit-heap-error
Draft

[LIMIT_HEAP] Return error if LIMIT_HEAP passed to run_block_generator2#1396
richardkiss wants to merge 13 commits into
mainfrom
limit-heap-error

Conversation

@richardkiss

@richardkiss richardkiss commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Summary

LIMIT_HEAP is a mempool-policy flag that caps the CLVM allocator size. It was originally bundled into MEMPOOL_MODE (via clvmr's CLVM_MEMPOOL_MODE), so it flowed into every call site that passed MEMPOOL_MODE — including run_block_generator2, which is a consensus code path. Arvid confirmed (2026-03-11) that the mempool no longer uses run_block_generator2, so there is no reason for a mempool-policy flag to appear there.

Mempool transactions arrive as SpendBundle objects (already deserialized, individual CoinSpends), so there is no generator to run. The mempool path goes through run_spendbundlevalidate_clvm_and_signature, which runs each puzzle/solution pair individually. validate_clvm_and_signature already hardcodes make_allocator(ConsensusFlags::LIMIT_HEAP) directly, independent of the flags passed in — so LIMIT_HEAP in MEMPOOL_MODE was always redundant for the real mempool path.

The fix is to drop LIMIT_HEAP from MEMPOOL_MODE in flags.rs, making the constant reflect semantic mempool rules (strict args, unknown conditions) rather than bundling in a deployment policy.

Changes

  • flags.rs: MEMPOOL_MODE now excludes LIMIT_HEAP. Callers that genuinely want heap limiting must add it explicitly (only validate_clvm_and_signature needs it, and it already does so directly).
  • run_block_generator.rs: run_block_generator2 returns an error immediately if LIMIT_HEAP is set — defense-in-depth to make the boundary explicit.
  • Call sites (build_compressed_block.rs, get_puzzle_and_solution.rs, spendbundle_conditions.rs, tools, fuzz target): Reverted to plain MEMPOOL_MODE — no more - ConsensusFlags::LIMIT_HEAP arithmetic.
  • generator-tests/*.txt: Removed STRICT: sections that only contained FAILED: 117. These sections existed because mempool mode (with LIMIT_HEAP) caused those generators to OOM before parsing conditions, producing a different error than consensus mode. With LIMIT_HEAP gone from MEMPOOL_MODE, both modes fail the same way. The STRICT: sections were documenting an accidental OOM difference, not a meaningful semantic difference between mempool and consensus validation.

Background

The STRICT:\n format in generator test files separates non-mempool expected output from mempool expected output when they differ. A missing STRICT: section means both modes produce the same result. The removed sections were only ever FAILED: 117 (heap exhausted) — an artifact of LIMIT_HEAP being in MEMPOOL_MODE, not a real behavioral difference worth preserving.

Copilot AI review requested due to automatic review settings March 11, 2026 19:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the consensus/runtime boundary explicit by rejecting the mempool-policy heap limiter (ConsensusFlags::LIMIT_HEAP) when calling the Rust-native generator (run_block_generator2), and updates various call sites to stop passing that flag.

Changes:

  • Add an early error return in run_block_generator2 when ConsensusFlags::LIMIT_HEAP is set.
  • Update developer tools to use ConsensusFlags::empty() as the non-mempool baseline flags.
  • Update tests/fuzz targets that previously passed MEMPOOL_MODE (transitively including LIMIT_HEAP) to strip LIMIT_HEAP before calling run_block_generator2.

Reviewed changes

Copilot reviewed 6 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
crates/chia-consensus/src/run_block_generator.rs Reject LIMIT_HEAP in run_block_generator2.
crates/chia-consensus/src/test_generators.rs Strip LIMIT_HEAP from test flag sets before calling run_block_generator2.
crates/chia-consensus/src/spendbundle_conditions.rs Strip LIMIT_HEAP from MEMPOOL_MODE in tests that call run_block_generator2.
crates/chia-consensus/src/get_puzzle_and_solution.rs Strip LIMIT_HEAP from MEMPOOL_MODE in tests that call run_block_generator2.
crates/chia-consensus/src/build_compressed_block.rs Strip LIMIT_HEAP from MEMPOOL_MODE when calling run_block_generator2 in tests.
crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs Stop passing LIMIT_HEAP to run_block_generator2 in fuzzing.
crates/chia-tools/src/bin/validate-blockchain-db.rs Remove unconditional LIMIT_HEAP from tool flags.
crates/chia-tools/src/bin/test-block-generators.rs Switch non-mempool flags from LIMIT_HEAP to empty.
crates/chia-tools/src/bin/analyze-chain.rs Switch non-mempool flags from LIMIT_HEAP to empty.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +278 to +279
for (flags, expected) in zip(&[ConsensusFlags::empty(), MEMPOOL_MODE], expected) {
let mut flags = *flags;
let mut flags = *flags - ConsensusFlags::LIMIT_HEAP;

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

This line strips LIMIT_HEAP from both iterations, including the MEMPOOL_MODE iteration. Later in the function (inside the UPDATE_TESTS block) the code checks flags.contains(MEMPOOL_MODE) to decide whether to write the STRICT output, but after subtracting LIMIT_HEAP that check will always be false because MEMPOOL_MODE includes LIMIT_HEAP. Consider tracking whether you're in the mempool-mode iteration separately (before removing LIMIT_HEAP), or adjusting the check to match the intended strict flag set.

Copilot uses AI. Check for mistakes.
Comment on lines +231 to +236
if flags.contains(ConsensusFlags::LIMIT_HEAP) {
return Err(ValidationErr(
NodePtr::NIL,
ErrorCode::GeneratorRuntimeError,
));
}

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

The new LIMIT_HEAP rejection is a behavioral change that isn't directly asserted anywhere (most call sites were updated to avoid passing the flag). To prevent regressions, add a focused unit test that calls run_block_generator2 with ConsensusFlags::LIMIT_HEAP and asserts it returns ErrorCode::GeneratorRuntimeError (or whatever error code you intend to expose for this misuse).

Copilot uses AI. Check for mistakes.
Comment thread crates/chia-tools/src/bin/analyze-chain.rs
Comment thread crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs
Comment thread crates/chia-consensus/src/run_block_generator.rs Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

NodePtr::NIL,
ErrorCode::GeneratorRuntimeError,
));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fuzz target always panics after LIMIT_HEAP rejection added

High Severity

The new LIMIT_HEAP rejection in run_block_generator2 breaks the run-generator fuzz target, which still passes ConsensusFlags::LIMIT_HEAP on line 24. After this change, run_block_generator2 always immediately returns an error for every fuzz input. When run_block_generator succeeds but run_block_generator2 always fails, the match falls through to the catch-all arm and panics. The fuzz target can never reach the (Ok, Ok) comparison branch, completely defeating its purpose of cross-checking the two generator implementations.

Fix in Cursor Fix in Web

@richardkiss
richardkiss marked this pull request as draft March 11, 2026 23:00
richardkiss added a commit to richardkiss/chia-blockchain that referenced this pull request Mar 11, 2026
…ting

Uses chia_rs@8bcf2006 (Chia-Network/chia_rs#1396) which makes
run_block_generator2 return an error if LIMIT_HEAP is set in flags.

This PR exists to see which chia-blockchain tests break when LIMIT_HEAP
is rejected by the consensus code path. Any test failures indicate sites
that pass MEMPOOL_MODE (which includes LIMIT_HEAP) to run_block_generator2
— a separation-of-concerns bug.

Adds Rust toolchain setup to the install action so poetry can build
chia_rs from source.

Made-with: Cursor
@coveralls-official

coveralls-official Bot commented Mar 12, 2026

Copy link
Copy Markdown

Pull Request Test Coverage Report for Build 23262629980

Details

  • 22 of 23 (95.65%) changed or added relevant lines in 9 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.002%) to 80.943%

Changes Missing Coverage Covered Lines Changed/Added Lines %
wheel/src/api.rs 5 6 83.33%
Totals Coverage Status
Change from base Build 23252353802: -0.002%
Covered Lines: 14573
Relevant Lines: 18004

💛 - Coveralls

LIMIT_HEAP is a mempool-policy flag that caps the CLVM allocator to
500 MB. It has no place in run_block_generator2, which is a consensus
code path. The mempool no longer uses run_block_generator2 (confirmed
by Arvid, 2026-03-11), so accepting this flag there only muddies the
separation of concerns.

Changes:
- run_block_generator2 now returns GeneratorRuntimeError immediately
  if ConsensusFlags::LIMIT_HEAP is set in flags.
- Developer tools (test-block-generators, analyze-chain,
  validate-blockchain-db): non-mempool baseline flags changed from
  LIMIT_HEAP to ConsensusFlags::empty(). These tools run the
  Rust-native generator and never needed the heap cap.
- Tests and fuzz target: strip LIMIT_HEAP from MEMPOOL_MODE before
  calling run_block_generator2. Test results are unaffected because
  the test generators don't approach the heap limit.

Made-with: Cursor
These generators use >500 MB of CLVM allocator space when run in
MEMPOOL mode with LIMIT_HEAP set, so they previously returned error
117 (GeneratorRuntimeError / heap exhausted) in strict mode.

Now that run_block_generator2 rejects LIMIT_HEAP and we strip it from
test calls, those generators run to completion and return their actual
condition errors (e.g. 14 = AssertHeightAbsoluteFailed).

Changes:
- spendbundle_conditions.rs: replace get_conditions_from_spendbundle
  (which hardcodes MEMPOOL_MODE including LIMIT_HEAP) with a direct
  run_spendbundle call that strips LIMIT_HEAP, so both code paths
  use the same heap configuration and can be compared.
- test_generators.rs: introduce is_mempool boolean so UPDATE_TESTS
  detection still works correctly after LIMIT_HEAP is stripped from
  the flags variable. Stripping LIMIT_HEAP made flags.contains(MEMPOOL_MODE)
  return false, causing UPDATE_TESTS to write the mempool output as
  a second non-strict line instead of a STRICT section.
- generator-tests/*.txt: remove STRICT sections whose only content was
  FAILED: 117 (heap exhausted). Without LIMIT_HEAP, both mempool and
  non-mempool passes produce the same condition error, so no STRICT
  section is needed.

Made-with: Cursor
validate_clvm_and_signature hardcodes make_allocator(LIMIT_HEAP)
directly, so LIMIT_HEAP in MEMPOOL_MODE was never doing anything for
the real mempool path. Removing it from the constant eliminates the
need to strip it manually at every run_block_generator2 call site.

The run_block_generator2 guard (return error if LIMIT_HEAP is set)
stays as defense-in-depth.

Made-with: Cursor
LIMIT_HEAP was introduced as a flag to limit allocator heap size on
mempool paths, but all mempool callers (spendbundle_validation,
spendbundle_conditions, api.rs) already hardcode it explicitly rather
than threading it through as a parameter. The flag was never a true
caller-visible switch.

Changes:
- make_allocator() no longer takes a flags argument; it always returns
  Allocator::new_limited(500_000_000), which is the correct size for
  all spend-bundle / mempool paths
- Block-validation functions (run_block_generator, run_block_generator2,
  additions_and_removals, get_coinspends_*) now create their own
  Allocator::new_limited(u32::MAX) directly, making the intent clear
- The LIMIT_HEAP guard in run_block_generator2 is removed; it was only
  needed to prevent the flag from accidentally imposing a 500 MB cap on
  block validation, which can no longer happen
- LIMIT_HEAP is exported to Python as 0 for backwards compatibility so
  that existing code doing `flags & ~LIMIT_HEAP` or `flags | LIMIT_HEAP`
  is a no-op. The flag definition remains in ConsensusFlags (with value
  0x0004) only because clvmr requires bit-for-bit parity.
- MEMPOOL_MODE still explicitly excludes the LIMIT_HEAP bit to prevent
  clvmr-level heap enforcement from leaking into block-validation callers
  that pass MEMPOOL_MODE to run_block_generator2

Made-with: Cursor
Made-with: Cursor
Three places used make_allocator() (500MB) where they should have used
an unlimited allocator:

1. spendbundle_conditions tests: convert_block_to_bundle and the a1
   allocator in run_generator were comparing against run_block_generator2
   (which uses unlimited heap). Large stress-test generators exceeded
   500MB, causing the two to diverge and the test to panic.

2. test_generators::run_generator: the per-spend puzzle-runnable check
   used 500MB, causing assert!(runnable) to fail for many-large-ints
   generators whose individual puzzles need more than 500MB.

3. run_chia_program: this generic Python-callable CLVM runner should not
   impose a 500MB cap. Generator performance tests expect programs to
   hit the cost limit, not an OOM, so they failed with "expected cost
   to be exceeded".

In all three cases the correct allocator is unlimited, consistent with
the behavior before make_allocator() was changed to always return 500MB.
The 500MB cap belongs exclusively to the mempool spend-bundle paths.

Made-with: Cursor
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