Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ use drive::drive::identity::key::fetch::{
IdentityKeysRequest, KeyIDIdentityPublicKeyPairBTreeMap, KeyRequestType,
};
use drive::drive::identity::withdrawals::paths::{
get_withdrawal_root_path, WITHDRAWAL_TOTAL_CREDITS_HISTORY_KEY,
WITHDRAWAL_TRANSACTIONS_BROADCASTED_KEY, WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY,
get_withdrawal_root_path, WITHDRAWAL_CREDIT_INFLOWS_SUM_TREE_KEY,
WITHDRAWAL_TOTAL_CREDITS_HISTORY_KEY, WITHDRAWAL_TRANSACTIONS_BROADCASTED_KEY,
WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY,
};
use drive::drive::prefunded_specialized_balances::prefunded_specialized_balances_for_voting_path_vec;
use drive::drive::saved_block_transactions::{
Expand Down Expand Up @@ -723,6 +724,18 @@ impl<C> Platform<C> {
&platform_version.drive,
)?;

// Credit inflows sum tree: every credit mint is recorded here so the daily withdrawal
// limit counts net outflow instead of gross — credits that entered Platform within the
// window may leave again without consuming the withdrawal budget of other users.
self.drive.grove_insert_if_not_exists(
get_withdrawal_root_path().as_slice().into(),
&WITHDRAWAL_CREDIT_INFLOWS_SUM_TREE_KEY,
Element::empty_sum_tree(),
Some(transaction),
None,
&platform_version.drive,
)?;
Comment thread
QuantumExplorer marked this conversation as resolved.
Comment thread
QuantumExplorer marked this conversation as resolved.

Ok(())
}
}
Expand Down Expand Up @@ -1402,17 +1415,22 @@ mod tests {
use drive::grovedb_path::SubtreePath;

// Not there on a v13 genesis state
assert!(platform
.drive
.grove
.get(
SubtreePath::from(&get_withdrawal_root_path()),
&WITHDRAWAL_TOTAL_CREDITS_HISTORY_KEY,
Some(&transaction),
&platform_version.drive.grove_version,
)
.value
.is_err());
for key in [
&WITHDRAWAL_TOTAL_CREDITS_HISTORY_KEY,
&WITHDRAWAL_CREDIT_INFLOWS_SUM_TREE_KEY,
] {
assert!(platform
.drive
.grove
.get(
SubtreePath::from(&get_withdrawal_root_path()),
key,
Some(&transaction),
&platform_version.drive.grove_version,
)
.value
.is_err());
}

let block_info = BlockInfo {
time_ms: 1_000_000,
Expand All @@ -1437,6 +1455,19 @@ mod tests {
.expect("total credits history tree should exist after the v14 transition");
assert!(element.is_any_tree());

let element = platform
.drive
.grove
.get(
SubtreePath::from(&get_withdrawal_root_path()),
&WITHDRAWAL_CREDIT_INFLOWS_SUM_TREE_KEY,
Some(&transaction),
&platform_version.drive.grove_version,
)
.value
.expect("credit inflows sum tree should exist after the v14 transition");
assert!(element.is_sum_tree());

// Running it again is harmless and the tree stays usable
platform
.transition_to_version_14(&block_info, &transaction, platform_version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use dpp::version::PlatformVersion;
use drive::grovedb::Transaction;

mod v0;
mod v1;

impl<C> Platform<C>
where
Expand Down Expand Up @@ -48,9 +49,14 @@ where
transaction,
platform_version,
),
1 => self.cleanup_expired_locks_of_withdrawal_amounts_v1(
block_info,
transaction,
platform_version,
),
version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch {
method: "cleanup_expired_locks_of_withdrawal_amounts".to_string(),
known_versions: vec![0],
known_versions: vec![0, 1],
received: version,
})),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use crate::error::Error;
use crate::platform_types::platform::Platform;

use crate::rpc::core::CoreRPCLike;
use dpp::block::block_info::BlockInfo;

use dpp::version::PlatformVersion;
use drive::drive::identity::withdrawals::paths::{
get_withdrawal_credit_inflows_sum_tree_path_vec, get_withdrawal_transactions_sum_tree_path_vec,
};
use drive::grovedb::{MaybeTree, PathQuery, QueryItem, Transaction};
use drive::util::grove_operations::BatchDeleteApplyType;

impl<C> Platform<C>
where
C: CoreRPCLike,
{
/// Version 1 differs from version 0 in also pruning the expired entries of the credit
/// inflows sum tree, which exists from protocol version 14: both trees are keyed by the
/// block time their entries stop counting toward the daily withdrawal limit, on the same
/// 25 hour schedule, and both are pruned with the same per-block limit.
pub(super) fn cleanup_expired_locks_of_withdrawal_amounts_v1(
&self,
block_info: &BlockInfo,
transaction: &Transaction,
platform_version: &PlatformVersion,
) -> Result<(), Error> {
let limit = platform_version
.drive_abci
.withdrawal_constants
.cleanup_expired_locks_of_withdrawal_amounts_limit;

if limit == 0 {
// No clean up
return Ok(());
}

let mut batch_operations = vec![];

for path in [
get_withdrawal_transactions_sum_tree_path_vec(),
get_withdrawal_credit_inflows_sum_tree_path_vec(),
] {
let mut path_query = PathQuery::new_single_query_item(
path,
QueryItem::RangeTo(..block_info.time_ms.to_be_bytes().to_vec()),
);

path_query.query.limit = Some(limit);

self.drive.batch_delete_items_in_path_query(
&path_query,
true,
// we know that we are not deleting a subtree
BatchDeleteApplyType::StatefulBatchDelete {
is_known_to_be_subtree_with_sum: Some(MaybeTree::NotTree),
},
Some(transaction),
&mut batch_operations,
&platform_version.drive,
)?;
}

self.drive.apply_batch_low_level_drive_operations(
None,
Some(transaction),
batch_operations,
&mut vec![],
&platform_version.drive,
)?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use crate::test::helpers::setup::TestPlatformBuilder;
use dpp::block::block_info::BlockInfo;
use dpp::block::epoch::Epoch;
use dpp::version::PlatformVersion;
use drive::drive::identity::withdrawals::paths::{
get_withdrawal_credit_inflows_sum_tree_path_vec,
get_withdrawal_transactions_sum_tree_path_vec,
};
use drive::grovedb::{Element, PathQuery, Query, SizedQuery};
use drive::util::grove_operations::BatchInsertApplyType;
use drive::util::object_size_info::PathKeyElementInfo;

/// Both the reserved withdrawal amounts and the credit inflows expire on the same
/// schedule; the v1 cleanup must prune the entries of both trees whose key is before the
/// block time and leave the rest.
#[test]
fn should_prune_expired_entries_of_both_sum_trees() {
let platform_version = PlatformVersion::latest();
let platform = TestPlatformBuilder::new()
.with_latest_protocol_version()
.build_with_mock_rpc()
.set_initial_state_structure();

let transaction = platform.drive.grove.start_transaction();

let now_ms: u64 = 1_000_000;

for path in [
get_withdrawal_transactions_sum_tree_path_vec(),
get_withdrawal_credit_inflows_sum_tree_path_vec(),
] {
for (key_time_ms, amount) in [(now_ms - 1, 100i64), (now_ms, 250i64)] {
let mut drive_operations = vec![];
platform
.drive
.batch_insert_sum_item_or_add_to_if_already_exists(
PathKeyElementInfo::PathKeyElement::<0>((
path.clone(),
key_time_ms.to_be_bytes().to_vec(),
Element::new_sum_item(amount),
)),
BatchInsertApplyType::StatefulBatchInsert,
Some(&transaction),
&mut drive_operations,
&platform_version.drive,
)
.expect("expected to insert the entry");
platform
.drive
.apply_batch_low_level_drive_operations(
None,
Some(&transaction),
drive_operations,
&mut vec![],
&platform_version.drive,
)
.expect("expected to apply the entry");
}
}

platform
.cleanup_expired_locks_of_withdrawal_amounts_v1(
&BlockInfo {
time_ms: now_ms,
height: 100,
core_height: 10,
epoch: Epoch::default(),
},
&transaction,
platform_version,
)
.expect("expected the cleanup to succeed");

for path in [
get_withdrawal_transactions_sum_tree_path_vec(),
get_withdrawal_credit_inflows_sum_tree_path_vec(),
] {
let mut query = Query::new();
query.insert_all();
let (results, _) = platform
.drive
.grove_get_raw_path_query(
&PathQuery::new(path, SizedQuery::new(query, None, None)),
Some(&transaction),
drive::grovedb::query_result_type::QueryResultType::QueryKeyElementPairResultType,
&mut vec![],
&platform_version.drive,
)
.expect("expected to query the tree");
let keys: Vec<_> = results
.to_key_elements()
.into_iter()
.map(|(key, _)| key)
.collect();
// The entry exactly at the block time is not expired yet (strict `<`).
assert_eq!(keys, vec![now_ms.to_be_bytes().to_vec()]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ mod tests {

assert_eq!(processing_result.valid_count(), 1);

assert_eq!(processing_result.aggregated_fees().processing_fee, 1919540);
assert_eq!(processing_result.aggregated_fees().processing_fee, 2100540);

platform
.drive
Expand All @@ -443,7 +443,7 @@ mod tests {
.expect("expected to get identity balance")
.expect("expected there to be an identity balance for this identity");

assert_eq!(identity_balance, 99913867460);
assert_eq!(identity_balance, 99908880460);
}

#[tokio::test]
Expand Down Expand Up @@ -877,7 +877,7 @@ mod tests {

assert_eq!(processing_result.valid_count(), 1);

assert_eq!(processing_result.aggregated_fees().processing_fee, 2195200);
assert_eq!(processing_result.aggregated_fees().processing_fee, 2367440);

platform
.drive
Expand All @@ -892,7 +892,7 @@ mod tests {
.expect("expected to get identity balance")
.expect("expected there to be an identity balance for this identity");

assert_eq!(identity_balance, 99909262100); // The identity balance is smaller than if there hadn't been any issue
assert_eq!(identity_balance, 99909089860); // The identity balance is smaller than if there hadn't been any issue
}

#[tokio::test]
Expand Down Expand Up @@ -1842,7 +1842,7 @@ mod tests {

assert_eq!(processing_result.valid_count(), 1);

assert_eq!(processing_result.aggregated_fees().processing_fee, 2195200);
assert_eq!(processing_result.aggregated_fees().processing_fee, 2367440);

platform
.drive
Expand All @@ -1857,6 +1857,6 @@ mod tests {
.expect("expected to get identity balance")
.expect("expected there to be an identity balance for this identity");

assert_eq!(identity_balance, 99909262100); // The identity balance is smaller than if there hadn't been any issue
assert_eq!(identity_balance, 99909089860); // The identity balance is smaller than if there hadn't been any issue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ mod tests {

assert_eq!(processing_result.valid_count(), 1);

assert_eq!(processing_result.aggregated_fees().processing_fee, 588840);
assert_eq!(processing_result.aggregated_fees().processing_fee, 769840);

platform
.drive
Expand All @@ -382,7 +382,7 @@ mod tests {
.expect("expected to get identity balance")
.expect("expected there to be an identity balance for this identity");

assert_eq!(identity_balance, 149993606160); // about 0.5 Dash starting balance + 1 Dash asset lock top up
assert_eq!(identity_balance, 149988619160); // about 0.5 Dash starting balance + 1 Dash asset lock top up
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,16 +187,18 @@ mod tests {
.expect("expected to fetch balances")
.expect("expected to have an identity to get balance from");

assert_eq!(balance, 99864009940)
assert_eq!(balance, 99859022940)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}

#[tokio::test]
async fn run_chain_one_identity_in_solitude_protocol_version_13() {
// Pins the fee shape at protocol version 13. The grove v4 cleanup
// gates active from v14 derive their inspection from data the merk
// apply already loads, so they are cost-neutral: this balance is
// identical to the latest-version test's, and the pair proves the
// v13 -> v14 boundary changes nothing about this run's fees.
// apply already loads, so they are cost-neutral. From v14 the identity
// create also records its asset-lock credits in the credit inflows sum
// tree for the net daily withdrawal limit, so the latest-version test's
// balance is 4,987,000 credits lower than this one; the pair pins both
// sides of the v13 -> v14 boundary.
// This is different because in the root tree we added GroupActions
// DataContract_Documents 64
// / \
Expand Down
Loading
Loading