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],
- )?])))
-}