Conversation
Merge everything that is in WIP
…#41) Motivation ---- A malicious or buggy L1 transaction producer could declare duplicate resource_ids in its access metadata. The parent branch didn't dedup anywhere - neither the worker's extract_resources nor the guest's Payload::decode checked. This let two issues through: - Scheduler corruption. The scheduler links each declared access into the resource's dependency chain. Duplicates link the same tx into the same chain twice - risk of livelock (the second access waits on the first conceptually-same access), broken chain-linking invariants, or silent double-execution. - Journal bloat. The guest commits a separate InputResourceCommitment/OutputResourceCommitment per declared access, so duplicates produce multiple commitments for the same resource - wasted proof bytes and potentially divergent post-state. Additionally, AccessMetadata was two structurally-identical types maintained in parallel - host-owned (vprogs_core_types::AccessMetadata) and guest-borrowed (vprogs_zk_abi::transaction_processor::AccessMetadata<'a>). The guest's Payload::decode allocated a Vec per parse, decoding entries one at a time. Changes ---- **Single shared AccessMetadata** - Deleted the duplicate guest-side AccessMetadata<'a> view (zk/abi/src/transaction_processor/input/access_metadata.rs). Both host and guest now use vprogs_core_types::AccessMetadata. - Made the type repr(C) so its in-memory layout matches the wire byte layout exactly (33 bytes, no padding). WIRE_SIZE is now size_of::<Self>(). - Added TryFromBytes, IntoBytes, Immutable, KnownLayout derives - a byte slice can be re-interpreted as &[AccessMetadata] zero-copy via <[AccessMetadata]>::try_ref_from_bytes. - Added matching zerocopy derives on ResourceId (FromBytes, IntoBytes, Immutable, KnownLayout) and AccessType (TryFromBytes, IntoBytes, Immutable, KnownLayout) so they can be fields in the zerocopy struct. **Wire format invariant + dedup** - Access metadata is now strictly-ascending by resource_id on the wire. Verified inline during a single pass over the slice; rejects duplicates and unsorted input as the same condition (prev >= curr). - New: AccessMetadata::decode_slice(buf) -> &[Self] - zero-copy slice cast + sort verification (guest path). No Vec allocation. - New: AccessMetadata::decode_vec(buf) -> Vec<Self> - slice cast then .to_vec() (host path). - Removed the per-entry AccessMetadata::decode and AccessMetadata::encode inherent methods. Encoding now uses IntoBytes::as_bytes from the derive; decoding goes through the slice path. **Guest-side simplifications** - Payload<'a> now holds access_metadata: &'a [AccessMetadata] instead of Vec<AccessMetadata<'a>> - no allocation in the parse hot path. - Resource<'a> now holds access_metadata: &'a AccessMetadata instead of a by-value AccessMetadata<'a>. Lifetime works out cleanly because items live in the wire buffer (via the slice cast), not in a Vec owned by Transaction. - Resource::decode takes &'a AccessMetadata; Inputs::decode passes meta (reference) instead of *meta (by-value copy). **Worker / scheduler integration** - Inlined the extract_resources helper at its single call site in node/framework/src/worker.rs. Now: AccessMetadata::decode_vec(&mut tx.payload.as_slice()).unwrap_or_default(). - Malformed, duplicate, or unsorted access metadata → empty list → tx scheduled with no resource dependencies; the original payload still flows to the prover so it can attest to invalidity. **Cleanup** - Removed the unused From<&'a [u8; 32]> for &'a ResourceId impl (the unsafe pointer cast). Consumers were the deleted guest-side AccessMetadata<'a>::resource_id; no longer needed since the unified type holds ResourceId directly. - L1TransactionExt::for_l2_test (in the risc0 test-suite) sorts access_metadata internally before encoding, and uses IntoBytes::as_bytes for per-entry writes.
biryukovmaxim
approved these changes
May 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.