From af92caeca5236aa6bbc05d4c0eb548cad41b64d2 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Fri, 28 Jun 2024 08:24:20 -0700 Subject: [PATCH] [config] Added ability to remove settings (#18434) ## Description - Added the ability to remove settings and unset settings for a current epoch ## Test plan - Added new tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --- .../docs/sui-framework/config.md | 102 +++++++- .../docs/sui-framework/deny_list.md | 4 +- .../sui-framework/sources/config.move | 81 +++++- .../sui-framework/sources/deny_list.move | 4 +- .../sui-framework/tests/config_tests.move | 247 +++++++++++++++++- crates/sui-framework/published_api.txt | 3 + ..._populated_genesis_snapshot_matches-2.snap | 28 +- crates/sui-types/src/config.rs | 4 +- .../latest/sui-move-natives/src/config.rs | 19 +- 9 files changed, 428 insertions(+), 64 deletions(-) diff --git a/crates/sui-framework/docs/sui-framework/config.md b/crates/sui-framework/docs/sui-framework/config.md index 16fedebe3194ad..4f06b6c9e4b6a9 100644 --- a/crates/sui-framework/docs/sui-framework/config.md +++ b/crates/sui-framework/docs/sui-framework/config.md @@ -11,6 +11,7 @@ title: Module `0x2::config` - [Function `new`](#0x2_config_new) - [Function `create`](#0x2_config_create) - [Function `add_for_current_epoch`](#0x2_config_add_for_current_epoch) +- [Function `remove_for_current_epoch`](#0x2_config_remove_for_current_epoch) - [Function `exists_with_type`](#0x2_config_exists_with_type) - [Function `exists_with_type_for_current_epoch`](#0x2_config_exists_with_type_for_current_epoch) - [Function `borrow_for_current_epoch_mut`](#0x2_config_borrow_for_current_epoch_mut) @@ -61,7 +62,7 @@ title: Module `0x2::config` -
struct Setting<Value: copy, drop, store> has store
+
struct Setting<Value: copy, drop, store> has drop, store
 
@@ -88,7 +89,7 @@ title: Module `0x2::config` -
struct SettingData<Value: copy, drop, store> has store
+
struct SettingData<Value: copy, drop, store> has drop, store
 
@@ -105,7 +106,7 @@ title: Module `0x2::config`
-newer_value: Value +newer_value: option::Option<Value>
@@ -207,7 +208,7 @@ title: Module `0x2::config` -
public(friend) fun add_for_current_epoch<WriteCap, Name: copy, drop, store, Value: copy, drop, store>(config: &mut config::Config<WriteCap>, _cap: &mut WriteCap, name: Name, value: Value, _ctx: &mut tx_context::TxContext): option::Option<Value>
+
public(friend) fun add_for_current_epoch<WriteCap, Name: copy, drop, store, Value: copy, drop, store>(config: &mut config::Config<WriteCap>, _cap: &mut WriteCap, name: Name, value: Value, ctx: &mut tx_context::TxContext): option::Option<Value>
 
@@ -225,14 +226,14 @@ title: Module `0x2::config` _cap: &mut WriteCap, name: Name, value: Value, - _ctx: &mut TxContext, + ctx: &mut TxContext, ): Option<Value> { - let epoch = _ctx.epoch(); + let epoch = ctx.epoch(); if (!field::exists_(&config.id, name)) { let sobj = Setting { data: option::some(SettingData { newer_value_epoch: epoch, - newer_value: value, + newer_value: option::some(value), older_value_opt: option::none(), }), }; @@ -245,19 +246,88 @@ title: Module `0x2::config` newer_value, older_value_opt, } = sobj.data.extract(); - assert!(epoch > newer_value_epoch, EAlreadySetForEpoch); + let (older_value_opt, removed_value) = + if (epoch > newer_value_epoch) { + // if the `newer_value` is for a previous epoch, move it to `older_value_opt` + (move newer_value, move older_value_opt) + } else { + // the current epoch cannot be less than the `newer_value_epoch` + assert!(epoch == newer_value_epoch); + // if the `newer_value` is for the current epoch, then the option must be `none` + assert!(newer_value.is_none(), EAlreadySetForEpoch); + (move older_value_opt, option::none()) + }; sobj.data.fill(SettingData { newer_value_epoch: epoch, - newer_value: value, - older_value_opt: option::some(newer_value), + newer_value: option::some(value), + older_value_opt, }); - older_value_opt + removed_value } }
+ + + + +## Function `remove_for_current_epoch` + + + +
public(friend) fun remove_for_current_epoch<WriteCap, Name: copy, drop, store, Value: copy, drop, store>(config: &mut config::Config<WriteCap>, _cap: &mut WriteCap, name: Name, ctx: &mut tx_context::TxContext): option::Option<Value>
+
+ + + +
+Implementation + + +
public(package) fun remove_for_current_epoch<
+    WriteCap,
+    Name: copy + drop + store,
+    Value: copy + drop + store,
+>(
+    config: &mut Config<WriteCap>,
+    _cap: &mut WriteCap,
+    name: Name,
+    ctx: &mut TxContext,
+): Option<Value> {
+    let epoch = ctx.epoch();
+    if (!field::exists_(&config.id, name)) return option::none();
+    let sobj: &mut Setting<Value> = field::borrow_mut(&mut config.id, name);
+    let SettingData {
+        newer_value_epoch,
+        newer_value,
+        older_value_opt,
+    } = sobj.data.extract();
+    let (older_value_opt, removed_value) =
+        if (epoch > newer_value_epoch) {
+            // if the `newer_value` is for a previous epoch, move it to `older_value_opt`
+            (move newer_value, option::none())
+        } else {
+            // the current epoch cannot be less than the `newer_value_epoch`
+            assert!(epoch == newer_value_epoch);
+            (move older_value_opt, move newer_value)
+        };
+    let older_value_opt_is_none = older_value_opt.is_none();
+    sobj.data.fill(SettingData {
+        newer_value_epoch: epoch,
+        newer_value: option::none(),
+        older_value_opt,
+    });
+    if (older_value_opt_is_none) {
+        field::remove<_, Setting<Value>>(&mut config.id, name);
+    };
+    removed_value
+}
+
+ + +
@@ -318,7 +388,8 @@ title: Module `0x2::config` field::exists_with_type<_, Setting<Value>>(&config.id, name) && { let epoch = ctx.epoch(); let sobj: &Setting<Value> = field::borrow(&config.id, name); - epoch == sobj.data.borrow().newer_value_epoch + epoch == sobj.data.borrow().newer_value_epoch && + sobj.data.borrow().newer_value.is_some() } }
@@ -356,7 +427,8 @@ title: Module `0x2::config` let sobj: &mut Setting<Value> = field::borrow_mut(&mut config.id, name); let data = sobj.data.borrow_mut(); assert!(data.newer_value_epoch == epoch, ENotSetForEpoch); - &mut data.newer_value + assert!(data.newer_value.is_some(), ENotSetForEpoch); + data.newer_value.borrow_mut() }
@@ -388,7 +460,9 @@ title: Module `0x2::config` name: Name, ): &Value { let sobj: &Setting<Value> = field::borrow(&config.id, name); - &sobj.data.borrow().newer_value + let data = sobj.data.borrow(); + assert!(data.newer_value.is_some(), ENotSetForEpoch); + data.newer_value.borrow() } diff --git a/crates/sui-framework/docs/sui-framework/deny_list.md b/crates/sui-framework/docs/sui-framework/deny_list.md index 95f690ab849f91..22e9510d415252 100644 --- a/crates/sui-framework/docs/sui-framework/deny_list.md +++ b/crates/sui-framework/docs/sui-framework/deny_list.md @@ -393,13 +393,11 @@ meaningless to add them to the deny list. ) { let per_type_config = deny_list.per_type_config_entry!(per_type_index, per_type_key, ctx); let setting_name = AddressKey(addr); - let next_epoch_entry = per_type_config.entry!<_, AddressKey, bool>( + per_type_config.remove_for_current_epoch<_, AddressKey, bool>( &mut ConfigWriteCap(), setting_name, - |_deny_list, _cap, _ctx| false, ctx, ); - *next_epoch_entry = false; } diff --git a/crates/sui-framework/packages/sui-framework/sources/config.move b/crates/sui-framework/packages/sui-framework/sources/config.move index 2b7fe323655770..d59101940098e1 100644 --- a/crates/sui-framework/packages/sui-framework/sources/config.move +++ b/crates/sui-framework/packages/sui-framework/sources/config.move @@ -24,13 +24,13 @@ module sui::config { id: UID, } - public struct Setting has store { + public struct Setting has store, drop { data: Option>, } - public struct SettingData has store { + public struct SettingData has store, drop { newer_value_epoch: u64, - newer_value: Value, + newer_value: Option, older_value_opt: Option, } @@ -43,6 +43,7 @@ module sui::config { transfer::share_object(new(cap, ctx)) } + #[allow(unused_mut_parameter)] public(package) fun add_for_current_epoch< WriteCap, Name: copy + drop + store, @@ -52,14 +53,14 @@ module sui::config { _cap: &mut WriteCap, name: Name, value: Value, - _ctx: &mut TxContext, + ctx: &mut TxContext, ): Option { - let epoch = _ctx.epoch(); + let epoch = ctx.epoch(); if (!field::exists_(&config.id, name)) { let sobj = Setting { data: option::some(SettingData { newer_value_epoch: epoch, - newer_value: value, + newer_value: option::some(value), older_value_opt: option::none(), }), }; @@ -72,16 +73,66 @@ module sui::config { newer_value, older_value_opt, } = sobj.data.extract(); - assert!(epoch > newer_value_epoch, EAlreadySetForEpoch); + let (older_value_opt, removed_value) = + if (epoch > newer_value_epoch) { + // if the `newer_value` is for a previous epoch, move it to `older_value_opt` + (move newer_value, move older_value_opt) + } else { + // the current epoch cannot be less than the `newer_value_epoch` + assert!(epoch == newer_value_epoch); + // if the `newer_value` is for the current epoch, then the option must be `none` + assert!(newer_value.is_none(), EAlreadySetForEpoch); + (move older_value_opt, option::none()) + }; sobj.data.fill(SettingData { newer_value_epoch: epoch, - newer_value: value, - older_value_opt: option::some(newer_value), + newer_value: option::some(value), + older_value_opt, }); - older_value_opt + removed_value } } + #[allow(unused_mut_parameter)] + public(package) fun remove_for_current_epoch< + WriteCap, + Name: copy + drop + store, + Value: copy + drop + store, + >( + config: &mut Config, + _cap: &mut WriteCap, + name: Name, + ctx: &mut TxContext, + ): Option { + let epoch = ctx.epoch(); + if (!field::exists_(&config.id, name)) return option::none(); + let sobj: &mut Setting = field::borrow_mut(&mut config.id, name); + let SettingData { + newer_value_epoch, + newer_value, + older_value_opt, + } = sobj.data.extract(); + let (older_value_opt, removed_value) = + if (epoch > newer_value_epoch) { + // if the `newer_value` is for a previous epoch, move it to `older_value_opt` + (move newer_value, option::none()) + } else { + // the current epoch cannot be less than the `newer_value_epoch` + assert!(epoch == newer_value_epoch); + (move older_value_opt, move newer_value) + }; + let older_value_opt_is_none = older_value_opt.is_none(); + sobj.data.fill(SettingData { + newer_value_epoch: epoch, + newer_value: option::none(), + older_value_opt, + }); + if (older_value_opt_is_none) { + field::remove<_, Setting>(&mut config.id, name); + }; + removed_value + } + public(package) fun exists_with_type< WriteCap, Name: copy + drop + store, @@ -106,7 +157,8 @@ module sui::config { field::exists_with_type<_, Setting>(&config.id, name) && { let epoch = ctx.epoch(); let sobj: &Setting = field::borrow(&config.id, name); - epoch == sobj.data.borrow().newer_value_epoch + epoch == sobj.data.borrow().newer_value_epoch && + sobj.data.borrow().newer_value.is_some() } } @@ -125,7 +177,8 @@ module sui::config { let sobj: &mut Setting = field::borrow_mut(&mut config.id, name); let data = sobj.data.borrow_mut(); assert!(data.newer_value_epoch == epoch, ENotSetForEpoch); - &mut data.newer_value + assert!(data.newer_value.is_some(), ENotSetForEpoch); + data.newer_value.borrow_mut() } public(package) fun borrow_most_recent< @@ -137,7 +190,9 @@ module sui::config { name: Name, ): &Value { let sobj: &Setting = field::borrow(&config.id, name); - &sobj.data.borrow().newer_value + let data = sobj.data.borrow(); + assert!(data.newer_value.is_some(), ENotSetForEpoch); + data.newer_value.borrow() } public(package) macro fun entry< diff --git a/crates/sui-framework/packages/sui-framework/sources/deny_list.move b/crates/sui-framework/packages/sui-framework/sources/deny_list.move index 04fd6d6c0cd04d..c15b45532aa900 100644 --- a/crates/sui-framework/packages/sui-framework/sources/deny_list.move +++ b/crates/sui-framework/packages/sui-framework/sources/deny_list.move @@ -98,13 +98,11 @@ module sui::deny_list { ) { let per_type_config = deny_list.per_type_config_entry!(per_type_index, per_type_key, ctx); let setting_name = AddressKey(addr); - let next_epoch_entry = per_type_config.entry!<_, AddressKey, bool>( + per_type_config.remove_for_current_epoch<_, AddressKey, bool>( &mut ConfigWriteCap(), setting_name, - |_deny_list, _cap, _ctx| false, ctx, ); - *next_epoch_entry = false; } public(package) fun v2_most_recent_contains( diff --git a/crates/sui-framework/packages/sui-framework/tests/config_tests.move b/crates/sui-framework/packages/sui-framework/tests/config_tests.move index 3a443ea0eaff8f..cd6dc188e89296 100644 --- a/crates/sui-framework/packages/sui-framework/tests/config_tests.move +++ b/crates/sui-framework/packages/sui-framework/tests/config_tests.move @@ -6,6 +6,7 @@ module sui::config_tests { use sui::config::{Self, Config}; use sui::test_scenario as ts; + use std::unit_test::assert_eq; const SENDER: address = @42; @@ -97,12 +98,16 @@ module sui::config_tests { // check that read_setting is not updated until next epoch ts.next_tx(SENDER); - assert!(config::read_setting<_, u8>(id, n1, ts.ctx()).destroy_some() == 224u8); - assert!(config::read_setting<_, u8>(id, n2, ts.ctx()).is_none()); + { + assert!(config::read_setting<_, u8>(id, n1, ts.ctx()).destroy_some() == 224u8); + assert!(config::read_setting<_, u8>(id, n2, ts.ctx()).is_none()); + }; ts.next_epoch(SENDER); - assert!(config::read_setting<_, u8>(id, n1, ts.ctx()).destroy_some() == 0u8); - assert!(config::read_setting<_, u8>(id, n2, ts.ctx()).destroy_some() == 2u8); + { + assert!(config::read_setting<_, u8>(id, n1, ts.ctx()).destroy_some() == 0u8); + assert!(config::read_setting<_, u8>(id, n2, ts.ctx()).destroy_some() == 2u8); + }; ts.next_tx(SENDER); { @@ -201,6 +206,240 @@ module sui::config_tests { assert!(config::read_setting<_, u8>(id, w, ts.ctx()).is_none()); }; + // remove the setting, but still readable this epoch + ts.next_tx(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + // still some + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_some()); + // still none + assert!(config::read_setting<_, bool>(id, n, ts.ctx()).is_none()); + assert!(config::read_setting<_, u64>(id, n, ts.ctx()).is_none()); + assert!(config::read_setting<_, u8>(id, w, ts.ctx()).is_none()); + ts::return_shared(config); + }; + + // should now be none + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + // now none + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + // still none + assert!(config::read_setting<_, bool>(id, n, ts.ctx()).is_none()); + assert!(config::read_setting<_, u64>(id, n, ts.ctx()).is_none()); + assert!(config::read_setting<_, u8>(id, w, ts.ctx()).is_none()); + ts::return_shared(config); + }; + + // still none + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + // now none + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + // still none + assert!(config::read_setting<_, bool>(id, n, ts.ctx()).is_none()); + assert!(config::read_setting<_, u64>(id, n, ts.ctx()).is_none()); + assert!(config::read_setting<_, u8>(id, w, ts.ctx()).is_none()); + ts::return_shared(config); + }; + ts.end(); } + + #[test] + fun test_remove_doesnt_fail_on_duplicate() { + let mut ts = ts::begin(SENDER); + config::create(&mut WriteCap(), ts.ctx()); + ts.next_tx(SENDER); + let id = ts::most_recent_id_shared>().destroy_some(); + let n = b"hello"; + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + ts::return_shared(config); + }; + + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_some()); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_some()); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert!(config::read_setting<_, u8>(id, n, ts.ctx()).is_none()); + ts::return_shared(config); + }; + + ts.end(); + } + + #[test, expected_failure(abort_code = sui::dynamic_field::EFieldTypeMismatch)] + fun test_remove_fail_on_type_mismatch() { + let mut ts = ts::begin(SENDER); + config::create(&mut WriteCap(), ts.ctx()); + ts.next_tx(SENDER); + let id = ts::most_recent_id_shared>().destroy_some(); + let n = b"hello"; + ts.next_epoch(SENDER); + let mut config: Config = ts.take_shared_by_id(id); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + config.remove_for_current_epoch<_, _, bool>(&mut WriteCap(), n, ts.ctx()); + abort 0 + } + + #[test, expected_failure(abort_code = sui::dynamic_field::EFieldTypeMismatch)] + fun test_add_fail_on_type_mismatch() { + let mut ts = ts::begin(SENDER); + config::create(&mut WriteCap(), ts.ctx()); + ts.next_tx(SENDER); + let id = ts::most_recent_id_shared>().destroy_some(); + let n = b"hello"; + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + ts.next_epoch(SENDER); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + config.remove_for_current_epoch<_, _, bool>(&mut WriteCap(), n, ts.ctx()); + abort 0 + } + } + + #[test] + fun test_removed_value() { + let mut ts = ts::begin(SENDER); + config::create(&mut WriteCap(), ts.ctx()); + ts.next_tx(SENDER); + let id = ts::most_recent_id_shared>().destroy_some(); + let n = b"hello"; + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + let removed_value = + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 0, ts.ctx()); + assert_eq!(removed_value, option::none()); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + let removed_value = + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 1, ts.ctx()); + assert_eq!(removed_value, option::none()); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + let removed_value = + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 2, ts.ctx()); + assert_eq!(removed_value, option::some(0)); + ts::return_shared(config); + }; + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + let removed_value = + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert_eq!(removed_value, option::none()); + let removed_value = + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 3, ts.ctx()); + assert_eq!(removed_value, option::none()); + ts::return_shared(config); + }; + + + ts.next_epoch(SENDER); + { + let mut config: Config = ts.take_shared_by_id(id); + let removed_value = + config.add_for_current_epoch<_, _, u8>(&mut WriteCap(), n, 4, ts.ctx()); + assert_eq!(removed_value, option::some(2)); + let removed_value = + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert_eq!(removed_value, option::some(4)); + let removed_value = + config.remove_for_current_epoch<_, _, u8>(&mut WriteCap(), n, ts.ctx()); + assert_eq!(removed_value, option::none()); + ts::return_shared(config); + }; + + ts.end(); + } + + } diff --git a/crates/sui-framework/published_api.txt b/crates/sui-framework/published_api.txt index 4584a322f6d9e3..88cb0775f68ae8 100644 --- a/crates/sui-framework/published_api.txt +++ b/crates/sui-framework/published_api.txt @@ -1873,6 +1873,9 @@ create add_for_current_epoch public(package) fun 0x2::config +remove_for_current_epoch + public(package) fun + 0x2::config exists_with_type public(package) fun 0x2::config diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap index cb2e10a055228e..ff86636d63032c 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap @@ -240,13 +240,13 @@ validators: next_epoch_worker_address: ~ extra_fields: id: - id: "0x9782ad80b3af27622c103346d99fb630190cbea59ee12e398d965ac725c98201" + id: "0xedf032c6b47c9a27d46d9533a39b62ceef1f056f808eacec5e0ed545f7c1fdad" size: 0 voting_power: 10000 - operation_cap_id: "0xa82682c7e8cc6469e97584f0884fa620694f7c69ae379b4426724330e2459b67" + operation_cap_id: "0xcca7bcfb0c37af4c4e0dd305ed4babeb6743111e9765a5cfe5c4f369037c27a6" gas_price: 1000 staking_pool: - id: "0xecc45b11f965b0c6507287e8dfe6a155e60ad66ff9f8cb8018d2d5f14ac241e7" + id: "0xfbd6c23d91df3dfb53fa57e0ddb3317070ffa7afcf35e00717f4b4a9809e9802" activation_epoch: 0 deactivation_epoch: ~ sui_balance: 20000000000000000 @@ -254,14 +254,14 @@ validators: value: 0 pool_token_balance: 20000000000000000 exchange_rates: - id: "0xbc3c30211aef5b13284224b7e485f73584b6998c26b1a60ade850193fd141f35" + id: "0x5d656b3d0f8fd6497142742bf16262eaf906124cd4edb9c94f15b323440eb7b7" size: 1 pending_stake: 0 pending_total_sui_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0xda1c598ab339f29abf16db225fd9fa9cb2a5dd403c086a094049b499e3f05df9" + id: "0xeb2ece185bb3a9a468781b683e9be127d94cb2c65afa85d9b32fb8c5c586e4c4" size: 0 commission_rate: 200 next_epoch_stake: 20000000000000000 @@ -269,27 +269,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0x031729e8a97dd92a5738c189ec493ed2527ae0900ba7aa148a0134bbee60fe58" + id: "0x8b97b2bf431a598dbe6184c0c4e765ed770638146698438b9bbc3c94b53eac88" size: 0 pending_active_validators: contents: - id: "0x0b6928d3aff001e2929522eca3ff33957ab0ce8274bd7eaf61d546e3784dba0c" + id: "0x1139e5d1b39405f92ec995d36c752669f438c89b807c58acb5afb8c4b6c513a9" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x50676c9b2378c44854ca4a9154bd54c2c1f2433aa3338f6d72dd3d3dc9e48155" + id: "0x41f4be360bce58528aea2734ca2ab27fa0847da68fab36a7b577eecc5642ff21" size: 1 inactive_validators: - id: "0x43094c8ee5757e694ff7df3272975c35962043420fa3f9c8b063a7701a7ab4c5" + id: "0xa680d0a281b63d7eabc20288244a31964f6df47416b8fb61a8c162ee03d1f7dc" size: 0 validator_candidates: - id: "0xa756364517d279ec613a2b65b292743b9223375216caf1c6c9e871b986294f7d" + id: "0x76ebe1f42a643b10a2c465539388e2632b3df613753e247482c5e47a4775fb97" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0x829d9485dd0ad080f11b947ebd386ccf11edb0950fe4dfc78fdfd806b66583f4" + id: "0xcd6597578e8ca2343e45cd43800e04ce3682383ed7d4215f7180bf1b83768a23" size: 0 storage_fund: total_object_storage_rebates: @@ -306,7 +306,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0xfbb65ad85adf44c3798617b7a563d2771bb599f15e0ef8ce05e75a846da0cf4b" + id: "0x75257db99109e5749fdb019af4936c931d2b67c5d51731a1f09d234f531661ae" size: 0 reference_gas_price: 1000 validator_report_records: @@ -320,7 +320,7 @@ stake_subsidy: stake_subsidy_decrease_rate: 1000 extra_fields: id: - id: "0xf5253da519723dc32193015c61fcfce270ed0b1dde8c0961ae0f10473f821ddf" + id: "0x2a0623ca8b6471e329c0eb64b647614d5c57fca78a207b0d018397ac86de83fe" size: 0 safe_mode: false safe_mode_storage_rewards: @@ -332,6 +332,6 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0x8bb8345d87e584c4527d48e96586bd8ca9f5c5c096be00792338e2f4e4b068c9" + id: "0x186e5c10432adb4544ce3342f9222bb0027056f1172b4d8cebf7bd68d64eaa96" size: 0 diff --git a/crates/sui-types/src/config.rs b/crates/sui-types/src/config.rs index f7b820635a5267..9a87ff0316daf3 100644 --- a/crates/sui-types/src/config.rs +++ b/crates/sui-types/src/config.rs @@ -38,7 +38,7 @@ pub struct Setting { #[derive(Debug, Serialize, Deserialize)] pub struct SettingData { pub newer_value_epoch: u64, - pub newer_value: V, + pub newer_value: Option, pub older_value_opt: Option, } @@ -105,7 +105,7 @@ impl SettingData { None => true, }; if use_newer_value { - Some(&self.newer_value) + self.newer_value.as_ref() } else { self.older_value_opt.as_ref() } diff --git a/sui-execution/latest/sui-move-natives/src/config.rs b/sui-execution/latest/sui-move-natives/src/config.rs index d51374a182d8e9..e576f9d99ed9a3 100644 --- a/sui-execution/latest/sui-move-natives/src/config.rs +++ b/sui-execution/latest/sui-move-natives/src/config.rs @@ -149,11 +149,15 @@ fn consistent_value_before_current_epoch( }; let [newer_value_epoch, newer_value, older_value_opt]: [Value; 3] = unpack_struct(data)?; let newer_value_epoch: u64 = newer_value_epoch.value_as()?; - if current_epoch > newer_value_epoch { - option_some(value_ty, newer_value) + debug_assert!( + unpack_option(newer_value.copy_value()?, value_ty)?.is_some() + || unpack_option(older_value_opt.copy_value()?, value_ty)?.is_some() + ); + Ok(if current_epoch > newer_value_epoch { + newer_value } else { - Ok(older_value_opt) - } + older_value_opt + }) } fn unpack_struct(s: Value) -> PartialVMResult<[Value; N]> { @@ -183,10 +187,3 @@ fn option_none(type_param: &Type) -> PartialVMResult { type_param, )?]))) } - -fn option_some(type_param: &Type, value: Value) -> PartialVMResult { - Ok(Value::struct_(Struct::pack(vec![Vector::pack( - type_param, - vec![value], - )?]))) -}