Skip to content

[vm] Fix and test zero-copy BCS serialization (supersedes #20179, builds on #18535)#20190

Draft
gregnazario wants to merge 12 commits into
mainfrom
greg/bcs-zero-copy-serialization-6520
Draft

[vm] Fix and test zero-copy BCS serialization (supersedes #20179, builds on #18535)#20190
gregnazario wants to merge 12 commits into
mainfrom
greg/bcs-zero-copy-serialization-6520

Conversation

@gregnazario

Copy link
Copy Markdown
Contributor

Description

Recreates the work from #20179 (which was accidentally closed after its branch was overwritten by unrelated commits). This carries #18535's zero-copy BCS serialization commits (serialize_ref / serialized_size_ref, resolving #14175) plus the fixups below. Because #18535 comes from a fork, its commits cannot be used as a PR base in this repo, so they are included here and this PR targets main.

Fixups on top of #18535:

  1. Fix compile error (blocker). The new SerializationReadyValue<MoveTypeLayout, Container> impl passed the Arc<MoveStructLayout> from L::Struct directly where a &MoveStructLayout is expected, so move-vm-types did not compile. Added .as_ref() to match the existing Value serializer. (Not caught on [vm] Avoid deep copies in BCS serialization by using serialize_ref (#14175) #18535 because CI there is gated behind a failing permission-check, so all Rust build/test jobs were skipped.)

  2. Restore the indexed-ref invariant guard. IndexedRef::read_ref enforces check_valid_for_indexed_ref, which rejects a stale/malformed indexed reference pointing at a nested container or another reference. The zero-copy path dropped this guard; it is restored so serialize_ref has the same safety behavior as the copy-based path.

  3. Match read_ref depth accounting for indexed refs. IndexedRef::read_ref copies the referenced element at depth + 1, so the copy-based path enforces the nesting-depth limit one level deeper than the reference. The indexed-ref serialization arm now visits the element at self.depth + 1, so bcs::to_bytes / serialized_size reject at the same depth boundary as before (relevant for indexed refs to closures, whose captured arguments add nesting).

  4. Add differential and depth-parity tests. Since the correctness requirement is that the reference path produces byte-identical output to the copy-based path (BCS output is consensus-sensitive), added tests asserting parity of bytes, size, and depth-limit behavior.

Disposition of automated review comments (from #20179)

  • Depth-limit parity for indexed refs (closures): addressed by fixup (3) and pinned by serialize_ref_indexed_closure_enforces_depth_one_level_deeper.
  • Failure-class change (read_ref errors now map to NoneNFE_BCS_SERIALIZATION_FAILURE): intentional and consistent with the existing owned serialize path in [vm] Avoid deep copies in BCS serialization by using serialize_ref (#14175) #18535, which already maps every serializer error (including VM_MAX_VALUE_DEPTH_REACHED and invariant/tag failures) to None. Routing reference-structural failures through the same fail-closed abort keeps the reference and owned paths consistent; these are invariant violations that should not occur under a correct verifier. Left as-is by design.

How Has This Been Tested?

New tests in third_party/move/move-vm/types/src/values/serialization_tests.rs assert serialize_ref / serialized_size_ref match serialize / serialized_size across scalars, specialized/nested/general vectors, structs, nested structs, enum variants, signers (master/permissioned, legacy/non-legacy), delayed fields, indexed field/element refs, and depth-limit enforcement (incl. the indexed-ref-to-closure +1 parity).

cargo test -p move-vm-types --lib passes. cargo +nightly fmt and cargo clippy -p move-vm-types --lib --tests are clean for the changed code. aptos-move-stdlib (the natives caller) compiles.

Type of Change

  • Bug fix
  • Performance improvement
  • Tests

Which Components or Systems Does This Change Impact?

  • Move/Aptos Virtual Machine

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I tested both happy and unhappy path of the functionality
Open in Web Open in Cursor 

raushan728 and others added 12 commits July 2, 2026 16:02
The new SerializationReadyValue<MoveTypeLayout, Container> impl passed
the Arc<MoveStructLayout> from L::Struct directly where a
&MoveStructLayout is required, so move-vm-types failed to compile. Use
.as_ref() to match the existing Value serializer.

Co-authored-by: Greg Nazario <greg@gnazar.io>
IndexedRef::read_ref enforces check_valid_for_indexed_ref, which rejects
a stale/malformed indexed reference that points at a nested container or
another reference. The zero-copy serialization path dropped this guard;
restore it so serialize_ref has the same safety behavior as the
copy-based path.

Co-authored-by: Greg Nazario <greg@gnazar.io>
Verify serialize_ref / serialized_size_ref produce byte-identical output
and identical sizes to the copy-based serialize / serialized_size across
scalars, specialized and nested vectors, structs, enum variants, signers
(legacy and non-legacy), delayed fields, indexed field/element refs, and
depth-limit enforcement.

Co-authored-by: Greg Nazario <greg@gnazar.io>
IndexedRef::read_ref copies the referenced element at depth + 1, so the
copy-based path enforces the nesting-depth limit one level deeper than
the reference. Serialize the indexed element at self.depth + 1 so the
zero-copy path rejects at the same depth boundary (relevant for indexed
refs to closures, whose captured arguments add nesting).

Co-authored-by: Greg Nazario <greg@gnazar.io>
Add a test asserting serialize_ref of an indexed reference to a closure
enforces the depth limit exactly one level deeper than serializing the
owned closure, matching IndexedRef::read_ref's copy_value(depth + 1).

Co-authored-by: Greg Nazario <greg@gnazar.io>
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.

3 participants