Conversation
| @@ -91,9 +91,6 @@ impl<T: Transaction> BlockGasLimitProcessor<T> { | |||
| } else { | |||
There was a problem hiding this comment.
Medium: this still enables hot-state accumulation under the existing add_block_limit_outcome_onchain gate even though this PR intentionally changes how to_make_hot is derived. Once hotness_in_epilogue is turned on, mixed-version validators will compute different promotion sets for the same block and serialize different BlockEpiloguePayload::V2 contents, so this rollout is not safe without a separate gate for the new derivation.
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 2 issues that need to be addressed from previous scan.
Open findings:
- Rollout gating for the new hot-state derivation remains unresolved
- Sequential BCS-fallback discards can still affect hot-state promotion
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 2 issues that need to be addressed from previous scan.
Open findings:
- Mixed-version rollout divergence in hot-state derivation
- Discarded transactions still influence hot-state promotions
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 2 issues that need to be addressed from previous scan.
Open findings:
- Mixed-version rollout divergence in hot-state derivation
- Discarded transactions still influence hot-state promotions
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 4 issues that need to be addressed from previous scan.
Open findings:
- Mixed-version rollout divergence in hot-state derivation
- Sequential BCS-fallback discards can still affect hot-state promotion
- HashSet iteration can make hot-state promotions nondeterministic
- Discarded transactions still influence hot-state promotions
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 4 issues that need to be addressed from previous scan.
Open findings:
- Rollout gating for the new hot-state derivation remains unresolved
- HashSet iteration can make hot-state promotions nondeterministic
- Discarded transactions still influence hot-state promotions
- Nested respawned sessions still do not contribute data reads to the final hot-state read set
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 5 issues that need to be addressed from previous scan.
Open findings:
- Mixed-version rollout divergence in hot-state derivation
- Sequential BCS-fallback discards can still affect hot-state promotion
- HashSet iteration can make hot-state promotions nondeterministic
- Discarded transactions still influence hot-state promotions
- Respawned-session reads are still missing from the recorded hot-state read set
Sent by Cursor Automation: Security Review Bot
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 4 issues that need to be addressed from previous scan.
Open findings:
- Mixed-version rollout divergence in hot-state derivation
- Sequential BCS-fallback discards can still affect hot-state promotion
- Discarded transactions still influence hot-state promotions
- Respawned-session reads are still missing from the recorded hot-state read set
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Stale comment
Aptos Security Bugbot has reviewed your changes, there are still 4 issues that need to be addressed from previous scan.
Open findings:
- Rollout gating for the new hot-state derivation remains unresolved
- HashSet iteration can make hot-state promotions nondeterministic
- Discarded transactions still influence hot-state promotions
- Nested respawned sessions still do not contribute data reads to the final hot-state read set
Sent by Cursor Automation: Security Review Bot
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Aptos Security Bugbot has reviewed your changes, there are still 8 issues that need to be addressed from previous scan.
Open findings:
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Aptos Security Bugbot has reviewed your changes, there are still 8 issues that need to be addressed from previous scan.
Open findings:
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
Sent by Cursor Automation: Security Review Bot
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Aptos Security Bugbot has reviewed your changes, there are still 8 issues that need to be addressed from previous scan.
Open findings:
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
Sent by Cursor Automation: Security Review Bot
There was a problem hiding this comment.
Aptos Security Bugbot has reviewed your changes, there are still 8 issues that need to be addressed from previous scan.
Open findings:
- Rollout gating mismatch for new hot-state derivation
- Retry outputs still feed hot-state promotion
- BCS-fallback discards still count toward block-limit accounting
- Hotness can affect the checkpoint hash before epilogue serialization is enabled
- Pre-rollout read recording causes avoidable memory and CPU amplification
- Internal config lookups contaminate the transaction read set
- Full read-set retention defeats the promotion cap
- Eager module verification still misses transitive module reads
Sent by Cursor Automation: Security Review Bot
Hot state promotions (the `to_make_hot` set in the block epilogue) are part of consensus, so their inputs must be deterministic. This reworks how that set is derived. ## Why the old derivation was problematic Promotions were fed from BlockSTM's read/write summary, which exists for conflict detection, not for this: - **Path-dependent.** The summary differs between parallel and sequential execution (e.g. aggregator v1 reads served by delta resolution are dropped in parallel but kept in sequential), so the same block could promote different keys depending on how it happened to execute — a divergence in a consensus-agreed artifact. - **Coupled to gas config.** It was only populated inside the `conflict_penalty_window` branch, so promotions silently depended on an unrelated block-gas knob. - **Incomplete write exclusion.** Its write side misses in-place delayed-field rewrites, aggregator v1 writes/deltas and module writes, so a key the block writes could still be promoted by the epilogue even though the write already makes it hot. ## Approach Record the read set at the VM boundary, where it is a pure function of the transaction and the pre-state — identical across parallel and sequential execution and independent of gas config: - `StorageAdapter` records resource, resource-group, table-item, aggregator v1 and config reads. Respawned sessions share one recorder so all of a transaction's sessions accumulate into a single set. - `ReadRecordingCodeStorage` wraps the code storage and records module fetches. It sits above the global module cache, so a module is recorded whether served from that cache, the per-block cache or storage. - Written keys are enumerated directly from the change set, covering every write kind the conflict summary missed. ## Worth calling out The new set intentionally differs from the old one — e.g. `exists<T>` now loads the resource and counts as a read — so it ships behind the existing hotness feature flag, and the mixed-version forge suites keep that flag off to avoid old/new nodes disagreeing on transaction output. Adds a per-block promotions histogram and tests covering sequential/parallel parity, each read and write kind, and discard handling. Follow-ups: read-kind/hotness tagging and an on-chain, byte-based promotion cap. ## Performance The recording runs unconditionally, so its per-transaction cost has to stay small. The accumulating sets are keyed by `StateKey`, which already carries a precomputed 32-byte hash, so they use a fast (FxHash, via the maintained `rustc-hash`) hasher instead of the default SipHash. At extraction the read set is moved out rather than cloned, and module reads are yielded as an iterator. In the block hot-state accumulator, `to_make_hot` is a hash set ordered once when the promotion cap is applied, so a read no longer pays a comparison that deserializes the key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Aptos Security Bugbot has reviewed your changes, there are still 8 issues that need to be addressed from previous scan.
Open findings:
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
- #19989 (comment)
Sent by Cursor Automation: Security Review Bot
| let code_storage = ReadRecordingCodeStorage::new(view); | ||
| match self.vm.execute_single_transaction( | ||
| txn, | ||
| &resolver, | ||
| &code_storage, | ||
| &log_context, | ||
| auxiliary_info, | ||
| ) { | ||
| Ok((vm_status, vm_output)) => { | ||
| if vm_output.status().is_discarded() { | ||
| // Discarded transactions commit no state changes, so their reads must not feed | ||
| // hot-state promotion. Only carry the read set for outputs that can commit. | ||
| let read_set = if vm_output.status().is_discarded() { | ||
| speculative_trace!( | ||
| &log_context, | ||
| format!("Transaction discarded, status: {:?}", vm_status), | ||
| ); | ||
| } | ||
| UnorderedReadSet::default() | ||
| } else { | ||
| let mut keys = resolver.take_recorded_reads(); | ||
| keys.extend(code_storage.into_recorded_reads()); | ||
| UnorderedReadSet::new(keys) |
There was a problem hiding this comment.
Low: staged init_module execution can still bypass the new module-read recorder.
This wrapper now builds the carried read set from resolver.take_recorded_reads() plus the module keys seen through this outer ReadRecordingCodeStorage. Module-publish transactions later switch to a bare StagingModuleStorage for staged verification and init_module execution (move_vm_ext/session/user_transaction_sessions/user.rs), so any existing dependency module first loaded from that staged storage never reaches either recorder. A publisher can therefore drive init_module through specific dependency modules while keeping them out of to_make_hot, weakening the hot-state signal this feature is trying to persist.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
✅ Forge suite
|
✅ Forge suite
|
✅ Forge suite
|


Hot state promotions (the
to_make_hotset in the block epilogue) arepart of consensus, so their inputs must be deterministic. This reworks how
that set is derived.
Why the old derivation was problematic
Promotions were fed from BlockSTM's read/write summary, which exists for
conflict detection, not for this:
execution (e.g. aggregator v1 reads served by delta resolution are
dropped in parallel but kept in sequential), so the same block could
promote different keys depending on how it happened to execute — a
divergence in a consensus-agreed artifact.
conflict_penalty_windowbranch, so promotions silently depended on anunrelated block-gas knob.
delayed-field rewrites, aggregator v1 writes/deltas and module writes, so
a key the block writes could still be promoted by the epilogue even
though the write already makes it hot.
Approach
Record the read set at the VM boundary, where it is a pure function of the
transaction and the pre-state — identical across parallel and sequential
execution and independent of gas config:
StorageAdapterrecords resource, resource-group, table-item, aggregatorv1 and config reads. Respawned sessions share one recorder so all of a
transaction's sessions accumulate into a single set.
ReadRecordingCodeStoragewraps the code storage and records modulefetches. It sits above the global module cache, so a module is recorded
whether served from that cache, the per-block cache or storage.
write kind the conflict summary missed.
Worth calling out
The new set intentionally differs from the old one — e.g.
exists<T>nowloads the resource and counts as a read — so it ships behind the existing
hotness feature flag, and the mixed-version forge suites keep that flag off
to avoid old/new nodes disagreeing on transaction output. Adds a per-block
promotions histogram and tests covering sequential/parallel parity, each
read and write kind, and discard handling. Follow-ups: read-kind/hotness
tagging and an on-chain, byte-based promotion cap.
Performance
The recording runs unconditionally, so its per-transaction cost has to stay
small. The accumulating sets are keyed by
StateKey, which already carries aprecomputed 32-byte hash, so they use a fast (FxHash, via the maintained
rustc-hash) hasher instead of the default SipHash. At extraction the readset is moved out rather than cloned, and module reads are yielded as an
iterator. In the block hot-state accumulator,
to_make_hotis a hash setordered once when the promotion cap is applied, so a read no longer pays a
comparison that deserializes the key.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Note
Medium Risk
Medium risk: changes consensus-relevant block epilogue inputs and VM execution path (always-on read recording), though behavior is behind the hotness feature flag and mixed-version forge tests disable epilogue hotness.
Overview
Hot state promotion (
to_make_hotin the block epilogue) no longer derives from BlockSTM’s conflict read/write summary. The VM now records observed reads and the output carries explicit write keys, and the block executor feeds those intoBlockHotStateOpAccumulatorin commit order.Read recording:
StorageAdapteraccumulatesStateKeys for resource, resource-group, table, aggregator v1, and config accesses into a sharedReadRecorder(respawned sessions inherit the parent recorder).ReadRecordingCodeStoragewraps code storage and deduplicates module fetches before turning them into moduleStateKeys at extraction. Per-txn sets are merged intoUnorderedReadSetonAptosTransactionOutput; discarded txns get an empty read set.Write enumeration:
BeforeMaterializationOutputaddsstorage_keys_read/storage_keys_written; writes include resources, modules, aggregator v1 writes/deltas (not just conflict-summary keys). Hot-state accumulation is gated viais_hot_state_accumulation_enabledandaccumulate_hot_state_rwinstead of piggybacking onconflict_penalty_windowfee accumulation.Accumulator & ops:
to_make_hotuses a hash set with ordering applied once when capping promotions; addsHOT_STATE_PROMOTIONS_PER_BLOCKhistogram. Forge compat/framework upgrade suites disableHOTNESS_IN_EPILOGUEfor mixed-version safety.Tests: Large e2e
hot_statesuite (sequential/parallel parity, read/write kinds, discards) plus unit tests for read recording and shared recorders.Reviewed by Cursor Bugbot for commit 92c60c6. Bugbot is set up for automated code reviews on this repo. Configure here.