Skip to content

Commit 5adec6c

Browse files
committed
[framework] Add native function to check protocol feature flags in Move
Adds sui::protocol_config::is_feature_enabled() to allow Move code to query protocol feature flags at runtime. This enables conditional logic and fallbacks based on protocol version capabilities.
1 parent b7d058b commit 5adec6c

File tree

8 files changed

+246
-14
lines changed

8 files changed

+246
-14
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
title: Module `sui::protocol_config`
3+
---
4+
5+
This module provides access to protocol configuration feature flags.
6+
Feature flags control the availability of various protocol features and
7+
are enabled/disabled at specific protocol versions during epoch changes.
8+
9+
10+
- [Function `is_feature_enabled`](#sui_protocol_config_is_feature_enabled)
11+
- [Arguments](#@Arguments_0)
12+
- [Returns](#@Returns_1)
13+
- [Aborts](#@Aborts_2)
14+
- [Example (for framework use only)](#@Example_(for_framework_use_only)_3)
15+
16+
17+
<pre><code></code></pre>
18+
19+
20+
21+
<a name="sui_protocol_config_is_feature_enabled"></a>
22+
23+
## Function `is_feature_enabled`
24+
25+
Checks if a specific protocol feature flag is enabled.
26+
27+
Restricted to internal use within the sui-framework package only.
28+
If we need to use it in sui-system, we can add friend declarations.
29+
We should never need to expose this to user packages.
30+
31+
32+
<a name="@Arguments_0"></a>
33+
34+
### Arguments
35+
36+
* <code>feature_flag_name</code> - The name of the feature flag as bytes (e.g., b"enable_vdf")
37+
38+
39+
<a name="@Returns_1"></a>
40+
41+
### Returns
42+
43+
* <code><b>true</b></code> if the feature is enabled in the current protocol version
44+
* <code><b>false</b></code> if the feature is disabled
45+
46+
47+
<a name="@Aborts_2"></a>
48+
49+
### Aborts
50+
51+
* Error code 0: Feature flag name not found
52+
* Error code 1: Invalid feature flag name (not valid UTF-8)
53+
54+
55+
<a name="@Example_(for_framework_use_only)_3"></a>
56+
57+
### Example (for framework use only)
58+
59+
```move
60+
use sui::protocol_config;
61+
62+
if (protocol_config::is_feature_enabled(b"enable_accumulators")) {
63+
// Accumulators are available
64+
};
65+
```
66+
67+
68+
<pre><code><b>public</b>(<a href="../sui/package.md#sui_package">package</a>) <b>fun</b> <a href="../sui/protocol_config.md#sui_protocol_config_is_feature_enabled">is_feature_enabled</a>(feature_flag_name: vector&lt;u8&gt;): bool
69+
</code></pre>
70+
71+
72+
73+
<details>
74+
<summary>Implementation</summary>
75+
76+
77+
<pre><code><b>public</b>(<a href="../sui/package.md#sui_package">package</a>) <b>native</b> <b>fun</b> <a href="../sui/protocol_config.md#sui_protocol_config_is_feature_enabled">is_feature_enabled</a>(feature_flag_name: vector&lt;u8&gt;): bool;
78+
</code></pre>
79+
80+
81+
82+
</details>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/// This module provides access to protocol configuration feature flags.
5+
/// Feature flags control the availability of various protocol features and
6+
/// are enabled/disabled at specific protocol versions during epoch changes.
7+
8+
module sui::protocol_config;
9+
10+
/// Checks if a specific protocol feature flag is enabled.
11+
///
12+
/// Restricted to internal use within the sui-framework package only.
13+
/// If we need to use it in sui-system, we can add friend declarations.
14+
/// We should never need to expose this to user packages.
15+
///
16+
/// # Arguments
17+
/// * `feature_flag_name` - The name of the feature flag as bytes (e.g., b"enable_vdf")
18+
///
19+
/// # Returns
20+
/// * `true` if the feature is enabled in the current protocol version
21+
/// * `false` if the feature is disabled
22+
///
23+
/// # Aborts
24+
/// * Error code 0: Feature flag name not found
25+
/// * Error code 1: Invalid feature flag name (not valid UTF-8)
26+
///
27+
/// # Example (for framework use only)
28+
/// ```move
29+
/// use sui::protocol_config;
30+
///
31+
/// if (protocol_config::is_feature_enabled(b"enable_accumulators")) {
32+
/// // Accumulators are available
33+
/// };
34+
/// ```
35+
public(package) native fun is_feature_enabled(feature_flag_name: vector<u8>): bool;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#[test_only]
5+
module sui::protocol_config_tests;
6+
7+
use sui::protocol_config;
8+
9+
#[test]
10+
fun test_is_feature_enabled_true() {
11+
let is_enabled = protocol_config::is_feature_enabled(b"advance_epoch_start_time_in_safe_mode");
12+
assert!(is_enabled, 1);
13+
}
14+
15+
#[test]
16+
fun test_is_feature_enabled_false() {
17+
let is_enabled = protocol_config::is_feature_enabled(b"per_command_shared_object_transfer_rules");
18+
assert!(!is_enabled, 1);
19+
}
20+
21+
#[test]
22+
#[expected_failure(abort_code = 0)]
23+
fun test_is_feature_enabled_invalid_flag() {
24+
protocol_config::is_feature_enabled(b"nonexistent_feature_flag_xyz");
25+
}
26+
27+
#[test]
28+
#[expected_failure(abort_code = 1)]
29+
fun test_is_feature_enabled_invalid_utf8() {
30+
// Invalid UTF-8 sequence: 0xFF is not valid UTF-8
31+
let invalid_utf8 = vector[0xFF, 0xFE, 0xFD];
32+
protocol_config::is_feature_enabled(invalid_utf8);
33+
}
112 Bytes
Binary file not shown.

crates/sui-framework/published_api.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,9 @@ ecvrf_verify
11051105
ed25519_verify
11061106
public fun
11071107
0x2::ed25519
1108+
is_feature_enabled
1109+
public(package) fun
1110+
0x2::protocol_config
11081111
hash_to_input
11091112
public fun
11101113
0x2::vdf

crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -240,56 +240,56 @@ validators:
240240
next_epoch_worker_address: ~
241241
extra_fields:
242242
id:
243-
id: "0x41bab75a4999f01b96b0e9fb86616407b1646162329127308bfb83e0bbaf2269"
243+
id: "0xe586b6abfde382b212d7bb0d78d45b70562bdb8f1883071f64fc577e716f7214"
244244
size: 0
245245
voting_power: 10000
246-
operation_cap_id: "0xd6f39968901e8a440fe11b4fa7626052c331ee64c54a099b469d6d8250bd826e"
246+
operation_cap_id: "0x139deaadf4e4bf4769d853331effb400aaec7e5b2818d976f7615226a2cc308d"
247247
gas_price: 1000
248248
staking_pool:
249-
id: "0x5926424fffede15fe80ebfa20ed336c74c98aa9f2ce28f6662530a0c0fd9c02f"
249+
id: "0xe79577e79af111ccdb76458829a10c0e0387f96dfc6c323782b9afb92eb20d1a"
250250
activation_epoch: 0
251251
deactivation_epoch: ~
252252
sui_balance: 20000000000000000
253253
rewards_pool:
254254
value: 0
255255
pool_token_balance: 20000000000000000
256256
exchange_rates:
257-
id: "0x8e2de2713792615d0245ec30a7784eb0d271dab1a3eb8c0a92829c73fcf6e611"
257+
id: "0x6da1faacc8677779acc23963231521e5f99045d67b4ee430f6539d99575f0870"
258258
size: 1
259259
pending_stake: 0
260260
pending_total_sui_withdraw: 0
261261
pending_pool_token_withdraw: 0
262262
extra_fields:
263263
id:
264-
id: "0xf05055bdd05ab954963fababc162bfe50164423a3b09cb0b0ad85fcaed9920a2"
264+
id: "0x0d2e2085eb00b1c59760cbe145dbca6cb8f11b227c3c515b1e1230e8823e633c"
265265
size: 0
266266
commission_rate: 200
267267
next_epoch_stake: 20000000000000000
268268
next_epoch_gas_price: 1000
269269
next_epoch_commission_rate: 200
270270
extra_fields:
271271
id:
272-
id: "0x6ee5e1b2fd2fc0b9bc90f85df23b81247131efd1300c1ff31f7cddfe088c5c2d"
272+
id: "0x9e054c847b1ddb131176bc330fb9b281e8f63dcbaa761ea5abcd95f1f1a239b0"
273273
size: 0
274274
pending_active_validators:
275275
contents:
276-
id: "0xfc993d4f6b2c43a15c50d27265b3fbe102f98f3e880096fda2651249d7cfc28c"
276+
id: "0x353861467da8403c047e00ae3ebbe1a5150915dfb053dc719bd2add26bd44037"
277277
size: 0
278278
pending_removals: []
279279
staking_pool_mappings:
280-
id: "0x6b11d537051c1260bb91f25479ebbe52a57e30f2a4f9d17d77e76c9b96693cc9"
280+
id: "0x3c358cd059540d76fe26eb1cfc029dfd18ef4999e38734ccf569c98bd057e9d5"
281281
size: 1
282282
inactive_validators:
283-
id: "0xf672cf94515c0e99a207ccab019d5e7cd78310fe10a2bd6bca524c5fe69c5849"
283+
id: "0x1607c6d0db9e1df642ad5dd79da6eda562569567f8a52dab4252908ae24dc9df"
284284
size: 0
285285
validator_candidates:
286-
id: "0x87d2ae9a8cf6642d06d1b9943f6179c2dd06cc3b58da17a7d14c593edeb07f1d"
286+
id: "0xf1f99fcb0baa4a75ebff512b15626af3aed3cbefd9af96e23246babd02987b41"
287287
size: 0
288288
at_risk_validators:
289289
contents: []
290290
extra_fields:
291291
id:
292-
id: "0x8e7dfbedf7295c65affc16a595cf3d7c518f789e5b53f79d6114e4d17cf0ff7c"
292+
id: "0xe1d775c17b894320dc362d0d7b0a944bce81b0b2a1e2101af60998296088c276"
293293
size: 0
294294
storage_fund:
295295
total_object_storage_rebates:
@@ -306,7 +306,7 @@ parameters:
306306
validator_low_stake_grace_period: 7
307307
extra_fields:
308308
id:
309-
id: "0x293b3b5f09e713d5c5ee4b243cbd25e9a7e96010760ea54399f7a602684deacf"
309+
id: "0xcb4dc2489fade60fe357ebcf45cd42fddb2c89f96d2ca2c7fbbd2f118e40ad8e"
310310
size: 0
311311
reference_gas_price: 1000
312312
validator_report_records:
@@ -320,7 +320,7 @@ stake_subsidy:
320320
stake_subsidy_decrease_rate: 1000
321321
extra_fields:
322322
id:
323-
id: "0x536eae7cd37967730e534a0e3a14e24b4cab4e01d4739061c4dd1d72588e4c5d"
323+
id: "0x57d6b0759dce2c8cd55dcad9409cde4b987eb74af99bbbf6a8286b1df5fad43c"
324324
size: 0
325325
safe_mode: false
326326
safe_mode_storage_rewards:
@@ -332,5 +332,5 @@ safe_mode_non_refundable_storage_fee: 0
332332
epoch_start_timestamp_ms: 10
333333
extra_fields:
334334
id:
335-
id: "0x2bcf2f212c4f178fc6972629ebfdb650235a65581b362982faa159a27b07e4c3"
335+
id: "0xc9dbad8a26614a903a8ae92f5fbb6c37d8e5d653c44edd2f8109e8b4afdc39cc"
336336
size: 0

sui-execution/latest/sui-move-natives/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub mod event;
8282
mod funds_accumulator;
8383
mod object;
8484
pub mod object_runtime;
85+
mod protocol_config;
8586
mod random;
8687
pub mod test_scenario;
8788
mod test_utils;
@@ -1245,6 +1246,11 @@ pub fn all_natives(silent: bool, protocol_config: &ProtocolConfig) -> NativeFunc
12451246
"poseidon_bn254_internal",
12461247
make_native!(poseidon::poseidon_bn254_internal),
12471248
),
1249+
(
1250+
"protocol_config",
1251+
"is_feature_enabled",
1252+
make_native!(protocol_config::is_feature_enabled),
1253+
),
12481254
(
12491255
"vdf",
12501256
"vdf_verify_internal",
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::get_extension;
5+
use crate::object_runtime::ObjectRuntime;
6+
use move_binary_format::errors::PartialVMResult;
7+
use move_vm_runtime::native_functions::NativeContext;
8+
use move_vm_types::{
9+
loaded_data::runtime_types::Type,
10+
natives::function::NativeResult,
11+
pop_arg,
12+
values::{Value, VectorRef},
13+
};
14+
use smallvec::smallvec;
15+
use std::collections::VecDeque;
16+
17+
pub const FEATURE_FLAG_NOT_FOUND_ERROR: u64 = 0;
18+
pub const INVALID_FEATURE_FLAG_NAME_ERROR: u64 = 1;
19+
20+
/***************************************************************************************************
21+
* native fun is_feature_enabled
22+
*
23+
* Implementation of the Move native function `protocol_config::is_feature_enabled(
24+
* feature_flag_name: vector<u8>): bool`
25+
*
26+
* Checks if a protocol feature flag is enabled in the current protocol version.
27+
* Takes raw bytes for simplicity since this is internal framework use only.
28+
*
29+
* Gas cost: 0 (zero cost for framework-internal use)
30+
**************************************************************************************************/
31+
pub fn is_feature_enabled(
32+
context: &mut NativeContext,
33+
ty_args: Vec<Type>,
34+
mut args: VecDeque<Value>,
35+
) -> PartialVMResult<NativeResult> {
36+
debug_assert!(ty_args.is_empty());
37+
debug_assert!(args.len() == 1);
38+
39+
// Get the bytes directly - Move passes raw vector<u8>
40+
let feature_flag_name_bytes = pop_arg!(args, VectorRef);
41+
let bytes = feature_flag_name_bytes.as_bytes_ref();
42+
43+
// Validate that the feature flag name is valid UTF-8
44+
let feature_flag_name = match String::from_utf8(bytes.to_vec()) {
45+
Ok(s) => s,
46+
Err(_) => {
47+
// Invalid UTF-8 in feature flag name - abort with error
48+
return Ok(NativeResult::err(
49+
context.gas_used(),
50+
INVALID_FEATURE_FLAG_NAME_ERROR,
51+
));
52+
}
53+
};
54+
55+
let protocol_config = &get_extension!(context, ObjectRuntime)?.protocol_config;
56+
57+
// Use the auto-generated lookup_feature method to find the feature flag
58+
let is_enabled = match protocol_config.lookup_feature(feature_flag_name) {
59+
Some(value) => value,
60+
None => {
61+
// Feature flag not found - abort with error
62+
return Ok(NativeResult::err(
63+
context.gas_used(),
64+
FEATURE_FLAG_NOT_FOUND_ERROR,
65+
));
66+
}
67+
};
68+
69+
Ok(NativeResult::ok(
70+
context.gas_used(),
71+
smallvec![Value::bool(is_enabled)],
72+
))
73+
}

0 commit comments

Comments
 (0)