Skip to content

Commit cec6322

Browse files
vgao1996claude
andcommitted
[mono-move] Address review feedback; migrate to type-erased errors
Rebased onto main past #20254 (type-erased error handling): the executor now carries VMInternalError through its taxonomy, and the legacy status projection downcasts to RuntimeError/LoaderError for exact codes with an ExecutionErrorKind fallback. The StateView providers gained a typed error for the VMResult-based ModuleProvider trait. Review feedback: - Split legacy/ into output.rs (renders the storage-facing TransactionOutput -- not a legacy format) and status.rs (the actual legacy VMStatus mapping). Output is built directly, without the VMChangeSet/VMOutput indirection, using plain WriteOps. - materialize() returns Result<TransactionOutput, MaterializationError>: a rendering failure is abnormal, unlike a discard, and the status no longer comes back twice. TxnOutcome::Committed -> Executed. - The resource-group API operates on interned types, and its membership cache is keyed by them. - calls.rs takes parameter types from the loaded module's interned signature instead of walking a CompiledModule; CallOutcome is now CallStatus with no Error variant, so VM errors flow only through Err; exhaustive Type match; guard-first argument order. - serialize_value is shared from the runtime rather than duplicated. - Type-argument resolution failures map to TYPE_RESOLUTION_FAILURE, not INVALID_SIGNATURE. Function type tags now intern. - The provider crate is renamed aptos-state-view-providers: these serve simulation and replay tools, not just mocks. A missing package registry is an error instead of a silent single-module fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 49a1085 commit cec6322

27 files changed

