Skip to content

Commit d178117

Browse files
committed
partial cheat scenario impl
1 parent 5afd7c1 commit d178117

8 files changed

Lines changed: 301 additions & 140 deletions

File tree

Cargo.lock

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

crates/movy-replay/src/exec.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use move_vm_runtime::move_vm::MoveVM;
88
use movy_sui::{
99
cheats::{all_cheates, backend::CheatBackend},
1010
compile::SuiCompiledPackage,
11-
database::cache::ObjectSuiStoreCommit,
11+
database::cache::{CachedSnapshot, ObjectSuiStoreCommit},
1212
};
1313
use movy_types::{error::MovyError, object::MoveOwner};
1414
use sui_move_natives_latest::all_natives;
@@ -43,7 +43,7 @@ pub fn testing_proto() -> ProtocolConfig {
4343
#[derive(Clone)]
4444
pub struct SuiExecutor<T> {
4545
pub db: T,
46-
pub cheat_backend: CheatBackend<T>,
46+
pub cheat_backend: CheatBackend,
4747
pub protocol_config: ProtocolConfig,
4848
pub metrics: Arc<LimitsMetrics>,
4949
pub registry: prometheus::Registry,
@@ -70,19 +70,13 @@ impl<R> Deref for ExecutionTracedResults<R> {
7070

7171
impl<T> SuiExecutor<T>
7272
where
73-
T: ObjectStore
74-
+ BackingStore
75-
+ ObjectSuiStoreCommit
76-
+ ObjectStoreMintObject
77-
+ ObjectStoreInfo
78-
+ Clone
79-
+ 'static,
73+
T: ObjectStore + BackingStore + ObjectSuiStoreCommit + ObjectStoreMintObject + ObjectStoreInfo,
8074
{
81-
pub fn new(db: T) -> Result<Self, MovyError> {
75+
pub fn new_with_cheats_storage(db: T, storage: CachedSnapshot) -> Result<Self, MovyError> {
8276
let protocol_config = testing_proto();
8377
let registry = prometheus::Registry::new();
8478
let metrics = Arc::new(LimitsMetrics::new(&registry));
85-
let (cheat_backend, cheats) = all_cheates(db.clone());
79+
let (cheat_backend, cheats) = all_cheates(storage);
8680
let movevm = Arc::new(
8781
MoveVM::new(
8882
all_natives(false, &protocol_config)
@@ -100,6 +94,9 @@ where
10094
movevm,
10195
})
10296
}
97+
pub fn new(db: T) -> Result<Self, MovyError> {
98+
Self::new_with_cheats_storage(db, CachedSnapshot::default())
99+
}
103100

104101
pub fn run_tx_trace<R: Tracer>(
105102
&self,

crates/movy-sui-stds/movy/sources/cheats.move

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,42 @@
11
module movy::cheats;
22

33
use sui::tx_context::TxContext;
4+
use sui::vec_map::VecMap;
45

56
const TX_HASH_LENGTH: u64 = 32;
67

8+
9+
/// Attempted to return an object to the inventory that was not previously removed from the
10+
/// inventory during the current transaction. Can happen if the user attempts to call
11+
/// `return_to_address` on a locally constructed object rather than one returned from a
12+
/// `test_scenario` function such as `take_from_address`.
13+
const ECantReturnObject: u64 = 2;
14+
15+
/// Attempted to retrieve an object of a particular type from the inventory, but it is empty.
16+
/// Can happen if the user already transferred the object or a previous transaction failed to
17+
/// transfer the object to the user.
18+
const EEmptyInventory: u64 = 3;
19+
20+
/// The effects of a transaction
21+
public struct TransactionEffects has drop {
22+
/// The objects created this transaction
23+
created: vector<ID>,
24+
/// The objects written/modified this transaction
25+
written: vector<ID>,
26+
/// The objects deleted this transaction
27+
deleted: vector<ID>,
28+
/// The objects transferred to an account this transaction
29+
transferred_to_account: VecMap<ID, /* owner */ address>,
30+
/// The objects transferred to an object this transaction
31+
transferred_to_object: VecMap<ID, /* owner */ ID>,
32+
/// The objects shared this transaction
33+
shared: vector<ID>,
34+
/// The objects frozen this transaction
35+
frozen: vector<ID>,
36+
/// The number of user events emitted this transaction
37+
num_user_events: u64,
38+
}
39+
740
/// A cheat scenario for mocking a multi-transaction Sui execution
841
public struct CheatScenario {
942
txn_number: u64,
@@ -18,11 +51,15 @@ public fun begin(sender: address): CheatScenario {
1851
}
1952
}
2053

54+
public fun ctx(scenario: &mut CheatScenario): &mut TxContext {
55+
&mut scenario.ctx
56+
}
57+
2158
/// Ends the test scenario
2259
/// Returns the results from the final transaction
2360
/// Will abort if shared or immutable objects were deleted, transferred, or wrapped.
2461
/// Will abort if TransactionEffects cannot be generated
25-
public fun end(scenario: CheatScenario) {
62+
public fun end(scenario: CheatScenario): TransactionEffects {
2663
let CheatScenario { txn_number: _, ctx: _ } = scenario;
2764
end_transaction()
2865
}
@@ -36,7 +73,7 @@ public fun end(scenario: CheatScenario) {
3673
/// Returns the results from the previous transaction
3774
/// Will abort if shared or immutable objects were deleted, transferred, or wrapped.
3875
/// Will abort if TransactionEffects cannot be generated
39-
public fun next_tx(scenario: &mut CheatScenario, sender: address) {
76+
public fun next_tx(scenario: &mut CheatScenario, sender: address): TransactionEffects {
4077
// create a seed for new transaction digest to ensure that this tx has a different
4178
// digest (and consequently, different object ID's) than the previous tx
4279
scenario.txn_number = scenario.txn_number + 1;
@@ -105,16 +142,34 @@ native fun new_tx_context(
105142
ids_created: u64
106143
): TxContext;
107144

145+
/// Helper combining `take_shared_by_id` and `most_recent_id_shared`
146+
/// Aborts if there is no shared object of type `T` in the global inventory
147+
public fun take_shared<T: key>(): T {
148+
let id_opt = most_recent_id_shared<T>();
149+
assert!(id_opt.is_some(), EEmptyInventory);
150+
take_by_id(id_opt.destroy_some())
151+
}
152+
153+
/// Return `t` to the global inventory
154+
public fun return_shared<T: key>(t: T) {
155+
let id = object::id(&t);
156+
assert!(was_taken_shared(id), ECantReturnObject);
157+
share_object_impl(t)
158+
}
159+
160+
161+
native fun was_taken_shared(id: ID): bool;
162+
native fun most_recent_id_shared<T: key>(): Option<ID>;
163+
108164
// native fun objects_by_type<T: key>(): vector<ID>;
109165

110166
native fun take_by_id<T: key>(id: ID): T;
111167

112168
// Forward to sui std native call
113-
// native fun share_object_impl<T: key>(obj: T);
114-
169+
native fun share_object_impl<T: key>(obj: T);
115170
// native fun freeze_object_impl<T: key>(obj: T);
116171

117-
native fun end_transaction();
172+
native fun end_transaction(): TransactionEffects;
118173

119174
// public fun return_shared<T: key>(t: T) {
120175
// share_object_impl(t)

crates/movy-sui/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ sui-rpc = {workspace = true}
4343
sui-sdk-types = {workspace = true}
4444
shared-crypto = {workspace = true}
4545
sui-sdk = {workspace = true}
46+
sui-move-natives-latest = {workspace = true}
4647

4748
movy-types = {workspace = true}
4849

crates/movy-sui/src/cheats/backend.rs

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,43 @@
1-
use std::{cell::UnsafeCell, sync::Arc};
1+
use std::{
2+
cell::UnsafeCell,
3+
collections::{BTreeMap, BTreeSet},
4+
sync::Arc,
5+
};
6+
7+
use sui_types::{
8+
TypeTag,
9+
base_types::{MoveObjectType, ObjectID},
10+
};
11+
12+
use crate::database::cache::CachedSnapshot;
213

314
// CheatBackend is a global backend that a Move VM globally holds
4-
pub struct CheatBackendInner<T> {
5-
pub db: T,
15+
pub struct CheatBackendInner {
16+
pub storage: CachedSnapshot,
17+
pub tys: BTreeMap<MoveObjectType, BTreeSet<ObjectID>>,
18+
pub latest_objects_by_types: BTreeMap<MoveObjectType, ObjectID>,
19+
pub taken: BTreeSet<ObjectID>,
620
}
721

8-
impl<T> CheatBackendInner<T> {
9-
pub fn new(db: T) -> Self {
10-
Self { db }
22+
impl CheatBackendInner {
23+
pub fn new(storage: CachedSnapshot) -> Self {
24+
let mut tys: BTreeMap<MoveObjectType, BTreeSet<ObjectID>> = BTreeMap::new();
25+
26+
for (id, mp) in storage.objects.iter() {
27+
for (_, obj) in mp.iter() {
28+
if let Some(obj) = obj {
29+
if let Some(ty) = obj.type_() {
30+
tys.entry(ty.clone()).or_default().insert(*id);
31+
}
32+
}
33+
}
34+
}
35+
Self {
36+
storage,
37+
tys,
38+
latest_objects_by_types: BTreeMap::new(),
39+
taken: BTreeSet::new(),
40+
}
1141
}
1242

1343
// Must be called before every ptb execution
@@ -18,11 +48,11 @@ impl<T> CheatBackendInner<T> {
1848

1949
// Tight wrapper of CheatBackendInner to satisfy the contract of Sui natives
2050
// Note it is ub to call cheat backend concurrently.
21-
pub struct CheatBackend<T> {
22-
inner: Arc<UnsafeCell<CheatBackendInner<T>>>,
51+
pub struct CheatBackend {
52+
inner: Arc<UnsafeCell<CheatBackendInner>>,
2353
}
2454

25-
impl<T> Clone for CheatBackend<T> {
55+
impl Clone for CheatBackend {
2656
fn clone(&self) -> Self {
2757
Self {
2858
inner: self.inner.clone(),
@@ -31,22 +61,22 @@ impl<T> Clone for CheatBackend<T> {
3161
}
3262

3363
// SAFETY: We guarantee single-threaded access
34-
unsafe impl<T> Send for CheatBackend<T> {}
35-
unsafe impl<T> Sync for CheatBackend<T> {}
64+
unsafe impl Send for CheatBackend {}
65+
unsafe impl Sync for CheatBackend {}
3666

37-
impl<T> CheatBackend<T> {
38-
pub fn new(db: T) -> Self {
67+
impl CheatBackend {
68+
pub fn new(storage: CachedSnapshot) -> Self {
3969
Self {
40-
inner: Arc::new(UnsafeCell::new(CheatBackendInner::new(db))),
70+
inner: Arc::new(UnsafeCell::new(CheatBackendInner::new(storage))),
4171
}
4272
}
4373

44-
pub fn inner(&self) -> &CheatBackendInner<T> {
74+
pub fn inner(&self) -> &CheatBackendInner {
4575
// SAFETY: No concurrent access exists by design.
4676
unsafe { &*self.inner.get() }
4777
}
4878

49-
pub fn inner_mut(&self) -> &mut CheatBackendInner<T> {
79+
pub fn inner_mut(&self) -> &mut CheatBackendInner {
5080
// SAFETY: No concurrent access exists by design.
5181
unsafe { &mut *self.inner.get() }
5282
}

crates/movy-sui/src/cheats/mod.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use move_core_types::account_address::AccountAddress;
44
use move_vm_runtime::native_functions::NativeFunctionTable;
55
use sui_types::Identifier;
66

7-
use crate::cheats::backend::CheatBackend;
7+
use crate::{cheats::backend::CheatBackend, database::cache::CachedSnapshot};
88

99
pub mod backend;
1010
pub mod ctx;
@@ -55,11 +55,8 @@ pub fn cheat_address() -> &'static AccountAddress {
5555
&CHEAT
5656
}
5757

58-
pub fn all_cheates<T>(db: T) -> (CheatBackend<T>, NativeFunctionTable)
59-
where
60-
T: 'static,
61-
{
62-
let backend = CheatBackend::new(db);
58+
pub fn all_cheates(storage: CachedSnapshot) -> (CheatBackend, NativeFunctionTable) {
59+
let backend = CheatBackend::new(storage);
6360
(
6461
backend.clone(),
6562
vec![
@@ -68,6 +65,12 @@ where
6865
"new_tx_context",
6966
super::cheats::ctx::new_tx_context
7067
),
68+
make_backend_cheat!(
69+
backend,
70+
"cheats",
71+
"share_object_impl",
72+
super::cheats::scenario::share_object_impl
73+
),
7174
make_backend_cheat!(
7275
backend,
7376
"cheats",
Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,87 @@
11
use std::collections::VecDeque;
22

3-
use move_binary_format::errors::PartialVMResult;
4-
use move_core_types::gas_algebra::InternalGas;
3+
use move_binary_format::errors::{PartialVMError, PartialVMResult};
4+
use move_core_types::{
5+
account_address::AccountAddress, gas_algebra::InternalGas, vm_status::StatusCode,
6+
};
57
use move_vm_runtime::native_functions::NativeContext;
68
use move_vm_types::{
79
loaded_data::runtime_types::Type, natives::function::NativeResult, values::Value,
810
};
11+
use sui_move_natives_latest::get_nth_struct_field;
12+
use sui_types::{
13+
TypeTag,
14+
base_types::{MoveObjectType, ObjectID},
15+
};
916

1017
use crate::cheats::backend::CheatBackend;
1118

12-
// pub fn take_by_id<T>(db: T, _ctx: &NativeContext, tys: Vec<Type>, vals: VecDeque<Value>) -> PartialVMResult<NativeResult>
13-
// {
19+
fn get_specified_ty(mut ty_args: Vec<Type>) -> Type {
20+
assert!(ty_args.len() == 1);
21+
ty_args.pop().unwrap()
22+
}
23+
24+
fn pop_id(args: &mut VecDeque<Value>) -> PartialVMResult<ObjectID> {
25+
let v = match args.pop_back() {
26+
None => {
27+
return Err(PartialVMError::new(
28+
StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
29+
));
30+
}
31+
Some(v) => v,
32+
};
33+
Ok(get_nth_struct_field(v, 0)?
34+
.value_as::<AccountAddress>()?
35+
.into())
36+
}
37+
38+
fn object_type_of_type(context: &NativeContext, ty: &Type) -> PartialVMResult<MoveObjectType> {
39+
let TypeTag::Struct(s_tag) = context.type_to_type_tag(ty)? else {
40+
return Err(PartialVMError::new(
41+
StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
42+
));
43+
};
44+
Ok(MoveObjectType::from(*s_tag))
45+
}
46+
47+
// native fun was_taken_shared(id: ID): bool;
48+
// native fun most_recent_id_shared<T: key>(): Option<ID>;
1449

15-
// }
50+
// // native fun objects_by_type<T: key>(): vector<ID>;
51+
52+
// // Forward to sui std native call
53+
// native fun share_object_impl<T: key>(obj: T);
54+
pub fn share_object_impl(
55+
_backend: &CheatBackend,
56+
ctx: &mut NativeContext,
57+
tys: Vec<Type>,
58+
vals: VecDeque<Value>,
59+
) -> PartialVMResult<NativeResult> {
60+
sui_move_natives_latest::transfer::share_object(ctx, tys, vals)
61+
}
62+
63+
// // native fun freeze_object_impl<T: key>(obj: T); // TODO
64+
65+
// native fun take_by_id<T: key>(id: ID): T;
66+
pub fn take_by_id(
67+
backend: &CheatBackend,
68+
ctx: &mut NativeContext,
69+
tys: Vec<Type>,
70+
mut vals: VecDeque<Value>,
71+
) -> PartialVMResult<NativeResult> {
72+
let ty = get_specified_ty(tys);
73+
let id = pop_id(vals)?;
74+
let specified_obj_ty = object_type_of_type(ctx, &ty)?;
75+
// TODO
76+
PartialVMResult::Ok(NativeResult::ok(InternalGas::zero(), vec![].into()))
77+
}
1678

1779
// native fun end_transaction();
18-
pub fn end_transaction<T>(
19-
backend: &CheatBackend<T>,
20-
_ctx: &NativeContext,
80+
pub fn end_transaction(
81+
backend: &CheatBackend,
82+
_ctx: &mut NativeContext,
2183
_tys: Vec<Type>,
2284
_vals: VecDeque<Value>,
2385
) -> PartialVMResult<NativeResult> {
24-
backend.inner_mut().end_transaction();
2586
PartialVMResult::Ok(NativeResult::ok(InternalGas::zero(), vec![].into()))
2687
}

0 commit comments

Comments
 (0)