-
Couldn't load subscription status.
- Fork 44
feat(platform)!: withdrawal limits #2182
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
Changes from 3 commits
b1f45b2
35062e3
6c3e318
211ec4f
fa53e84
82ac2c0
538f017
8af069d
802811c
385b78f
f5c07d2
9d2a56a
29b774a
baa33ea
16cb5b7
5cd1e83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| use crate::fee::Credits; | ||
| use crate::withdrawal::daily_withdrawal_limit::v0::daily_withdrawal_limit_v0; | ||
| use crate::ProtocolError; | ||
| use platform_version::version::PlatformVersion; | ||
|
|
||
| mod v0; | ||
|
|
||
| pub fn daily_withdrawal_limit( | ||
| total_credits_in_platform: Credits, | ||
| platform_version: &PlatformVersion, | ||
| ) -> Result<Credits, ProtocolError> { | ||
| match platform_version.dpp.methods.daily_withdrawal_limit { | ||
| 0 => Ok(daily_withdrawal_limit_v0(total_credits_in_platform)), | ||
| v => Err(ProtocolError::UnknownVersionError(format!( | ||
| "Unknown daily_withdrawal_limit version {v}" | ||
| ))), | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| use crate::fee::Credits; | ||
| use crate::ProtocolError; | ||
|
Check warning on line 2 in packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/v0/mod.rs
|
||
QuantumExplorer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /// Calculates the daily withdrawal limit based on the total credits available in the platform. | ||
| /// | ||
| /// The function enforces the following rules: | ||
| /// | ||
| /// 1. If the total credits are 1000 or more: | ||
| /// - The withdrawal limit is set to 10% of the total credits. | ||
| /// 2. If the total credits are between 100 and 999: | ||
| /// - The withdrawal limit is capped at 100 credits. | ||
| /// 3. If the total credits are less than 100: | ||
| /// - The withdrawal limit is the total available credits, as no more than the available amount can be withdrawn. | ||
| /// | ||
| /// # Parameters | ||
| /// | ||
| /// * `total_credits_in_platform`: The total amount of credits available in the platform. | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// * `Credits`: The calculated daily withdrawal limit based on the available credits. | ||
| /// | ||
| pub fn daily_withdrawal_limit_v0(total_credits_in_platform: Credits) -> Credits { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it credits in dash? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean it's credits There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, fixed. |
||
| if total_credits_in_platform >= 1000 { | ||
| total_credits_in_platform / 10 | ||
| } else if total_credits_in_platform >= 100 { | ||
| 100 | ||
| } else { | ||
| total_credits_in_platform | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance robustness and add unit tests While the basic functionality is implemented correctly, there are a few areas where the function could be improved:
Here's a suggested improvement that addresses these points: use crate::fee::Credits;
#[derive(Debug, thiserror::Error)]
pub enum WithdrawalLimitError {
#[error("Invalid credit amount: {0}")]
InvalidCreditAmount(i64),
#[error("Overflow error")]
Overflow,
}
pub fn daily_withdrawal_limit_v0(total_credits_in_platform: Credits) -> Result<Credits, WithdrawalLimitError> {
if total_credits_in_platform < 0 {
return Err(WithdrawalLimitError::InvalidCreditAmount(total_credits_in_platform));
}
Ok(if total_credits_in_platform >= 1000 {
total_credits_in_platform.checked_div(10).ok_or(WithdrawalLimitError::Overflow)?
} else if total_credits_in_platform >= 100 {
100
} else {
total_credits_in_platform
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_daily_withdrawal_limit() {
assert_eq!(daily_withdrawal_limit_v0(2000).unwrap(), 200);
assert_eq!(daily_withdrawal_limit_v0(500).unwrap(), 100);
assert_eq!(daily_withdrawal_limit_v0(50).unwrap(), 50);
}
#[test]
fn test_invalid_input() {
assert!(matches!(daily_withdrawal_limit_v0(-100), Err(WithdrawalLimitError::InvalidCreditAmount(-100))));
}
#[test]
fn test_overflow() {
assert!(matches!(daily_withdrawal_limit_v0(Credits::MAX), Err(WithdrawalLimitError::Overflow)));
}
}This implementation adds input validation, error handling, and unit tests to increase the robustness and reliability of the function. Would you like me to create a separate GitHub issue to track the implementation of these improvements? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Credits is a u64. I don't think a division by 10 could ever overflow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @QuantumExplorer, you're correct. Since ✏️ Learnings added
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| mod check_for_desired_protocol_upgrade; | ||
| mod perform_events_on_first_block_of_protocol_change; | ||
| mod upgrade_protocol_version; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| mod v0; | ||
|
|
||
| use crate::error::execution::ExecutionError; | ||
| use crate::error::Error; | ||
| use crate::platform_types::epoch_info::EpochInfo; | ||
| use crate::platform_types::platform::Platform; | ||
| use dpp::version::PlatformVersion; | ||
| use dpp::version::ProtocolVersion; | ||
| use drive::grovedb::Transaction; | ||
|
|
||
| impl<C> Platform<C> { | ||
| /// Executes specific events that need to be performed on the first block of a protocol change. | ||
| /// | ||
| /// # Parameters | ||
| /// | ||
| /// - `&self`: A reference to the current instance of the struct implementing this function. | ||
| /// - `block_info`: Information about the current block, encapsulated in a `BlockInfo` struct. | ||
| /// - `epoch_info`: Information about the current epoch, encapsulated in an `EpochInfo` struct. | ||
| /// - `last_committed_platform_state`: The state of the platform as it was after the last committed block, before the protocol change. | ||
| /// - `block_platform_state`: A mutable reference to the platform state that will be modified for the current block. | ||
| /// - `transaction`: The transaction object representing the current transaction context. | ||
| /// - `platform_version`: The version of the platform being executed, encapsulated in a `PlatformVersion` struct. | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// - `Result<(), Error>`: Returns `Ok(())` if the events are successfully executed. | ||
| /// Returns an `Error` if there is a version mismatch or another execution issue. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// - Returns an `Error::Execution(ExecutionError::UnknownVersionMismatch)` if an unknown version is received | ||
| /// that is not supported by the current implementation. | ||
| /// | ||
| /// # Versioning | ||
| /// | ||
| /// This function uses the `platform_version` parameter to determine which version-specific implementation | ||
| /// of the protocol change events should be executed: | ||
| /// | ||
| /// - If the version is `0`, it calls the `perform_events_on_first_block_of_protocol_change_v0` function, | ||
| /// which contains the logic for version `0`. | ||
| /// - If no version is specified (`None`), the function does nothing and returns `Ok(())`. | ||
| /// - If a different version is specified, it returns an error indicating an unknown version mismatch. | ||
| /// | ||
|
||
| pub fn perform_events_on_first_block_of_protocol_change( | ||
| &self, | ||
| epoch_info: &EpochInfo, | ||
| transaction: &Transaction, | ||
| previous_protocol_version: ProtocolVersion, | ||
| platform_version: &PlatformVersion, | ||
| ) -> Result<(), Error> { | ||
| match platform_version | ||
| .drive_abci | ||
| .methods | ||
| .protocol_upgrade | ||
| .perform_events_on_first_block_of_protocol_change | ||
| { | ||
| Some(0) => self.perform_events_on_first_block_of_protocol_change_v0( | ||
| epoch_info, | ||
| transaction, | ||
| previous_protocol_version, | ||
| platform_version, | ||
| ), | ||
| None => return Ok(()), | ||
| Some(version) => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { | ||
| method: "perform_events_on_first_block_of_protocol_change".to_string(), | ||
| known_versions: vec![0], | ||
| received: version, | ||
| })), | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| use crate::error::Error; | ||
| use crate::platform_types::epoch_info::EpochInfo; | ||
| use crate::platform_types::platform::Platform; | ||
| use dpp::version::PlatformVersion; | ||
| use dpp::version::ProtocolVersion; | ||
| use drive::drive::identity::withdrawals::paths::{ | ||
| get_withdrawal_root_path, WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY, | ||
| }; | ||
| use drive::drive::RootTree; | ||
| use drive::grovedb::{Element, Transaction}; | ||
|
|
||
| impl<C> Platform<C> { | ||
| /// checks for a network upgrade and resets activation window | ||
| /// this should only be called on epoch change | ||
| pub(super) fn perform_events_on_first_block_of_protocol_change_v0( | ||
| &self, | ||
| _epoch_info: &EpochInfo, | ||
| transaction: &Transaction, | ||
| previous_protocol_version: ProtocolVersion, | ||
| platform_version: &PlatformVersion, | ||
| ) -> Result<(), Error> { | ||
| if previous_protocol_version < 4 && platform_version.protocol_version >= 4 { | ||
| let path = get_withdrawal_root_path(); | ||
| self.drive.grove_insert_if_not_exists( | ||
| (&path).into(), | ||
| &WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY, | ||
| Element::empty_sum_tree(), | ||
| Some(transaction), | ||
| None, | ||
| &platform_version.drive, | ||
| )?; | ||
| } | ||
|
|
||
| Ok(()) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Consider Enhancing Error Handling While Maintaining Consistent Version Checks and Sum Tree Initializations. The version check 🔗 Analysis chainConsider enhancing error handling and verifying version check logic.
To verify the usage and impact of this method: This script will help identify other areas of the codebase that might be affected by or related to this change, ensuring consistency and completeness of the implementation. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Check for other occurrences of protocol version checks and sum tree insertions
# Test 1: Look for other protocol version checks
echo "Checking for protocol version checks:"
rg --type rust "protocol_version.*[<>=].*4"
# Test 2: Look for other sum tree insertions
echo "Checking for sum tree insertions:"
rg --type rust "Element::empty_sum_tree"
# Test 3: Check for withdrawal-related operations
echo "Checking for withdrawal-related operations:"
rg --type rust "withdrawal"
Length of output: 234831 |
||
| } | ||
| } | ||
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.
🛠️ Refactor suggestion
Consider refactoring to reduce code duplication.
The
new_p2shmethod is similar to the existingrandom_p2shmethod. To reduce duplication and improve maintainability, consider refactoring these methods to share common logic.Here's a suggested refactoring:
This refactoring extracts the common logic into a private
create_p2sh_scriptmethod, which is then used by bothnew_p2shandrandom_p2sh.