Lines changed: 486 additions & 430 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ members = [
210210
"third_party/move/documentation/framework-book/builder",
211211
"third_party/move/extensions/move-table-extension",
212212
"third_party/move/mono-move/alloc",
213-
"third_party/move/mono-move/aptos-mock-providers",
213+
"third_party/move/mono-move/aptos-state-view-providers",
214214
"third_party/move/mono-move/aptos-transaction-executor",
215215
"third_party/move/mono-move/core",
216216
"third_party/move/mono-move/global-context",
@@ -904,7 +904,7 @@ aptos-position-natives = { path = "aptos-move/framework/position-natives" }
904904
aptos-table-natives = { path = "aptos-move/framework/table-natives" }
905905
legacy-move-compiler = { path = "third_party/move/move-compiler-v2/legacy-move-compiler" }
906906
mono-move-alloc = { path = "third_party/move/mono-move/alloc" }
907-
mono-move-aptos-mock-providers = { path = "third_party/move/mono-move/aptos-mock-providers" }
907+
mono-move-aptos-state-view-providers = { path = "third_party/move/mono-move/aptos-state-view-providers" }
908908
mono-move-aptos-transaction-executor = { path = "third_party/move/mono-move/aptos-transaction-executor" }
909909
mono-move-core = { path = "third_party/move/mono-move/core" }
910910
mono-move-global-context = { path = "third_party/move/mono-move/global-context" }

third_party/move/mono-move/aptos-mock-providers/AGENTS.md

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# aptos-state-view-providers
2+
3+
`StateView`-backed implementations of the transaction executor's data traits,
4+
for sequential execution: tests, simulation, and replay-style tools. The
5+
production data layer comes from the Block-STM integration behind the same
6+
traits.
7+
8+
See the crate docs in `src/lib.rs`.

third_party/move/mono-move/aptos-mock-providers/CLAUDE.md renamed to third_party/move/mono-move/aptos-state-view-providers/CLAUDE.md

File renamed without changes.

third_party/move/mono-move/aptos-mock-providers/Cargo.toml renamed to third_party/move/mono-move/aptos-state-view-providers/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
2-
name = "mono-move-aptos-mock-providers"
3-
description = "Mock StateView-backed data providers for the MonoMove Aptos transaction executor."
2+
name = "mono-move-aptos-state-view-providers"
3+
description = "StateView-backed data providers for the MonoMove Aptos transaction executor."
44
version = "0.1.0"
55

66
# Workspace inherited keys

third_party/move/mono-move/aptos-mock-providers/src/lib.rs renamed to third_party/move/mono-move/aptos-state-view-providers/src/lib.rs

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
// Copyright (c) Aptos Foundation
22
// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE
33

4-
//! Mock data providers serving MonoMove's module and resource reads from an
5-
//! Aptos `StateView`.
4+
//! Data providers serving MonoMove's module and resource reads from an Aptos
5+
//! `StateView`, for sequential execution: tests, simulation, and replay-style
6+
//! tools. Production execution uses the Block-STM integration's providers.
67
//!
7-
//! Only intended for tests and tools (e.g. replay), not production.
8+
//! - `StateViewModuleProvider` serves module bytes and package co-membership
9+
//! to the loader.
10+
//! - `StateViewResourceProvider` serves resource and table-item reads,
11+
//! materializing each value into a long-lived arena on first access, and
12+
//! resolves resource-group placement.
813
914
use anyhow::{anyhow, Result};
1015
use aptos_framework::natives::code::PackageRegistry;
@@ -13,14 +18,19 @@ use aptos_types::{
1318
vm::module_metadata::{get_metadata, RuntimeModuleMetadataV1},
1419
};
1520
use bytes::Bytes;
16-
use mono_move_aptos_transaction_executor::{AptosDataProvider, StorageLocation};
21+
use mono_move_aptos_transaction_executor::{
22+
decode_group_members, AptosDataProvider, GroupMembers, StorageLocation,
23+
};
1724
use mono_move_core::{
25+
intern_struct_tag,
1826
storage::{
1927
module_provider::ModuleProvider,
2028
resource_provider::{
2129
InMemoryStorageKey, ResourceProvider, ResourceProviderError, StorageRead,
2230
},
2331
},
32+
struct_tag_of,
33+
types::InternedType,
2434
ExecutionErrorKind, FrameOffset, IntoExecutionError, LayoutProvider, VMInternalError, VMResult,
2535
OBJECT_HEADER_SIZE,
2636
};
@@ -32,29 +42,25 @@ use move_core_types::{
3242
move_resource::MoveStructType,
3343
};
3444
use specializer::lower::gc_layout::type_pointer_offsets;
35-
use std::{
36-
cell::RefCell,
37-
collections::{BTreeMap, HashMap},
38-
sync::Arc,
39-
};
45+
use std::{cell::RefCell, collections::HashMap, sync::Arc};
4046
use thiserror::Error;
4147

42-
/// Size of the mock provider's value arena.
48+
/// Size of the provider's value arena.
4349
pub const DEFAULT_RESOURCE_ARENA_BYTES: usize = 64 * 1024 * 1024;
4450

45-
/// Errors raised by the mock providers.
51+
/// Errors raised by these providers.
4652
#[derive(Debug, Error)]
4753
#[error("{0}")]
48-
struct MockProviderError(String);
54+
struct ProviderError(String);
4955

50-
impl IntoExecutionError for MockProviderError {
56+
impl IntoExecutionError for ProviderError {
5157
fn kind(&self) -> ExecutionErrorKind {
5258
ExecutionErrorKind::Placeholder
5359
}
5460
}
5561

5662
fn provider_error(detail: String) -> VMInternalError {
57-
VMInternalError::new(MockProviderError(detail))
63+
VMInternalError::new(ProviderError(detail))
5864
}
5965

6066
/// Serves module bytes to the loader from a `StateView`.
@@ -111,8 +117,7 @@ impl<S: StateView> ModuleProvider for StateViewModuleProvider<'_, S> {
111117
.get_state_value(&key)
112118
.map_err(|e| provider_error(format!("package registry read failed: {e}")))?
113119
else {
114-
// No registry (e.g. genesis-internal code): the module is its own package.
115-
return Ok(vec![identifier(module_name)?]);
120+
return Err(provider_error(format!("no package registry at {address}")));
116121
};
117122
let registry: PackageRegistry = bcs::from_bytes(value.bytes())
118123
.map_err(|e| provider_error(format!("malformed package registry: {e}")))?;
@@ -133,8 +138,8 @@ impl<S: StateView> ModuleProvider for StateViewModuleProvider<'_, S> {
133138
}
134139

135140
/// Serves resource and table-item reads from a `StateView`, materializing each
136-
/// value into a long-lived arena on first access. Also resolves
137-
/// and remembers resource-group placement, which write-set publication reuses.
141+
/// value into a long-lived arena on first access. Also resolves and remembers
142+
/// resource-group membership.
138143
pub struct StateViewResourceProvider<'a, 'ctx, S> {
139144
guard: &'a ExecutionGuard<'ctx>,
140145
state_view: &'a S,
@@ -147,16 +152,16 @@ struct ProviderState {
147152
/// `ExternalHeap` pointers point into. Never collected or reset; must stay
148153
/// alive as long as those pointers (through materialization).
149154
arena: Heap,
150-
/// Resource-group membership per struct tag, resolved from the defining
155+
/// Resource-group membership per resource type, resolved from the defining
151156
/// module's metadata. `None` = not a group member.
152-
group_membership: HashMap<StructTag, Option<StructTag>>,
157+
group_membership: HashMap<InternedType, Option<InternedType>>,
153158
/// Aptos metadata of each module consulted for group membership so far,
154159
/// keyed by the module's state key. `None` = module absent or without
155160
/// metadata.
156161
module_metadata: HashMap<StateKey, Option<Arc<RuntimeModuleMetadataV1>>>,
157162
/// Stored members of each resource group read so far, keyed by the
158-
/// group's state key. Publication merges member writes back into these.
159-
groups: HashMap<StateKey, Arc<BTreeMap<StructTag, Bytes>>>,
163+
/// group's state key.
164+
groups: HashMap<StateKey, Arc<GroupMembers>>,
160165
}
161166

162167
impl<'a, 'ctx, S: StateView> StateViewResourceProvider<'a, 'ctx, S> {
@@ -174,23 +179,29 @@ impl<'a, 'ctx, S: StateView> StateViewResourceProvider<'a, 'ctx, S> {
174179
}
175180

176181
/// Resolves group membership from the defining module's Aptos metadata.
177-
fn resolve_group_of(&self, tag: &StructTag) -> Result<Option<StructTag>> {
178-
let Some(metadata) = self.module_metadata(tag)? else {
182+
fn resolve_group_of(&self, ty: InternedType) -> Result<Option<InternedType>> {
183+
let tag = struct_tag_of(ty).ok_or_else(|| anyhow!("resource type is not nominal"))?;
184+
let Some(metadata) = self.module_metadata(&tag)? else {
179185
return Ok(None);
180186
};
181-
Ok(metadata
187+
let Some(group_tag) = metadata
182188
.struct_attributes
183189
.get(tag.name.as_str())
184190
.into_iter()
185191
.flatten()
186-
.find_map(|attr| attr.get_resource_group_member()))
192+
.find_map(|attr| attr.get_resource_group_member())
193+
else {
194+
return Ok(None);
195+
};
196+
Ok(Some(intern_struct_tag(&group_tag, self.guard)?))
187197
}
188198

189199
/// The Aptos metadata of `tag`'s defining module, if any. Cached per
190200
/// module.
191201
//
192-
// TODO(perf): deserializes the whole defining module for its metadata,
193-
// duplicating the loader's own fetch + deserialization of the same bytes.
202+
// TODO(perf): cache the metadata in the global context instead — this
203+
// deserializes the whole defining module for its metadata, duplicating the
204+
// loader's own fetch and deserialization of the same bytes.
194205
fn module_metadata(&self, tag: &StructTag) -> Result<Option<Arc<RuntimeModuleMetadataV1>>> {
195206
let key = StateKey::module(&tag.address, &tag.module);
196207
if let Some(metadata) = self.inner.borrow().module_metadata.get(&key) {
@@ -275,32 +286,28 @@ impl<S: StateView> ResourceProvider for StateViewResourceProvider<'_, '_, S> {
275286
}
276287

277288
impl<S: StateView> AptosDataProvider for StateViewResourceProvider<'_, '_, S> {
278-
/// Cached per tag.
279-
fn group_of(&self, tag: &StructTag) -> Result<Option<StructTag>> {
280-
if let Some(group) = self.inner.borrow().group_membership.get(tag) {
281-
return Ok(group.clone());
289+
/// Cached per resource type.
290+
fn group_of(&self, ty: InternedType) -> Result<Option<InternedType>> {
291+
if let Some(group) = self.inner.borrow().group_membership.get(&ty) {
292+
return Ok(*group);
282293
}
283-
let group = self.resolve_group_of(tag)?;
284-
self.inner
285-
.borrow_mut()
286-
.group_membership
287-
.insert(tag.clone(), group.clone());
294+
let group = self.resolve_group_of(ty)?;
295+
self.inner.borrow_mut().group_membership.insert(ty, group);
288296
Ok(group)
289297
}
290298

291-
/// Loaded from the state view on first access; missing group = empty map.
292-
fn group_members(&self, group_key: &StateKey) -> Result<Arc<BTreeMap<StructTag, Bytes>>> {
299+
/// Loaded from the state view on first access; missing group = no members.
300+
fn group_members(&self, group_key: &StateKey) -> Result<Arc<GroupMembers>> {
293301
if let Some(members) = self.inner.borrow().groups.get(group_key) {
294302
return Ok(members.clone());
295303
}
296-
let members: BTreeMap<StructTag, Bytes> = match self
304+
let members = match self
297305
.state_view
298306
.get_state_value(group_key)
299-
.map_err(|e| {
300-
anyhow!("group read failed: {e}")
301-
})? {
302-
Some(value) => bcs::from_bytes(value.bytes())?,
303-
None => BTreeMap::new(),
307+
.map_err(|e| anyhow!("group read failed: {e}"))?
308+
{
309+
Some(value) => decode_group_members(value.bytes(), self.guard)?,
310+
None => GroupMembers::new(),
304311
};
305312
let members = Arc::new(members);
306313
self.inner

third_party/move/mono-move/aptos-transaction-executor/AGENTS.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ incomplete (see Key design decisions).
1919
nothing it reads and never touches a `StateView` itself.
2020
- In: `SignedTransaction` (signature verification is the caller's job).
2121
- Out: `TxnOutcome` — the transaction's typed conclusion, carrying its side
22-
effects in VM representation. `materialize()` projects it onto the legacy
23-
`(VMStatus, TransactionOutput)` contract; only this step needs the wider
24-
`AptosDataProvider`, whose answers must agree with the provider that served
25-
execution. Block-level consumers will eventually read the effects directly.
26-
- Mock `StateView`-backed implementations of the data traits live in the
27-
sibling `mono-move-aptos-mock-providers` crate (a dev-dependency here, used
28-
by the e2e tests); Block-STM integration provides the production
22+
effects in VM representation. `materialize()` renders it into a
23+
`TransactionOutput`; only this step needs the wider `AptosDataProvider`,
24+
whose answers must agree with the provider that served execution.
25+
Block-level consumers will eventually read the effects directly.
26+
- `StateView`-backed implementations of the data traits live in the sibling
27+
`mono-move-aptos-state-view-providers` crate (a dev-dependency here, used by
28+
the e2e tests); Block-STM integration provides the production
2929
implementations.
3030

3131
## Modules
@@ -36,9 +36,9 @@ Modules stay private; anything public at the crate level is re-exported from
3636
| Module | Purpose |
3737
|---|---|
3838
| `executor.rs` | `AptosTransactionExecutor`: the transaction lifecycle driver |
39-
| `outcome.rs` | `TxnOutcome`: the unmaterialized transaction conclusion; `materialize()` escape hatch into the legacy formats |
39+
| `outcome.rs` | `TxnOutcome`: the unmaterialized transaction conclusion, and `materialize()` |
4040
| `errors.rs` | The typed outcome taxonomy: `DiscardReason`, `CommitStatus`, and the per-stage failure enums |
41-
| `legacy/` | The only place legacy types are constructed: `status.rs` projects the taxonomy onto legacy statuses; `materialize.rs` renders effects into `TransactionOutput` |
41+
| `materialize/` | Rendering into the storage-facing formats: `txn_output.rs` drains the write set (with resource-group merge-back) into a `TransactionOutput`; `vm_status.rs` projects the taxonomy onto `VMStatus`/`TransactionStatus` and hosts the keep/discard rules |
4242
| `providers.rs` | The `AptosDataProvider` trait: what write-set materialization needs from the data layer |
4343
| `natives.rs` | Native function wiring: the production native registry and the per-transaction native extensions |
4444
| `calls.rs` | Making one function call in the shared interpreter context |
@@ -60,11 +60,13 @@ Modules stay private; anything public at the crate level is re-exported from
6060
refunds are not charged yet, and the prologue/epilogue run unmetered.
6161
Gas amounts therefore diverge from the legacy VM by design; differential
6262
tests compare outputs with only the fee-embedding slots masked.
63-
- **The write-set drain in `legacy/materialize.rs` is a stopgap.** Real
64-
publication (modification detection, storage metadata, refunds) will be
65-
built inside the runtime by a separate workstream. Do not use the runtime's
66-
`SessionEffects::write_set()` here in the meantime: it does not handle
67-
resource groups; the drain does.
63+
- **The write-set drain in `materialize/txn_output.rs` is a stopgap**, and where it should
64+
ultimately live is an open question. Real publication (modification
65+
detection, storage metadata, refunds) belongs inside the runtime, and the
66+
runtime already has `SessionEffects::write_set()` — but that path knows
67+
nothing about resource groups, which this drain handles via the data
68+
provider. Resolving the two (and how much of resource groups the VM layer
69+
should see at all) needs a design doc before either side hardens.
6870

6971
## Testing
7072

third_party/move/mono-move/aptos-transaction-executor/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mono-move-aptos-transaction-executor"
3-
description = "The Aptos transaction-execution layer (the legacy AptosVM role) on the MonoMove VM."
3+
description = "The Aptos transaction-execution layer on the MonoMove VM."
44
version = "0.1.0"
55

66
# Workspace inherited keys
@@ -27,9 +27,10 @@ mono-move-runtime = { workspace = true }
2727
move-binary-format = { workspace = true }
2828
move-core-types = { workspace = true }
2929
serde = { workspace = true }
30+
thiserror = { workspace = true }
3031

3132
[dev-dependencies]
3233
aptos-cached-packages = { workspace = true }
3334
aptos-language-e2e-tests = { workspace = true }
3435
aptos-transaction-simulation = { workspace = true }
35-
mono-move-aptos-mock-providers = { workspace = true }
36+
mono-move-aptos-state-view-providers = { workspace = true }

0 commit comments

Comments
 (0)