diff --git a/crates/sui-framework/docs/sui-system/staking_pool.md b/crates/sui-framework/docs/sui-system/staking_pool.md
index 5401368971c0c..782ad81ed3cc1 100644
--- a/crates/sui-framework/docs/sui-system/staking_pool.md
+++ b/crates/sui-framework/docs/sui-system/staking_pool.md
@@ -517,6 +517,14 @@ A proportional amount of pool token withdraw is recorded and processed at epoch
staked_sui: StakedSui,
ctx: &TxContext
) : Balance<SUI> {
+ // stake is inactive
+ if (staked_sui.stake_activation_epoch > ctx.epoch()) {
+ let principal = unwrap_staked_sui(staked_sui);
+ pool.pending_stake = pool.pending_stake - principal.value();
+
+ return principal
+ };
+
let (pool_token_withdraw_amount, mut principal_withdraw) =
withdraw_from_principal(pool, staked_sui);
let principal_withdraw_amount = principal_withdraw.value();
diff --git a/crates/sui-framework/docs/sui-system/sui_system_state_inner.md b/crates/sui-framework/docs/sui-system/sui_system_state_inner.md
index 82baf3b957633..0a0f0ad82ac31 100644
--- a/crates/sui-framework/docs/sui-system/sui_system_state_inner.md
+++ b/crates/sui-framework/docs/sui-system/sui_system_state_inner.md
@@ -717,15 +717,6 @@ the epoch advancement transaction.
-
-
-
-
-
const EStakeWithdrawBeforeActivation: u64 = 6;
-
-
-
-
@@ -1302,10 +1293,6 @@ Withdraw some portion of a stake from a validator's staking pool.
staked_sui: StakedSui,
ctx: &TxContext,
) : Balance<SUI> {
- assert!(
- stake_activation_epoch(&staked_sui) <= ctx.epoch(),
- EStakeWithdrawBeforeActivation
- );
self.validators.request_withdraw_stake(staked_sui, ctx)
}
diff --git a/crates/sui-framework/packages/sui-system/sources/staking_pool.move b/crates/sui-framework/packages/sui-system/sources/staking_pool.move
index 3cf43037d537c..0295dbfa703a1 100644
--- a/crates/sui-framework/packages/sui-system/sources/staking_pool.move
+++ b/crates/sui-framework/packages/sui-system/sources/staking_pool.move
@@ -130,6 +130,14 @@ module sui_system::staking_pool {
staked_sui: StakedSui,
ctx: &TxContext
) : Balance {
+ // stake is inactive
+ if (staked_sui.stake_activation_epoch > ctx.epoch()) {
+ let principal = unwrap_staked_sui(staked_sui);
+ pool.pending_stake = pool.pending_stake - principal.value();
+
+ return principal
+ };
+
let (pool_token_withdraw_amount, mut principal_withdraw) =
withdraw_from_principal(pool, staked_sui);
let principal_withdraw_amount = principal_withdraw.value();
diff --git a/crates/sui-framework/packages/sui-system/sources/sui_system_state_inner.move b/crates/sui-framework/packages/sui-system/sources/sui_system_state_inner.move
index 2b0385d7d7bc3..b0830c20e6fe0 100644
--- a/crates/sui-framework/packages/sui-system/sources/sui_system_state_inner.move
+++ b/crates/sui-framework/packages/sui-system/sources/sui_system_state_inner.move
@@ -4,7 +4,7 @@
module sui_system::sui_system_state_inner {
use sui::balance::{Self, Balance};
use sui::coin::Coin;
- use sui_system::staking_pool::{stake_activation_epoch, StakedSui};
+ use sui_system::staking_pool::StakedSui;
use sui::sui::SUI;
use sui_system::validator::{Self, Validator};
use sui_system::validator_set::{Self, ValidatorSet};
@@ -214,7 +214,6 @@ module sui_system::sui_system_state_inner {
const ECannotReportOneself: u64 = 3;
const EReportRecordNotFound: u64 = 4;
const EBpsTooLarge: u64 = 5;
- const EStakeWithdrawBeforeActivation: u64 = 6;
const ESafeModeGasNotProcessed: u64 = 7;
const EAdvancedToWrongEpoch: u64 = 8;
@@ -516,10 +515,6 @@ module sui_system::sui_system_state_inner {
staked_sui: StakedSui,
ctx: &TxContext,
) : Balance {
- assert!(
- stake_activation_epoch(&staked_sui) <= ctx.epoch(),
- EStakeWithdrawBeforeActivation
- );
self.validators.request_withdraw_stake(staked_sui, ctx)
}
diff --git a/crates/sui-framework/packages/sui-system/tests/sui_system_tests.move b/crates/sui-framework/packages/sui-system/tests/sui_system_tests.move
index 8be089338330c..3d2f2d0543795 100644
--- a/crates/sui-framework/packages/sui-system/tests/sui_system_tests.move
+++ b/crates/sui-framework/packages/sui-system/tests/sui_system_tests.move
@@ -9,7 +9,7 @@
module sui_system::sui_system_tests {
use sui::test_scenario::{Self, Scenario};
use sui::sui::SUI;
- use sui_system::governance_test_utils::{add_validator_full_flow, advance_epoch, remove_validator, set_up_sui_system_state, create_sui_system_state_for_testing};
+ use sui_system::governance_test_utils::{add_validator_full_flow, advance_epoch, remove_validator, set_up_sui_system_state, create_sui_system_state_for_testing, stake_with, unstake};
use sui_system::sui_system::SuiSystemState;
use sui_system::sui_system_state_inner;
use sui_system::validator::{Self, Validator};
@@ -1002,4 +1002,54 @@ module sui_system::sui_system_tests {
test_scenario::return_shared(system_state);
scenario.next_epoch(@0x0);
}
+
+ #[test]
+ fun test_withdraw_inactive_stake() {
+ let mut scenario_val = test_scenario::begin(@0x0);
+ let scenario = &mut scenario_val;
+ // Epoch duration is set to be 42 here.
+ set_up_sui_system_state(vector[@0x1, @0x2]);
+
+ {
+ scenario.next_tx(@0x0);
+ let mut system_state = scenario.take_shared();
+ let staking_pool = system_state.active_validator_by_address(@0x1).get_staking_pool_ref();
+
+ assert!(staking_pool.pending_stake_amount() == 0, 0);
+ assert!(staking_pool.pending_stake_withdraw_amount() == 0, 0);
+ assert!(staking_pool.sui_balance() == 100 * 1_000_000_000, 0);
+
+ test_scenario::return_shared(system_state);
+ };
+
+ stake_with(@0x0, @0x1, 1, scenario);
+
+ {
+ scenario.next_tx(@0x0);
+ let mut system_state = scenario.take_shared();
+ let staking_pool = system_state.active_validator_by_address(@0x1).get_staking_pool_ref();
+
+ assert!(staking_pool.pending_stake_amount() == 1_000_000_000, 0);
+ assert!(staking_pool.pending_stake_withdraw_amount() == 0, 0);
+ assert!(staking_pool.sui_balance() == 100 * 1_000_000_000, 0);
+
+ test_scenario::return_shared(system_state);
+ };
+
+ unstake(@0x0, 0, scenario);
+
+ {
+ scenario.next_tx(@0x0);
+ let mut system_state = scenario.take_shared();
+ let staking_pool = system_state.active_validator_by_address(@0x1).get_staking_pool_ref();
+
+ assert!(staking_pool.pending_stake_amount() == 0, 0);
+ assert!(staking_pool.pending_stake_withdraw_amount() == 0, 0);
+ assert!(staking_pool.sui_balance() == 100 * 1_000_000_000, 0);
+
+ test_scenario::return_shared(system_state);
+ };
+
+ scenario_val.end();
+ }
}
diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs
index 3321c74c2a408..a4d213a629c5c 100644
--- a/crates/sui-protocol-config/src/lib.rs
+++ b/crates/sui-protocol-config/src/lib.rs
@@ -148,6 +148,7 @@ const MAX_PROTOCOL_VERSION: u64 = 52;
// Set number of leaders per round for Mysticeti commits.
// Version 51: Switch to DKG V1.
// Version 52: Emit `CommitteeMemberUrlUpdateEvent` when updating bridge node url.
+// Modified sui-system package to enable withdrawal of stake before it becomes active.
#[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct ProtocolVersion(u64);
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 8d0bf6fbbfb64..b70f77ea2bdf9 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: "0x5ee3815bd22bb164a92d2d9fa8e45c1ece868a4cec17af9f1a4f2b35e7e97612"
+ id: "0xee18e93d074b1e76fdd721caaa1f52c7a16f8736b3193f5236db390131f67286"
size: 0
voting_power: 10000
- operation_cap_id: "0x5bb1af2dad062878b913e848b9091ed6f81f064ccd6884fc181706f41f331fec"
+ operation_cap_id: "0xd8322d5e52c7948c4542b7228f0f5756ae2c7013c29357668440b45806984425"
gas_price: 1000
staking_pool:
- id: "0x5e20976a9075d0e2c1a0bacb5aa475cb868222c9ff5069da0a6cf862d18a505c"
+ id: "0x3e684f142a70db097fa72b6dc6895e62c805b123c9ca73a3b46f6acde37c67f0"
activation_epoch: 0
deactivation_epoch: ~
sui_balance: 20000000000000000
@@ -254,14 +254,14 @@ validators:
value: 0
pool_token_balance: 20000000000000000
exchange_rates:
- id: "0xb452b2f138c9eb4897a0bdb5bf8b49f16f4dfff5a0e32fc281304ab3fd3519b4"
+ id: "0xea9fbe1476736c2dea2ba0c6944567ad1cf8183724c645b501ba1c8bdc236c72"
size: 1
pending_stake: 0
pending_total_sui_withdraw: 0
pending_pool_token_withdraw: 0
extra_fields:
id:
- id: "0xeddc255f7992dc59f21cc4687253baba7d67df4289d7363ad168e0be1176e2aa"
+ id: "0x3b04d2e0e34c5eb443d932500834b52aba31725d347b8e5f02d14ddd6ea03bea"
size: 0
commission_rate: 200
next_epoch_stake: 20000000000000000
@@ -269,27 +269,27 @@ validators:
next_epoch_commission_rate: 200
extra_fields:
id:
- id: "0xe3bb275be68081f76e6f243a9e2b66c88ee49870c9a0d7ebb64e597f61e9e141"
+ id: "0x3bae281bb2a7d5fa3bbed3b6172a337fc46019f90d183982054b04e9f32133fb"
size: 0
pending_active_validators:
contents:
- id: "0xa5ce78594a6f3e8b98a766f93dc5016f80567b7d174305b2f1f5bdf648def400"
+ id: "0x9a771ce2dd53849c44948bc9eaadd8ed4ab6297808610e65b0176ca52e5c7817"
size: 0
pending_removals: []
staking_pool_mappings:
- id: "0x053838f3d4abadf5674b9b3dd267aa9754f5914edae3c0e260eb0447d4339272"
+ id: "0x0c771e8f738e428185e77491408570183f4285565ba98b8b62b2dbaa9930f02a"
size: 1
inactive_validators:
- id: "0xb6409731aacdc307c30a616a1ff94aec387ac220d5f3fd48d85812a5d2bf8306"
+ id: "0x8d0b80b0ec49ddac7fd6ad1467c78b5a28e88fe3fed7bd13d6495293d928559a"
size: 0
validator_candidates:
- id: "0xff35969e6260537114784d8654873c597d3828eb54f295ea978a80a81a512468"
+ id: "0xe6db5c73ef0829daf5f71782bd6837b0a2be1395a065638d64e491ed5b97dc55"
size: 0
at_risk_validators:
contents: []
extra_fields:
id:
- id: "0xbb3eb2d0cc5c03d7733596cc7924f7dfdf38dffffe9cf617057512e0621ff5cd"
+ id: "0x390b78ffa27f9465b475a9e3df0305f8a22284bb62ef362339415bc31cff79d5"
size: 0
storage_fund:
total_object_storage_rebates:
@@ -306,7 +306,7 @@ parameters:
validator_low_stake_grace_period: 7
extra_fields:
id:
- id: "0x94f44132137189cb0e9253c4f3d7b9491ad2de2f7dc4a26188cb9e426334f60b"
+ id: "0x3a66a39b4dc73ad58cc15074094cdfad890e567b311a27fbfa3d5cbd2a80258a"
size: 0
reference_gas_price: 1000
validator_report_records:
@@ -320,7 +320,7 @@ stake_subsidy:
stake_subsidy_decrease_rate: 1000
extra_fields:
id:
- id: "0x6acc16d86b4dee5ba8485221215c59a16641467fdb2cbe95c41fe6533d31a130"
+ id: "0xac6c38f74f782e44ec0c0219c970f4b43e6ed899e90776e545d399a10b2f6eea"
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: "0xf72863df91bf14efc27270ef17592ce3f3ecb9302099f8b5bf18303784558fa8"
+ id: "0x33f45d7789643796859e2cc2cae4765ba0cd89423f5819deab37244924a09caa"
size: 0