Skip to content

Commit e3fb8b7

Browse files
authored
Move unit test for monotonically increasing counter native function (#17885)
1 parent 77ea387 commit e3fb8b7

File tree

5 files changed

+117
-8
lines changed

5 files changed

+117
-8
lines changed

aptos-move/framework/aptos-framework/doc/transaction_context.md

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
- [Function `inner_entry_function_payload`](#0x1_transaction_context_inner_entry_function_payload)
4242
- [Function `monotonically_increasing_counter`](#0x1_transaction_context_monotonically_increasing_counter)
4343
- [Function `monotonically_increasing_counter_internal`](#0x1_transaction_context_monotonically_increasing_counter_internal)
44+
- [Function `monotonically_increasing_counter_internal_for_test_only`](#0x1_transaction_context_monotonically_increasing_counter_internal_for_test_only)
4445
- [Specification](#@Specification_1)
4546
- [Function `get_txn_hash`](#@Specification_1_get_txn_hash)
4647
- [Function `get_transaction_hash`](#@Specification_1_get_transaction_hash)
@@ -59,6 +60,7 @@
5960
- [Function `entry_function_payload_internal`](#@Specification_1_entry_function_payload_internal)
6061
- [Function `multisig_payload_internal`](#@Specification_1_multisig_payload_internal)
6162
- [Function `monotonically_increasing_counter_internal`](#@Specification_1_monotonically_increasing_counter_internal)
63+
- [Function `monotonically_increasing_counter_internal_for_test_only`](#@Specification_1_monotonically_increasing_counter_internal_for_test_only)
6264

6365

6466
<pre><code><b>use</b> <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error">0x1::error</a>;
@@ -997,6 +999,7 @@ Returns a monotonically increasing counter value that combines timestamp, transa
997999
session counter, and local counter into a 128-bit value.
9981000
Format: <code>&lt;reserved_byte (8 bits)&gt; || timestamp_us (64 bits) || transaction_index (32 bits) || session_counter (8 bits) || local_counter (16 bits)</code>
9991001
The function aborts if the local counter overflows (after 65535 calls in a single session).
1002+
When compiled for testing, this function bypasses feature checks and returns a simplified counter value.
10001003

10011004

10021005
<pre><code><b>public</b> <b>fun</b> <a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter">monotonically_increasing_counter</a>(): u128
@@ -1009,10 +1012,12 @@ The function aborts if the local counter overflows (after 65535 calls in a singl
10091012

10101013

10111014
<pre><code><b>public</b> <b>fun</b> <a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter">monotonically_increasing_counter</a>(): u128 {
1012-
<b>assert</b>!(<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_transaction_context_extension_enabled">features::transaction_context_extension_enabled</a>(), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_state">error::invalid_state</a>(<a href="transaction_context.md#0x1_transaction_context_ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED">ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED</a>));
1013-
<b>assert</b>!(<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_is_monotonically_increasing_counter_enabled">features::is_monotonically_increasing_counter_enabled</a>(), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_state">error::invalid_state</a>(<a href="transaction_context.md#0x1_transaction_context_EMONOTONICALLY_INCREASING_COUNTER_NOT_ENABLED">EMONOTONICALLY_INCREASING_COUNTER_NOT_ENABLED</a>));
1014-
<b>let</b> timestamp_us = <a href="timestamp.md#0x1_timestamp_now_microseconds">timestamp::now_microseconds</a>();
1015-
<a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter_internal">monotonically_increasing_counter_internal</a>(timestamp_us)
1015+
<b>if</b> (__COMPILE_FOR_TESTING__) {
1016+
<a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter_internal_for_test_only">monotonically_increasing_counter_internal_for_test_only</a>()
1017+
} <b>else</b> {
1018+
<b>assert</b>!(<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_is_monotonically_increasing_counter_enabled">features::is_monotonically_increasing_counter_enabled</a>(), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_state">error::invalid_state</a>(<a href="transaction_context.md#0x1_transaction_context_EMONOTONICALLY_INCREASING_COUNTER_NOT_ENABLED">EMONOTONICALLY_INCREASING_COUNTER_NOT_ENABLED</a>));
1019+
<a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter_internal">monotonically_increasing_counter_internal</a>(<a href="timestamp.md#0x1_timestamp_now_microseconds">timestamp::now_microseconds</a>())
1020+
}
10161021
}
10171022
</code></pre>
10181023

@@ -1040,6 +1045,31 @@ The function aborts if the local counter overflows (after 65535 calls in a singl
10401045

10411046

10421047

1048+
</details>
1049+
1050+
<a id="0x1_transaction_context_monotonically_increasing_counter_internal_for_test_only"></a>
1051+
1052+
## Function `monotonically_increasing_counter_internal_for_test_only`
1053+
1054+
Test-only version of monotonically_increasing_counter that returns increasing values
1055+
without requiring a user transaction context. This allows unit tests to verify
1056+
the monotonically increasing behavior.
1057+
1058+
1059+
<pre><code><b>fun</b> <a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter_internal_for_test_only">monotonically_increasing_counter_internal_for_test_only</a>(): u128
1060+
</code></pre>
1061+
1062+
1063+
1064+
<details>
1065+
<summary>Implementation</summary>
1066+
1067+
1068+
<pre><code><b>native</b> <b>fun</b> <a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter_internal_for_test_only">monotonically_increasing_counter_internal_for_test_only</a>(): u128;
1069+
</code></pre>
1070+
1071+
1072+
10431073
</details>
10441074

10451075
<a id="@Specification_1"></a>
@@ -1376,6 +1406,22 @@ The function aborts if the local counter overflows (after 65535 calls in a singl
13761406

13771407

13781408

1409+
<pre><code><b>pragma</b> opaque;
1410+
</code></pre>
1411+
1412+
1413+
1414+
<a id="@Specification_1_monotonically_increasing_counter_internal_for_test_only"></a>
1415+
1416+
### Function `monotonically_increasing_counter_internal_for_test_only`
1417+
1418+
1419+
<pre><code><b>fun</b> <a href="transaction_context.md#0x1_transaction_context_monotonically_increasing_counter_internal_for_test_only">monotonically_increasing_counter_internal_for_test_only</a>(): u128
1420+
</code></pre>
1421+
1422+
1423+
1424+
13791425
<pre><code><b>pragma</b> opaque;
13801426
</code></pre>
13811427

aptos-move/framework/aptos-framework/sources/transaction_context.move

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,22 @@ module aptos_framework::transaction_context {
193193
/// session counter, and local counter into a 128-bit value.
194194
/// Format: `<reserved_byte (8 bits)> || timestamp_us (64 bits) || transaction_index (32 bits) || session_counter (8 bits) || local_counter (16 bits)`
195195
/// The function aborts if the local counter overflows (after 65535 calls in a single session).
196+
/// When compiled for testing, this function bypasses feature checks and returns a simplified counter value.
196197
public fun monotonically_increasing_counter(): u128 {
197-
assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
198-
assert!(features::is_monotonically_increasing_counter_enabled(), error::invalid_state(EMONOTONICALLY_INCREASING_COUNTER_NOT_ENABLED));
199-
let timestamp_us = timestamp::now_microseconds();
200-
monotonically_increasing_counter_internal(timestamp_us)
198+
if (__COMPILE_FOR_TESTING__) {
199+
monotonically_increasing_counter_internal_for_test_only()
200+
} else {
201+
assert!(features::is_monotonically_increasing_counter_enabled(), error::invalid_state(EMONOTONICALLY_INCREASING_COUNTER_NOT_ENABLED));
202+
monotonically_increasing_counter_internal(timestamp::now_microseconds())
203+
}
201204
}
202205
native fun monotonically_increasing_counter_internal(timestamp_us: u64): u128;
203206

207+
/// Test-only version of monotonically_increasing_counter that returns increasing values
208+
/// without requiring a user transaction context. This allows unit tests to verify
209+
/// the monotonically increasing behavior.
210+
native fun monotonically_increasing_counter_internal_for_test_only(): u128;
211+
204212
#[test_only]
205213
public fun new_entry_function_payload(
206214
account_address: address,
@@ -295,4 +303,15 @@ module aptos_framework::transaction_context {
295303
// expected to fail with the error code of `invalid_state(E_TRANSACTION_CONTEXT_NOT_AVAILABLE)`
296304
let _multisig = multisig_payload();
297305
}
306+
307+
#[test]
308+
fun test_monotonically_increasing_counter() {
309+
let counter1 = monotonically_increasing_counter();
310+
let counter2 = monotonically_increasing_counter();
311+
let counter3 = monotonically_increasing_counter();
312+
313+
// Verify that the increment is exactly 1 (since only local_counter changes in test mode)
314+
assert!(counter2 == counter1 + 1, 2);
315+
assert!(counter3 == counter2 + 1, 3);
316+
}
298317
}

aptos-move/framework/aptos-framework/sources/transaction_context.spec.move

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,9 @@ spec aptos_framework::transaction_context {
113113
//TODO: temporary mockup
114114
pragma opaque;
115115
}
116+
117+
spec monotonically_increasing_counter_internal_for_test_only(): u128 {
118+
//TODO: temporary mockup
119+
pragma opaque;
120+
}
116121
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,46 @@ fn native_monotonically_increasing_counter_internal(
182182
monotonically_increasing_counter |= local_counter;
183183
Ok(smallvec![Value::u128(monotonically_increasing_counter)])
184184
} else {
185+
// When transaction context is not available, return an error
185186
Err(SafeNativeError::Abort {
186187
abort_code: error::invalid_state(abort_codes::ETRANSACTION_CONTEXT_NOT_AVAILABLE),
187188
})
188189
}
189190
}
190191

192+
/***************************************************************************************************
193+
* native fun monotonically_increasing_counter_internal_for_test_only
194+
*
195+
* gas cost: base_cost
196+
*
197+
* This is a test-only version that returns increasing counter values without requiring
198+
* a user transaction context. Used when COMPILE_FOR_TESTING flag is enabled.
199+
*
200+
**************************************************************************************************/
201+
fn native_monotonically_increasing_counter_internal_for_test_only(
202+
context: &mut SafeNativeContext,
203+
_ty_args: Vec<Type>,
204+
_args: VecDeque<Value>,
205+
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
206+
context.charge(TRANSACTION_CONTEXT_MONOTONICALLY_INCREASING_COUNTER_BASE)?;
207+
208+
let transaction_context = context
209+
.extensions_mut()
210+
.get_mut::<NativeTransactionContext>();
211+
if transaction_context.local_counter == u16::MAX {
212+
return Err(SafeNativeError::Abort {
213+
abort_code: error::invalid_state(
214+
abort_codes::EMONOTONICALLY_INCREASING_COUNTER_OVERFLOW,
215+
),
216+
});
217+
}
218+
transaction_context.local_counter += 1;
219+
let local_counter = transaction_context.local_counter as u128;
220+
221+
// For testing, return just the local counter value to verify monotonically increasing behavior
222+
Ok(smallvec![Value::u128(local_counter)])
223+
}
224+
191225
/***************************************************************************************************
192226
* native fun get_script_hash
193227
*
@@ -471,6 +505,10 @@ pub fn make_all(
471505
"monotonically_increasing_counter_internal",
472506
native_monotonically_increasing_counter_internal,
473507
),
508+
(
509+
"monotonically_increasing_counter_internal_for_test_only",
510+
native_monotonically_increasing_counter_internal_for_test_only,
511+
),
474512
("get_txn_hash", native_get_txn_hash),
475513
("sender_internal", native_sender_internal),
476514
(

aptos-move/framework/tests/move_unit_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn run_tests_for_pkg(path_to_pkg: impl Into<String>, use_latest_language: bool)
3939
full_model_generation: true, // Run extended checks also on test code
4040
..Default::default()
4141
};
42+
4243
if use_latest_language {
4344
let latest_build_options = BuildOptions::default().set_latest_language();
4445
build_config.compiler_config.bytecode_version = latest_build_options.bytecode_version;

0 commit comments

Comments
 (0)