From c556c30640f4d1aab3dac9edaf5459dcf964a304 Mon Sep 17 00:00:00 2001 From: 0xripleys <105607696+0xripleys@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:08:07 -0400 Subject: [PATCH] [sui-system] Allow inactive StakedSui objects to be withdrawn immediately (#18265) ## Description Allow inactive StakedSui objects to be withdrawn immediately Implementation for https://github.com/sui-foundation/sips/pull/33 ## Test plan Wrote unit test to make sure the the functionality worked as expected. --- ## 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: --------- Co-authored-by: Emma Zhong --- .../docs/sui-system/staking_pool.md | 8 +++ .../docs/sui-system/sui_system_state_inner.md | 13 ----- .../sui-system/sources/staking_pool.move | 8 +++ .../sources/sui_system_state_inner.move | 7 +-- .../sui-system/tests/sui_system_tests.move | 52 ++++++++++++++++++- crates/sui-protocol-config/src/lib.rs | 1 + ..._populated_genesis_snapshot_matches-2.snap | 28 +++++----- 7 files changed, 83 insertions(+), 34 deletions(-) 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