Skip to content

Commit dfc012a

Browse files
committed
[vm] add a native test function for reading events
this is useful for ensuring that emitted events match expectations. an increasingly large number of developers have become dependent on the correctness of events. currently the only way to test events is at an integration layer, which creates challenges for pure smart contract teams. this change will improve their developer experience substantially.
1 parent d485f8e commit dfc012a

File tree

5 files changed

+74
-3
lines changed

5 files changed

+74
-3
lines changed

aptos-move/aptos-vm/src/natives.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ pub fn assert_no_test_natives(err_msg: &str) {
7676
&& func_name.as_str() == "generate_keys_internal"
7777
|| module_name.as_str() == "bls12381" && func_name.as_str() == "sign_internal"
7878
|| module_name.as_str() == "bls12381"
79-
&& func_name.as_str() == "generate_proof_of_possession_internal")
79+
&& func_name.as_str() == "generate_proof_of_possession_internal"
80+
|| module_name.as_str() == "event"
81+
&& func_name.as_str() == "emitted_events_internal")
8082
}),
8183
"{}",
8284
err_msg

aptos-move/framework/aptos-framework/sources/account.move

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,4 +1309,27 @@ module aptos_framework::account {
13091309
account_state.guid_creation_num = MAX_GUID_CREATION_NUM - 1;
13101310
create_guid(account);
13111311
}
1312+
1313+
#[test_only]
1314+
struct FakeCoin { }
1315+
#[test_only]
1316+
struct SadFakeCoin { }
1317+
1318+
#[test(account = @0x1234)]
1319+
fun test_events(account: &signer) acquires Account {
1320+
let addr = signer::address_of(account);
1321+
create_account_unchecked(addr);
1322+
register_coin<FakeCoin>(addr);
1323+
1324+
let eventhandle = &borrow_global<Account>(addr).coin_register_events;
1325+
let event = CoinRegisterEvent { type_info: type_info::type_of<FakeCoin>() };
1326+
1327+
let events = event::emitted_events(eventhandle);
1328+
assert!(vector::length(&events) == 1, 0);
1329+
assert!(vector::borrow(&events, 0) == &event, 1);
1330+
assert!(event::was_event_emitted(eventhandle, &event), 2);
1331+
1332+
let event = CoinRegisterEvent { type_info: type_info::type_of<SadFakeCoin>() };
1333+
assert!(!event::was_event_emitted(eventhandle, &event), 3);
1334+
}
13121335
}

aptos-move/framework/aptos-framework/sources/event.move

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,18 @@ module aptos_framework::event {
5454
public fun destroy_handle<T: drop + store>(handle: EventHandle<T>) {
5555
EventHandle<T> { counter: _, guid: _ } = handle;
5656
}
57+
58+
#[test_only]
59+
public fun emitted_events<T: drop + store>(handle: &EventHandle<T>): vector<T> {
60+
emitted_events_internal(bcs::to_bytes(&handle.guid))
61+
}
62+
63+
#[test_only]
64+
public fun was_event_emitted<T: drop + store>(handle: &EventHandle<T>, msg: &T): bool {
65+
use std::vector;
66+
vector::contains(&emitted_events(handle), msg)
67+
}
68+
69+
#[test_only]
70+
native fun emitted_events_internal<T: drop + store>(id: vector<u8>): vector<T>;
5771
}
261 Bytes
Binary file not shown.

aptos-move/framework/src/natives/event.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,23 @@ pub fn make_native_write_to_event_store(
6868
}
6969
}
7070

71+
#[cfg(feature = "testing")]
72+
fn native_emitted_events_internal(
73+
_: &(),
74+
context: &mut SafeNativeContext,
75+
mut ty_args: Vec<Type>,
76+
mut arguments: VecDeque<Value>,
77+
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
78+
debug_assert!(ty_args.len() == 1);
79+
debug_assert!(arguments.len() == 1);
80+
81+
let ty = ty_args.pop().unwrap();
82+
let guid = safely_pop_arg!(arguments, Vec<u8>);
83+
84+
let events = context.emitted_events(guid, ty)?;
85+
Ok(smallvec![Value::vector_for_testing_only(events)])
86+
}
87+
7188
/***************************************************************************************************
7289
* module
7390
*
@@ -77,21 +94,36 @@ pub struct GasParameters {
7794
pub write_to_event_store: WriteToEventStoreGasParameters,
7895
}
7996

97+
#[allow(clippy::vec_init_then_push)]
8098
pub fn make_all(
8199
gas_params: GasParameters,
82100
calc_abstract_val_size: impl Fn(&Value) -> AbstractValueSize + Send + Sync + 'static,
83101
timed_features: TimedFeatures,
84102
features: Arc<Features>,
85103
) -> impl Iterator<Item = (String, NativeFunction)> {
86-
let natives = [(
104+
let mut natives = vec![];
105+
106+
// Test-only natives
107+
#[cfg(feature = "testing")]
108+
natives.push((
109+
"emitted_events_internal",
110+
make_safe_native(
111+
(),
112+
timed_features.clone(),
113+
features.clone(),
114+
native_emitted_events_internal,
115+
),
116+
));
117+
118+
natives.push((
87119
"write_to_event_store",
88120
make_safe_native(
89121
gas_params.write_to_event_store,
90122
timed_features,
91123
features,
92124
make_native_write_to_event_store(calc_abstract_val_size),
93125
),
94-
)];
126+
));
95127

96128
make_module_natives(natives)
97129
}

0 commit comments

Comments
 (0)