Skip to content

[mono-move] Add aptos-vm-v2 transaction layer#20181

Draft
vgao1996 wants to merge 1 commit into
mainfrom
mono-move-aptos-vm-v2
Draft

[mono-move] Add aptos-vm-v2 transaction layer#20181
vgao1996 wants to merge 1 commit into
mainfrom
mono-move-aptos-vm-v2

Conversation

@vgao1996

@vgao1996 vgao1996 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Description

Reimplements the AptosVM transaction-execution layer on the MonoMove VM, as a new crate mono-move/aptos-vm-v2: prologue → entry-function payload → epilogue → write-set publication, producing a VMOutput, executed single-threaded.

The core structural change from the legacy VM: one MonoMove session with checkpoints replaces the staged prologue/user/epilogue sessions, respawned sessions, and per-stage change-set squashing. A failed payload rolls the heap and read-write set back to the post-prologue checkpoint, and writes are drained exactly once at the end — including resource-group members merged back into their group slot.

Scope (deliberately minimal)

  • Entry functions only; only the versioned validation path (versioned_prologue/versioned_epilogue).
  • Resource groups supported end to end (split on read, merge-back on publication).
  • Placeholder gas: MonoMove's uncalibrated block costs against the txn gas limit; fee amounts intentionally diverge from v1.
  • Everything else is an inline TODO(<group>) placeholder: scripts, multisig, module publishing, keyless/AA, orderless txns, IO/storage fees + refunds, StateValueMetadata, abort locations, Block-STM.
  • Write-set publication is a stopgap behind the runtime's new ResourceReadWriteSet::entries() drain API, so the real publication workstream can replace it from inside the runtime without touching this crate.

Interfaces

  • Per-txn: AptosVMv2::execute_user_transaction(&StateView, &SignedTransaction) -> (VMStatus, VMOutput) (TODO: move to ExecutorView/ResourceGroupView for Block-STM).
  • Block: AptosVMv2BlockExecutor implements VMBlockExecutor with a sequential loop over an output-overlay view — the eventual node-level swap point.

Runtime additions

ResourceReadWriteSet::entries() (drain seam) and InterpreterContext::{read_write_set, reset_gas, gas_balance, load_function} so a driver can run several functions in one transaction sharing heap/read-write set/extensions.

How Has This Been Tested?

aptos-vm-v2/tests/e2e.rs — differential tests against the legacy VM on FakeExecutor head-genesis state:

  • p2p transfer (FA path): identical write-key sets; byte-equal writes and events except the two fee-embedding slots (fee payer's primary store group, APT supply at 0xa) and fee events.
  • Insufficient-balance abort: payload rollback + failure epilogue; abort codes match; fee still charged, sequence number still bumped.
  • Two dependent transfers as a sequential block through the VMBlockExecutor wrapper (second prologue observes the first's sequence bump via the overlay).

Also verified: mono-move runtime and differential testsuites pass with the runtime edits; fmt/clippy clean. (Note: mono-move-testsuite's aptos_experimental_on_mono_move unit test stack-overflows in debug builds on the base commit already — pre-existing, unrelated.)

🤖 Generated with Claude Code

Reimplements the AptosVM transaction-execution layer on the MonoMove VM:
prologue -> entry-function payload -> epilogue -> write-set publication,
producing a VMOutput, executed single-threaded. One MonoMove session with
checkpoints replaces the legacy VM's staged sessions and change-set
squashing: a failed payload rolls the heap and read-write set back to the
post-prologue state, and writes (including resource-group merge-back) are
drained exactly once at the end.

Scope is deliberately minimal: only the versioned validation path, entry
functions only, placeholder gas. Scripts, multisig, publishing, keyless/AA,
orderless, IO/storage fees, and Block-STM integration are inline TODOs;
write-set publication is a stopgap behind the runtime's new drain API so the
real publication workstream can replace it.

Differential tests against the legacy VM on FakeExecutor genesis: a p2p
transfer (byte-equal writes except the two fee-embedding slots), an
insufficient-balance abort (rollback + failure epilogue, matching abort
codes), and a two-transaction sequential block through the VMBlockExecutor
wrapper.

Runtime additions: ResourceReadWriteSet::entries() drain API and
InterpreterContext accessors (read_write_set, reset_gas, gas_balance,
load_function) so a driver can run several functions in one transaction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

mono-move benchmark gate

1 regression(s) beyond ±3% noise band

5 ok · 1 improved · 0 new · 0 absent (threshold T = ±3%, criterion mean CI vs main)

Benchmark mean Δ 95% CI median (main → PR) Verdict
fib/mono +1.0% [+0.5%, +1.5%] 8.25ms → 8.32ms ok
nested_loop/mono -0.1% [-0.2%, -0.1%] 13.79ms → 13.79ms ok
merge_sort/mono +6.5% [+6.0%, +6.9%] 1.26ms → 1.35ms regression
bst/mono -3.4% [-3.7%, -3.1%] 3.77ms → 3.66ms improved
match_sum/mono -1.6% [-2.3%, -0.7%] 28.73ms → 27.99ms ok
int_arith_loop/mono_u64 +3.0% [+2.4%, +3.7%] 378.74µs → 395.11µs ok
int_arith_loop/mono_i64 +1.4% [+0.9%, +2.0%] 576.92µs → 589.87µs ok

Improvements are not failures. main rebaselines on merge, so the next PR compares against the faster code automatically.

provider: &StateViewResourceProvider<'_, '_, '_, S>,
) -> Result<BTreeMap<StateKey, AbstractResourceWriteOp>> {
let mut writes = BTreeMap::new();
let mut group_ops: HashMap<StateKey, BTreeMap<StructTag, MemberOp>> = HashMap::new();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I hope we can avoid all these allocations of state keys and struct tags - keep using the interned representation. And simply charge gas for sum of state keys sizes and write sizes so we do not even have a problem of non-deterministic iteration

@vgao1996 vgao1996 Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Totally agreed. The current implementation is quite conservative in the sense that it reuses all the public interfaces. We can and should get this changed as we integrate with BlockSTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants