mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 16:31:07 +00:00
Introduce rebond (#4374)
* Implement rebond: allowing to re-bond stake unbonded.
This commit is contained in:
@@ -78,8 +78,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
// and set impl_version to equal spec_version. If only runtime
|
||||
// implementation changes and behavior does not, then leave spec_version as
|
||||
// is and increment impl_version.
|
||||
spec_version: 199,
|
||||
impl_version: 199,
|
||||
spec_version: 200,
|
||||
impl_version: 200,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
};
|
||||
|
||||
|
||||
@@ -395,7 +395,7 @@ pub struct StakingLedger<AccountId, Balance: HasCompact> {
|
||||
|
||||
impl<
|
||||
AccountId,
|
||||
Balance: HasCompact + Copy + Saturating,
|
||||
Balance: HasCompact + Copy + Saturating + SimpleArithmetic,
|
||||
> StakingLedger<AccountId, Balance> {
|
||||
/// Remove entries from `unlocking` that are sufficiently old and reduce the
|
||||
/// total by the sum of their balances.
|
||||
@@ -412,6 +412,30 @@ impl<
|
||||
Self { total, active: self.active, stash: self.stash, unlocking }
|
||||
}
|
||||
|
||||
/// Re-bond funds that were scheduled for unlocking.
|
||||
fn rebond(mut self, value: Balance) -> Self {
|
||||
let mut unlocking_balance: Balance = Zero::zero();
|
||||
|
||||
while let Some(last) = self.unlocking.last_mut() {
|
||||
if unlocking_balance + last.value <= value {
|
||||
unlocking_balance += last.value;
|
||||
self.active += last.value;
|
||||
self.unlocking.pop();
|
||||
} else {
|
||||
let diff = value - unlocking_balance;
|
||||
|
||||
unlocking_balance += diff;
|
||||
self.active += diff;
|
||||
last.value -= diff;
|
||||
}
|
||||
|
||||
if unlocking_balance >= value {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Balance> StakingLedger<AccountId, Balance> where
|
||||
@@ -804,6 +828,8 @@ decl_error! {
|
||||
InsufficientValue,
|
||||
/// Can not schedule more unlock chunks.
|
||||
NoMoreChunks,
|
||||
/// Can not rebond without unlocking chunks.
|
||||
NoUnlockChunk,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -959,6 +985,26 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Rebond a portion of the stash scheduled to be unlocked.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - Time complexity: O(1). Bounded by `MAX_UNLOCKING_CHUNKS`.
|
||||
/// - Storage changes: Can't increase storage, only decrease it.
|
||||
/// # </weight>
|
||||
#[weight = SimpleDispatchInfo::FixedNormal(500_000)]
|
||||
fn rebond(origin, #[compact] value: BalanceOf<T>) {
|
||||
let controller = ensure_signed(origin)?;
|
||||
let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
|
||||
ensure!(
|
||||
ledger.unlocking.len() > 0,
|
||||
Error::<T>::NoUnlockChunk,
|
||||
);
|
||||
|
||||
let ledger = ledger.rebond(value);
|
||||
|
||||
Self::update_ledger(&controller, &ledger);
|
||||
}
|
||||
|
||||
/// Remove any unlocked chunks from the `unlocking` queue from our management.
|
||||
///
|
||||
/// This essentially frees up that balance to be used by the stash account to do
|
||||
|
||||
@@ -1196,6 +1196,146 @@ fn too_many_unbond_calls_should_not_work() {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rebond_works() {
|
||||
// * Should test
|
||||
// * Given an account being bonded [and chosen as a validator](not mandatory)
|
||||
// * it can unbond a portion of its funds from the stash account.
|
||||
// * it can re-bond a portion of the funds scheduled to unlock.
|
||||
ExtBuilder::default()
|
||||
.nominate(false)
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Set payee to controller. avoids confusion
|
||||
assert_ok!(Staking::set_payee(
|
||||
Origin::signed(10),
|
||||
RewardDestination::Controller
|
||||
));
|
||||
|
||||
// Give account 11 some large free balance greater than total
|
||||
let _ = Balances::make_free_balance_be(&11, 1000000);
|
||||
|
||||
// confirm that 10 is a normal validator and gets paid at the end of the era.
|
||||
start_era(1);
|
||||
|
||||
// Initial state of 10
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 1000,
|
||||
unlocking: vec![],
|
||||
})
|
||||
);
|
||||
|
||||
start_era(2);
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
|
||||
// Try to rebond some funds. We get an error since no fund is unbonded.
|
||||
assert_noop!(
|
||||
Staking::rebond(Origin::signed(10), 500),
|
||||
Error::<Test>::NoUnlockChunk,
|
||||
);
|
||||
|
||||
// Unbond almost all of the funds in stash.
|
||||
Staking::unbond(Origin::signed(10), 900).unwrap();
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 100,
|
||||
unlocking: vec![UnlockChunk {
|
||||
value: 900,
|
||||
era: 2 + 3
|
||||
},]
|
||||
})
|
||||
);
|
||||
|
||||
// Re-bond all the funds unbonded.
|
||||
Staking::rebond(Origin::signed(10), 900).unwrap();
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 1000,
|
||||
unlocking: vec![],
|
||||
})
|
||||
);
|
||||
|
||||
// Unbond almost all of the funds in stash.
|
||||
Staking::unbond(Origin::signed(10), 900).unwrap();
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 100,
|
||||
unlocking: vec![UnlockChunk { value: 900, era: 5 }],
|
||||
})
|
||||
);
|
||||
|
||||
// Re-bond part of the funds unbonded.
|
||||
Staking::rebond(Origin::signed(10), 500).unwrap();
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 600,
|
||||
unlocking: vec![UnlockChunk { value: 400, era: 5 }],
|
||||
})
|
||||
);
|
||||
|
||||
// Re-bond the remainder of the funds unbonded.
|
||||
Staking::rebond(Origin::signed(10), 500).unwrap();
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 1000,
|
||||
unlocking: vec![]
|
||||
})
|
||||
);
|
||||
|
||||
// Unbond parts of the funds in stash.
|
||||
Staking::unbond(Origin::signed(10), 300).unwrap();
|
||||
Staking::unbond(Origin::signed(10), 300).unwrap();
|
||||
Staking::unbond(Origin::signed(10), 300).unwrap();
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 100,
|
||||
unlocking: vec![
|
||||
UnlockChunk { value: 300, era: 5 },
|
||||
UnlockChunk { value: 300, era: 5 },
|
||||
UnlockChunk { value: 300, era: 5 },
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
// Re-bond part of the funds unbonded.
|
||||
Staking::rebond(Origin::signed(10), 500).unwrap();
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 600,
|
||||
unlocking: vec![
|
||||
UnlockChunk { value: 300, era: 5 },
|
||||
UnlockChunk { value: 100, era: 5 },
|
||||
]
|
||||
})
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment() {
|
||||
// Test that slot_stake is determined by the least staked validator
|
||||
|
||||
Reference in New Issue
Block a user