diff --git a/aptos-move/framework/aptos-framework/doc/aggregator_v2.md b/aptos-move/framework/aptos-framework/doc/aggregator_v2.md
index 8b9f822d0b515..9fd3230df6d94 100644
--- a/aptos-move/framework/aptos-framework/doc/aggregator_v2.md
+++ b/aptos-move/framework/aptos-framework/doc/aggregator_v2.md
@@ -11,9 +11,8 @@ across multiple transactions, enabling parallel execution. For example, if the
first transaction is doing try_add(X, 1)
for aggregator X
, and the second is
doing try_sub(X,3)
, they can be executed in parallel avoiding a read-modify-write
dependency.
-However, reading the aggregator value (i.e. calling read(X)
) is an expensive
-operation and should be avoided as much as possible because it reduces the
-parallelism.
+However, reading the aggregator value (i.e. calling read(X)
) is a resource-intensive
+operation that also reduced parallelism, and should be avoided as much as possible.
- [Struct `Aggregator`](#0x1_aggregator_v2_Aggregator)
@@ -32,6 +31,7 @@ parallelism.
- [Function `copy_snapshot`](#0x1_aggregator_v2_copy_snapshot)
- [Function `read_snapshot`](#0x1_aggregator_v2_read_snapshot)
- [Function `string_concat`](#0x1_aggregator_v2_string_concat)
+- [Function `test_aggregator_valid_type`](#0x1_aggregator_v2_test_aggregator_valid_type)
- [Specification](#@Specification_1)
- [Function `create_aggregator`](#@Specification_1_create_aggregator)
- [Function `try_add`](#@Specification_1_try_add)
@@ -59,7 +59,7 @@ across multiple transactions. See the module description for more details.
Currently supported types for Element are u64 and u128.
-
struct Aggregator<IntElement> has store
+struct Aggregator<IntElement> has drop, store
@@ -141,23 +141,23 @@ The value of aggregator underflows (goes below zero). Raised by uncoditional sub
-
+
-The native aggregator function, that is in the move file, is not yet supported.
-and any calls will raise this error.
+The aggregator api v2 feature flag is not enabled.
-const EAGGREGATOR_FUNCTION_NOT_YET_SUPPORTED: u64 = 9;
+const EAGGREGATOR_API_V2_NOT_ENABLED: u64 = 6;
-
+
-The aggregator snapshots feature flag is not enabled.
+The native aggregator function, that is in the move file, is not yet supported.
+and any calls will raise this error.
-const EAGGREGATOR_SNAPSHOTS_NOT_ENABLED: u64 = 6;
+const EAGGREGATOR_FUNCTION_NOT_YET_SUPPORTED: u64 = 9;
@@ -371,7 +371,9 @@ If subtraction would result in a negative value, false
is re
## Function `read`
Returns a value stored in this aggregator.
-Note: This operation prevents parallelism of the transaction that calls it.
+Note: This operation is resource-intensive, and reduces parallelism.
+(Especially if called in a transaction that also modifies the aggregator,
+or has other read/write conflicts)
public fun read<IntElement>(aggregator: &aggregator_v2::Aggregator<IntElement>): IntElement
@@ -395,7 +397,7 @@ Note: This operation prevents parallelism of the transaction that calls it.
## Function `snapshot`
Returns a wrapper of a current value of an aggregator
-Unlike read(), this enables parallel execution of transactions.
+Unlike read(), it is fast and avoids sequential dependencies.
public fun snapshot<IntElement>(aggregator: &aggregator_v2::Aggregator<IntElement>): aggregator_v2::AggregatorSnapshot<IntElement>
@@ -466,7 +468,9 @@ NOT YET IMPLEMENTED, always raises EAGGREGATOR_FUNCTION_NOT_YET_SUPPORTED.
## Function `read_snapshot`
Returns a value stored in this snapshot.
-Note: This operation prevents parallelism of the transaction that calls it.
+Note: This operation is resource-intensive, and reduces parallelism.
+(Especially if called in a transaction that also modifies the aggregator,
+or has other read/write conflicts)
public fun read_snapshot<Element>(snapshot: &aggregator_v2::AggregatorSnapshot<Element>): Element
@@ -509,6 +513,33 @@ If length of prefix and suffix together exceed 256 bytes, ECONCAT_STRING_LENGTH_
+
+
+
+
+## Function `test_aggregator_valid_type`
+
+
+
+public fun test_aggregator_valid_type()
+
+
+
+
+
+Implementation
+
+
+public fun test_aggregator_valid_type() {
+ create_unbounded_aggregator<u64>();
+ create_unbounded_aggregator<u128>();
+ create_aggregator<u64>(5);
+ create_aggregator<u128>(5);
+}
+
+
+
+
diff --git a/aptos-move/framework/aptos-framework/sources/aggregator_v2/aggregator_v2.move b/aptos-move/framework/aptos-framework/sources/aggregator_v2/aggregator_v2.move
index 37f1d46a82161..df077b183be0e 100644
--- a/aptos-move/framework/aptos-framework/sources/aggregator_v2/aggregator_v2.move
+++ b/aptos-move/framework/aptos-framework/sources/aggregator_v2/aggregator_v2.move
@@ -6,9 +6,8 @@
/// first transaction is doing `try_add(X, 1)` for aggregator `X`, and the second is
/// doing `try_sub(X,3)`, they can be executed in parallel avoiding a read-modify-write
/// dependency.
-/// However, reading the aggregator value (i.e. calling `read(X)`) is an expensive
-/// operation and should be avoided as much as possible because it reduces the
-/// parallelism.
+/// However, reading the aggregator value (i.e. calling `read(X)`) is a resource-intensive
+/// operation that also reduced parallelism, and should be avoided as much as possible.
module aptos_framework::aggregator_v2 {
use std::error;
use std::string::String;
@@ -22,8 +21,8 @@ module aptos_framework::aggregator_v2 {
/// The generic type supplied to the aggregator snapshot is not supported.
const EUNSUPPORTED_AGGREGATOR_SNAPSHOT_TYPE: u64 = 5;
- /// The aggregator snapshots feature flag is not enabled.
- const EAGGREGATOR_SNAPSHOTS_NOT_ENABLED: u64 = 6;
+ /// The aggregator api v2 feature flag is not enabled.
+ const EAGGREGATOR_API_V2_NOT_ENABLED: u64 = 6;
/// The generic type supplied to the aggregator is not supported.
const EUNSUPPORTED_AGGREGATOR_TYPE: u64 = 7;
@@ -39,7 +38,7 @@ module aptos_framework::aggregator_v2 {
/// across multiple transactions. See the module description for more details.
///
/// Currently supported types for Element are u64 and u128.
- struct Aggregator has store {
+ struct Aggregator has store, drop {
value: IntElement,
max_value: IntElement,
}
@@ -90,11 +89,13 @@ module aptos_framework::aggregator_v2 {
}
/// Returns a value stored in this aggregator.
- /// Note: This operation prevents parallelism of the transaction that calls it.
+ /// Note: This operation is resource-intensive, and reduces parallelism.
+ /// (Especially if called in a transaction that also modifies the aggregator,
+ /// or has other read/write conflicts)
public native fun read(aggregator: &Aggregator): IntElement;
/// Returns a wrapper of a current value of an aggregator
- /// Unlike read(), this enables parallel execution of transactions.
+ /// Unlike read(), it is fast and avoids sequential dependencies.
public native fun snapshot(aggregator: &Aggregator): AggregatorSnapshot;
/// Creates a snapshot of a given value.
@@ -105,7 +106,9 @@ module aptos_framework::aggregator_v2 {
public native fun copy_snapshot(snapshot: &AggregatorSnapshot): AggregatorSnapshot;
/// Returns a value stored in this snapshot.
- /// Note: This operation prevents parallelism of the transaction that calls it.
+ /// Note: This operation is resource-intensive, and reduces parallelism.
+ /// (Especially if called in a transaction that also modifies the aggregator,
+ /// or has other read/write conflicts)
public native fun read_snapshot(snapshot: &AggregatorSnapshot): Element;
/// Concatenates `before`, `snapshot` and `after` into a single string.
@@ -114,85 +117,98 @@ module aptos_framework::aggregator_v2 {
/// If length of prefix and suffix together exceed 256 bytes, ECONCAT_STRING_LENGTH_TOO_LARGE is raised.
public native fun string_concat(before: String, snapshot: &AggregatorSnapshot, after: String): AggregatorSnapshot;
- #[test(_fx = @std)]
- public fun test_correct_read(_fx: &signer) {
- // use std::features;
- // let feature = features::get_aggregator_v2_api_feature();
- // features::change_feature_flags(fx, vector[feature], vector[]);
+ #[test]
+ public fun test_aggregator() {
+ let agg = create_aggregator(10);
+ assert!(try_add(&mut agg, 5), 1);
+ assert!(try_add(&mut agg, 5), 2);
+ assert!(read(&agg) == 10, 3);
+ assert!(!try_add(&mut agg, 5), 4);
+ assert!(read(&agg) == 10, 5);
+ assert!(try_sub(&mut agg, 5), 6);
+ assert!(read(&agg) == 5, 7);
+
+ let snap = snapshot(&agg);
+ assert!(try_add(&mut agg, 2), 8);
+ assert!(read(&agg) == 7, 9);
+ assert!(read_snapshot(&snap) == 5, 10);
+ }
+ #[test]
+ public fun test_correct_read() {
let snapshot = create_snapshot(42);
- // copy not yet supported
- // let snapshot2 = copy_snapshot(&snapshot);
assert!(read_snapshot(&snapshot) == 42, 0);
- // assert!(read_snapshot(&snapshot2) == 42, 0);
- }
-
- #[test(_fx = @std)]
- public fun test_correct_read_string(_fx: &signer) {
- // use std::features;
- // let feature = features::get_aggregator_v2_api_feature();
- // features::change_feature_flags(fx, vector[feature], vector[]);
let snapshot = create_snapshot(std::string::utf8(b"42"));
- // copy not yet supported
- // let snapshot2 = copy_snapshot(&snapshot);
assert!(read_snapshot(&snapshot) == std::string::utf8(b"42"), 0);
- // assert!(read_snapshot(&snapshot2) == std::string::utf8(b"42"), 0);
}
- #[test(_fx = @std)]
- public fun test_string_concat1(_fx: &signer) {
- // use std::features;
- // let feature = features::get_aggregator_v2_api_feature();
- // features::change_feature_flags(fx, vector[feature], vector[]);
+ #[test]
+ #[expected_failure(abort_code = 0x030009, location = Self)]
+ public fun test_copy_not_yet_supported() {
+ let snapshot = create_snapshot(42);
+ copy_snapshot(&snapshot);
+ }
+ #[test]
+ public fun test_string_concat1() {
let snapshot = create_snapshot(42);
let snapshot2 = string_concat(std::string::utf8(b"before"), &snapshot, std::string::utf8(b"after"));
assert!(read_snapshot(&snapshot2) == std::string::utf8(b"before42after"), 0);
}
- #[test(_fx = @std)]
+ #[test]
#[expected_failure(abort_code = 0x030005, location = Self)]
- public fun test_string_concat2(_fx: &signer) {
- // use std::features;
- // let feature = features::get_aggregator_v2_api_feature();
- // features::change_feature_flags(fx, vector[feature], vector[]);
-
+ public fun test_string_concat_from_string_not_supported() {
let snapshot = create_snapshot(std::string::utf8(b"42"));
- let snapshot2 = string_concat(std::string::utf8(b"before"), &snapshot, std::string::utf8(b"after"));
- read_snapshot(&snapshot2);
- // String concatenation not supported
- // assert!(read_snapshot(&snapshot2) == std::string::utf8(b"before42after"), 0);
+ string_concat(std::string::utf8(b"before"), &snapshot, std::string::utf8(b"after"));
}
// #[test(fx = @std)]
// #[expected_failure(abort_code = 0x030006, location = Self)]
// public fun test_snapshot_feature_not_enabled(fx: &signer) {
// use std::features;
+ // use aptos_framework::reconfiguration;
// let feature = features::get_aggregator_v2_api_feature();
// features::change_feature_flags(fx, vector[], vector[feature]);
-
+ // reconfiguration::reconfigure_for_test();
// create_snapshot(42);
// }
- #[test(_fx = @std)]
+ // #[test(fx = @std)]
+ // #[expected_failure(abort_code = 0x030006, location = Self)]
+ // public fun test_aggregator_feature_not_enabled(fx: &signer) {
+ // use std::features;
+ // use aptos_framework::reconfiguration;
+ // let feature = features::get_aggregator_v2_api_feature();
+ // features::change_feature_flags(fx, vector[], vector[feature]);
+ // reconfiguration::reconfigure_for_test();
+ // create_aggregator(42);
+ // }
+
+ #[test]
+ #[expected_failure(abort_code = 0x030007, location = Self)]
+ public fun test_aggregator_invalid_type1() {
+ create_unbounded_aggregator();
+ }
+
+ public fun test_aggregator_valid_type() {
+ create_unbounded_aggregator();
+ create_unbounded_aggregator();
+ create_aggregator(5);
+ create_aggregator(5);
+ }
+
+ #[test]
#[expected_failure(abort_code = 0x030005, location = Self)]
- public fun test_snpashot_invalid_type1(_fx: &signer) {
- // use std::features;
+ public fun test_snpashot_invalid_type1() {
use std::option;
- // let feature = features::get_aggregator_v2_api_feature();
- // features::change_feature_flags(fx, vector[feature], vector[]);
-
create_snapshot(option::some(42));
}
- #[test(_fx = @std)]
+ #[test]
#[expected_failure(abort_code = 0x030005, location = Self)]
- public fun test_snpashot_invalid_type2(_fx: &signer) {
- // use std::features;
- // let feature = features::get_aggregator_v2_api_feature();
- // features::change_feature_flags(fx, vector[feature], vector[]);
-
+ public fun test_snpashot_invalid_type2() {
create_snapshot(vector[42]);
}
}
diff --git a/aptos-move/framework/src/natives/aggregator_natives/aggregator_v2.rs b/aptos-move/framework/src/natives/aggregator_natives/aggregator_v2.rs
index 3b63ab2b00c27..5d1730026658b 100644
--- a/aptos-move/framework/src/natives/aggregator_natives/aggregator_v2.rs
+++ b/aptos-move/framework/src/natives/aggregator_natives/aggregator_v2.rs
@@ -244,6 +244,16 @@ impl BoundedMath {
// ================= END TEMPORARY CODE =================
+macro_rules! abort_if_not_enabled {
+ ($context:expr) => {
+ if !$context.aggregator_v2_api_enabled() {
+ return Err(SafeNativeError::Abort {
+ abort_code: EAGGREGATOR_API_NOT_ENABLED,
+ });
+ }
+ };
+}
+
/***************************************************************************************************
* native fun create_aggregator(max_value: Element): Aggregator;
**************************************************************************************************/
@@ -253,11 +263,7 @@ fn native_create_aggregator(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
- if !context.aggregator_v2_api_enabled() {
- return Err(SafeNativeError::Abort {
- abort_code: EAGGREGATOR_API_NOT_ENABLED,
- });
- }
+ abort_if_not_enabled!(context);
debug_assert_eq!(args.len(), 1);
@@ -281,11 +287,7 @@ fn native_create_unbounded_aggregator(
ty_args: Vec,
args: VecDeque,
) -> SafeNativeResult> {
- if !context.aggregator_v2_api_enabled() {
- return Err(SafeNativeError::Abort {
- abort_code: EAGGREGATOR_API_NOT_ENABLED,
- });
- }
+ abort_if_not_enabled!(context);
debug_assert_eq!(args.len(), 0);
@@ -318,6 +320,8 @@ fn native_try_add(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
+ abort_if_not_enabled!(context);
+
debug_assert_eq!(args.len(), 2);
context.charge(AGGREGATOR_V2_TRY_ADD_BASE)?;
@@ -347,6 +351,8 @@ fn native_try_sub(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
+ abort_if_not_enabled!(context);
+
debug_assert_eq!(args.len(), 2);
context.charge(AGGREGATOR_V2_TRY_SUB_BASE)?;
@@ -376,6 +382,8 @@ fn native_read(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
+ abort_if_not_enabled!(context);
+
debug_assert_eq!(args.len(), 1);
context.charge(AGGREGATOR_V2_READ_BASE)?;
@@ -401,6 +409,8 @@ fn native_snapshot(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
+ abort_if_not_enabled!(context);
+
debug_assert_eq!(args.len(), 1);
context.charge(AGGREGATOR_V2_SNAPSHOT_BASE)?;
@@ -423,11 +433,7 @@ fn native_create_snapshot(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
- if !context.aggregator_v2_api_enabled() {
- return Err(SafeNativeError::Abort {
- abort_code: EAGGREGATOR_API_NOT_ENABLED,
- });
- }
+ abort_if_not_enabled!(context);
debug_assert_eq!(ty_args.len(), 1);
debug_assert_eq!(args.len(), 1);
@@ -448,10 +454,11 @@ fn native_create_snapshot(
**************************************************************************************************/
fn native_copy_snapshot(
- _context: &mut SafeNativeContext,
+ context: &mut SafeNativeContext,
_ty_args: Vec,
_args: VecDeque,
) -> SafeNativeResult> {
+ abort_if_not_enabled!(context);
Err(SafeNativeError::Abort {
abort_code: EAGGREGATOR_FUNCTION_NOT_YET_SUPPORTED,
})
@@ -479,6 +486,8 @@ fn native_read_snapshot(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
+ abort_if_not_enabled!(context);
+
debug_assert_eq!(ty_args.len(), 1);
debug_assert_eq!(args.len(), 1);
context.charge(AGGREGATOR_V2_READ_SNAPSHOT_BASE)?;
@@ -502,6 +511,8 @@ fn native_string_concat(
ty_args: Vec,
mut args: VecDeque,
) -> SafeNativeResult> {
+ abort_if_not_enabled!(context);
+
debug_assert_eq!(ty_args.len(), 1);
debug_assert_eq!(args.len(), 3);
context.charge(AGGREGATOR_V2_STRING_CONCAT_BASE)?;