Skip to content

Commit 4f0548d

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 4f0548d

File tree

10 files changed

+220
-14
lines changed

10 files changed

+220
-14
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
- [Example (for framework use only)](#@Example_(for_framework_use_only)_2)
14+
15+
16+
<pre><code></code></pre>
17+
18+
19+
20+
<a name="sui_protocol_config_is_feature_enabled"></a>
21+
22+
## Function `is_feature_enabled`
23+
24+
Checks if a specific protocol feature flag is enabled.
25+
26+
Restricted to internal use within the sui-framework package only.
27+
If we need to use it in sui-system, we can add friend declarations.
28+
We should never need to expose this to user packages.
29+
30+
31+
<a name="@Arguments_0"></a>
32+
33+
### Arguments
34+
35+
* <code>feature_flag_name</code> - The name of the feature flag as bytes (e.g., b"enable_vdf")
36+
- It is expected to be a valid UTF-8 string
37+
- The flag should exist in the protocol config
38+
39+
40+
<a name="@Returns_1"></a>
41+
42+
### Returns
43+
44+
* <code><b>true</b></code> if the feature is enabled in the current protocol version
45+
* <code><b>false</b></code> if the feature is disabled
46+
47+
48+
<a name="@Example_(for_framework_use_only)_2"></a>
49+
50+
### Example (for framework use only)
51+
52+
```move
53+
use sui::protocol_config;
54+
55+
if (protocol_config::is_feature_enabled(b"enable_accumulators")) {
56+
// Accumulators are available
57+
};
58+
```
59+
60+
61+
<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
62+
</code></pre>
63+
64+
65+
66+
<details>
67+
<summary>Implementation</summary>
68+
69+
70+
<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;
71+
</code></pre>
72+
73+
74+
75+
</details>
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+
/// 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+
/// - It is expected to be a valid UTF-8 string
19+
/// - The flag should exist in the protocol config
20+
///
21+
/// # Returns
22+
/// * `true` if the feature is enabled in the current protocol version
23+
/// * `false` if the feature is disabled
24+
///
25+
/// # Example (for framework use only)
26+
/// ```move
27+
/// use sui::protocol_config;
28+
///
29+
/// if (protocol_config::is_feature_enabled(b"enable_accumulators")) {
30+
/// // Accumulators are available
31+
/// };
32+
/// ```
33+
public(package) native fun is_feature_enabled(feature_flag_name: vector<u8>): bool;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
}
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

crates/sui/tests/snapshots/shell_tests__summaries__summarize_json.sh.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ party.json
7878
pay.json
7979
poseidon.json
8080
priority_queue.json
81+
protocol_config.json
8182
prover.json
8283
random.json
8384
sui.json

crates/sui/tests/snapshots/shell_tests__with_network__summaries__summarize_yaml.sh.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ party.yaml
6666
pay.yaml
6767
poseidon.yaml
6868
priority_queue.yaml
69+
protocol_config.yaml
6970
prover.yaml
7071
random.yaml
7172
sui.yaml

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: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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::{PartialVMError, PartialVMResult};
7+
use move_core_types::vm_status::StatusCode;
8+
use move_vm_runtime::native_functions::NativeContext;
9+
use move_vm_types::{
10+
loaded_data::runtime_types::Type,
11+
natives::function::NativeResult,
12+
pop_arg,
13+
values::{Value, Vector},
14+
};
15+
use smallvec::smallvec;
16+
use std::collections::VecDeque;
17+
18+
/***************************************************************************************************
19+
* native fun is_feature_enabled
20+
*
21+
* Implementation of the Move native function `protocol_config::is_feature_enabled(
22+
* feature_flag_name: vector<u8>): bool`
23+
*
24+
* Checks if a protocol feature flag is enabled in the current protocol version.
25+
*
26+
* Gas cost: 0 (zero cost for framework-internal use)
27+
**************************************************************************************************/
28+
pub fn is_feature_enabled(
29+
context: &mut NativeContext,
30+
ty_args: Vec<Type>,
31+
mut args: VecDeque<Value>,
32+
) -> PartialVMResult<NativeResult> {
33+
debug_assert!(ty_args.is_empty());
34+
debug_assert!(args.len() == 1);
35+
36+
let feature_flag_name_bytes = pop_arg!(args, Vector);
37+
let bytes = feature_flag_name_bytes.to_vec_u8()?;
38+
39+
let feature_flag_name = match String::from_utf8(bytes.to_vec()) {
40+
Ok(s) => s,
41+
Err(_) => {
42+
debug_assert!(false);
43+
return Err(
44+
PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
45+
.with_message("Feature flag name is not a valid UTF-8 string".to_owned()),
46+
);
47+
}
48+
};
49+
50+
let protocol_config = &get_extension!(context, ObjectRuntime)?.protocol_config;
51+
52+
// Use the auto-generated lookup_feature method to find the feature flag
53+
let is_enabled = match protocol_config.lookup_feature(feature_flag_name) {
54+
Some(value) => value,
55+
None => {
56+
debug_assert!(false);
57+
return Err(
58+
PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
59+
.with_message("Feature flag not found".to_owned()),
60+
);
61+
}
62+
};
63+
64+
Ok(NativeResult::ok(
65+
context.gas_used(),
66+
smallvec![Value::bool(is_enabled)],
67+
))
68+
}

0 commit comments

Comments
 (0)