-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/separate unlock chunks #1753 #1763
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #1763 +/- ##
==========================================
+ Coverage 87.62% 87.70% +0.08%
==========================================
Files 52 53 +1
Lines 4346 4410 +64
==========================================
+ Hits 3808 3868 +60
- Misses 538 542 +4 ☔ View full report in Codecov by Sentry. |
094a613
to
1c35112
Compare
}, | ||
)?; | ||
let staking_account = Self::get_staking_account_for(staker).unwrap_or_default(); | ||
let total_locked = staking_account.active.saturating_add(total_unlocking); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
total_locked
will be zero if they've already completely unstaked (in which case staking_account.active is the default of 0), and all unstake unlocks have thawed (in which case total_unlocking
never gets modified from 0).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aramikm note here unwrap_or_default
to takes care of the case described above so staking_account.active would be 0.
⏳ Running benchmarks and calculating weights. DO NOT MERGE! A new commit will be added upon completion... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great effort! Checked migrations and added some comments. Was not able to contextually check the math for changes since I don't have much context.
✅ Finished running benchmarks. Updated weights have been committed to this PR branch in commit 1df7c3f. |
1df7c3f
to
a6f250e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
⏳ Running benchmarks and calculating weights. DO NOT MERGE! A new commit will be added upon completion... |
⏳ Running benchmarks and calculating weights. DO NOT MERGE! A new commit will be added upon completion... |
⏳ Running benchmarks and calculating weights. DO NOT MERGE! A new commit will be added upon completion... |
✅ Finished running benchmarks. Updated weights have been committed to this PR branch in commit 2b6f740. |
@@ -206,9 +214,15 @@ pub mod pallet { | |||
pub type EpochLength<T: Config> = | |||
StorageValue<_, BlockNumberFor<T>, ValueQuery, EpochLengthDefault<T>>; | |||
|
|||
#[pallet::storage] | |||
#[pallet::getter(fn get_unstake_unlocking_for)] | |||
pub type UnstakeUnlocks<T: Config> = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not name it UnlockChunks
?
@@ -237,3 +178,48 @@ pub struct EpochInfo<BlockNumber> { | |||
/// The block number when this epoch started. | |||
pub epoch_start: BlockNumber, | |||
} | |||
|
|||
/// A BoundedVec containing UnlockChunks | |||
pub type UnlockChunkList<T> = BoundedVec< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need a new type for this? There is already UnstakeUnlock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UnstakeUnlock is the name for the storage. UnlockChunkList is just an alias for readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes it less readable why do we need it? We do not do this anywhere else? This type does not really does not have an impl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rehash of our conversation earlier: the UnlockChunkList (not the greatest name I know) is used in the 3 fns in types.rs that take it as a parameter and also referenced by the declaration of UnstakeUnlocks.
} | ||
let staking_account = Self::get_staking_account_for(staker).unwrap_or_default(); | ||
let total_locked = staking_account.active.saturating_add(total_unlocking); | ||
if total_locked.is_zero() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should keep the set delete method interface
delete_staking_account
set_staking_account
I am concern that someone is going to use these directly at some point and mess up the accounting. That why I wrapped does in a function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you think will be "used directly." and how someone would mess up the accounting, nor how an encapsulation would fix that. I think encapsulating one-liner functions that are called only from one place is not a good way to prevent others from making calls they should not make. If anything it communicates that the function is intended to be called by multiple functions.
As for update/delete/set staking account, keeping all that logic together is not possible because it was called for both staking and unstaking. The refactor meant that the 'set_staking_account' logic, which still has locking logic, is called only on a stake. Now withdraw_unstake
no longer changes the staking account. To me it's clearer and simpler to leave the logic only where it's used, and the do_withdraw_unstake function is pretty short and easy to read. I see no benefit in making this function call at least 2 new, short functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok can we please wrap these in their own functions. It will make it easier for updating for the new fungible trait. We use set_lock with WithdrawReason::all twice. Its definitely nicer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah ok, I will do a follow-up then. I did do a small refactor that lets you call set_staking_account
which deletes it if there is a zero active
balance, but all that does now is set the staking account, and it's called in 3 places. The original is renamed to be more like what it does, set_staking_account_and_lock
let amount_withdrawn = unlock_chunks_reap_thawed::<T>(&mut unlocks, current_epoch); | ||
ensure!(!amount_withdrawn.is_zero(), Error::<T>::NoThawedTokenAvailable); | ||
if unlocks.is_empty() { | ||
UnstakeUnlocks::<T>::set(staker, None); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should wrap these too in a method and not use it directly it. It makes easier for updates and changes.
.min(proposed_amount) | ||
} | ||
|
||
pub(crate) fn do_withdraw_unstaked( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how you created this function and simplified the do_withdraw_unstaked
.
Ok(actual_unstaked_amount) | ||
} | ||
|
||
fn add_unlock_chunk( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set_unlock_chunk or to be consistent with your naming set_unstake_unlock_chunk
. Although, this is the reason why I like UnlockChunk instead of unstake_unlock because it seems more natural.
T::Currency::remove_lock(STAKING_ID, &staker); | ||
StakingAccountLedger::<T>::remove(&staker); | ||
/// Sets staking account details after a deposit | ||
fn set_staking_account_and_lock( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Goal
The goal of this PR is to separate unstaking unlock chunks into their own storage and do a storage migration which includes staking type, for convenience of the planned Provider Boost feature so that another storage migration is not needed for that feature. There are multiple advantages to this separation discussed in the linked issue.
Closes #1753 . Please see the linked issue for more details about this proposed change.
Discussion
A stake will require 1 extra read of unstaking unlocks from before, in order to set the account token lock correctly. This was not noted in the original issue.
As noted in the original issue, an unstake will require 1 extra read and 1 extra write from before.
Effectively changes the name of
StakingAccountDetails
to justStakingDetails
while splitting the storage.StakingAccountLedger
now storesStakingDetails
.The migration uses the
translate
function of theIterableStorageMap
trait.The implementation design document was updated to make it more clear that details of actual ProviderBoost staking rewards are undecided and unspecified.
Since token locks are being touched, please review with an eye toward bugs with token locks and test coverage.
To Verify
Install the CLI version of try-runtime, then run try-runtime to test the migration against Frequency Rococo:
cargo build --release --features frequency-rococo-testnet,try-runtime && \ try-runtime --runtime ./target/release/wbuild/frequency-runtime/frequency_runtime.wasm on-runtime-upgrade live --uri wss://rpc.rococo.frequency.xyz:443 -p Capacity
You should see at something like:
The errors afterward are unrelated to the migration, having to do with a runtime difference between local and rococo, and can be ignored.
Checklist
Custom RPC OR Runtime API added/changed? Updated js/api-augment.