Conversation
There was a problem hiding this comment.
Pull request overview
Adds bounded vector parsing to the Streamable derive to fail early on oversized protocol payloads, and applies explicit max-length limits to selected full node and wallet message types.
Changes:
- Introduces
#[chia(max_length = N)]onStreamable-derived fields to boundVec<T>/Option<Vec<T>>parsing. - Adds
parse_vec_with_max_length()tochia-traitsplus unit tests for bounded parsing behavior. - Annotates various protocol message vector fields with concrete
max_lengthlimits to prevent oversized allocations/validation work.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| crates/chia_streamable_macro/src/lib.rs | Extends the derive macro to parse #[chia(max_length = ...)] and generate bounded parsing for supported vector fields. |
| crates/chia-traits/src/streamable.rs | Adds parse_vec_with_max_length() and tests validating bounded vector/optional-vector parsing failures. |
| crates/chia-protocol/src/wallet_protocol.rs | Applies max_length bounds to multiple wallet protocol message vectors. |
| crates/chia-protocol/src/full_node_protocol.rs | Applies max_length bound to RespondBlocks.blocks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Ok(()) | ||
| } | ||
| fn parse<const TRUSTED: bool>(input: &mut std::io::Cursor<&[u8]>) -> #crate_name::chia_error::Result<Self> { | ||
| Ok(Self { #( #fnames: <#ftypes as #crate_name::Streamable>::parse::<TRUSTED>(input)?, )* }) | ||
| Ok(Self { #( #fnames: #parse_exprs, )* }) | ||
| } |
There was a problem hiding this comment.
The new max_length support is enforced only during parse(). The derived stream() implementation still allows serializing vectors longer than max_length, which can let the code construct and emit protocol messages that peers (or future local parsing) will reject. Consider also generating a length check in stream() for bounded fields (returning Error::SequenceTooLarge) to keep the invariant consistent in both directions.
Coverage Report for CI Build 25061556133Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Coverage increased (+0.03%) to 80.732%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
2f5d61a to
652502a
Compare
|
|
||
| #[streamable(message)] | ||
| pub struct RequestPuzzleState { | ||
| #[chia(max_length = 32700)] |
There was a problem hiding this comment.
If register_for_coin_updates and register_for_ph_updates can allow 1.6 million, so should these. This limit is insufficient for wallets like Sage to function. Many wallets have over this many puzzle hashes to subscribe to (each one is a wallet address).
There was a problem hiding this comment.
the 1.6 million is derived from the 50 MiB message limit, which will not fit more 32 byte hashes.
Does Sage subscribe to all coins in a single request? This number was picked based on CoinStore.MAX_PUZZLE_HASH_BATCH_SIZE, see: https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/full_node_api.py#L1949
The limit is defined here: https://github.com/Chia-Network/chia-blockchain/blob/main/chia/full_node/coin_store.py#L444
It seems like we silently drop any item past this limit.
|
|
||
| #[streamable(message)] | ||
| pub struct RespondPuzzleState { | ||
| #[chia(max_length = 32700)] |
| height: u32, | ||
| header_hash: Bytes32, | ||
| is_finished: bool, | ||
| #[chia(max_length = 32700)] |
|
|
||
| #[streamable(message)] | ||
| pub struct RequestCoinState { | ||
| #[chia(max_length = 32700)] |
|
|
||
| #[streamable(message)] | ||
| pub struct RespondCoinState { | ||
| #[chia(max_length = 32700)] |
| pub struct RespondCoinState { | ||
| #[chia(max_length = 32700)] | ||
| coin_ids: Vec<Bytes32>, | ||
| #[chia(max_length = 32700)] |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8c79e84. Configure here.
| height: u32, | ||
| header_hash: Bytes32, | ||
| is_finished: bool, | ||
| #[chia(max_length = 500000)] |
There was a problem hiding this comment.
Inconsistent puzzle hash limits between request and response
Medium Severity
RequestPuzzleState accepts up to 35000 puzzle hashes while RespondPuzzleState only allows 33000. Since the response echoes back the requested puzzle hashes, a peer accepting a request near the upper limit cannot generate a response that successfully parses on the other side, producing an unrecoverable protocol failure for otherwise valid requests.
Reviewed by Cursor Bugbot for commit 8c79e84. Configure here.
| header_hash: Bytes32, | ||
| #[chia(max_length = 30000)] | ||
| coins: Vec<(Bytes32, Vec<Coin>)>, | ||
| #[chia(max_length = 30000)] |
There was a problem hiding this comment.
Vec size limits may break existing wallet clients
Medium Severity
The 30000/35000 caps on puzzle_hashes/coin_names in RequestRemovals, RequestAdditions, RequestPuzzleState, RespondPuzzleState, and CoinStateUpdate are far below the 1_600_000 used for RegisterForPhUpdates/RegisterForCoinUpdates. Wallets that currently operate with more puzzle hashes per request (each address counts as a hash) will see legitimate messages rejected with SequenceTooLarge after this change.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 8c79e84. Configure here.


This adds an option to the streamable macro, to specify a limit on vectors in streamable types. It can short cut some validation by failing earlier for messages that are invalid.
The second commit employs the new option to set appropriate limits for some message types.
Note
Medium Risk
Changes deserialization behavior for
Streamablestructs by enforcing new per-field vector length limits, which can cause previously-accepted oversized network messages to be rejected. Risk is moderate because it touches core protocol parsing but is additive and guarded by explicit#[chia(max_length = ...)]annotations.Overview
Adds a new
#[chia(max_length = N)]field attribute to theStreamablederive macro to enforce bounded parsing forVec<T>andOption<Vec<T>>, failing early withError::SequenceTooLarge.Refactors vector parsing in
chia-traitsto route through a sharedparse_vec_with_max_lengthhelper and adds tests covering boundedVec/Option<Vec>behavior.Applies explicit maximum lengths to several full node and wallet protocol message fields (e.g., block/header responses, additions/removals queries, subscription update payloads, mempool notifications) to cap decoded list sizes.
Reviewed by Cursor Bugbot for commit 8c79e84. Bugbot is set up for automated code reviews on this repo. Configure here.