diff --git a/minter/src/state/mod.rs b/minter/src/state/mod.rs index 36e97d10..c265d456 100644 --- a/minter/src/state/mod.rs +++ b/minter/src/state/mod.rs @@ -3,7 +3,9 @@ use crate::{ ledger::client::LedgerClient, numeric::{LedgerBurnIndex, LedgerMintIndex}, state::event::{DepositId, TransactionPurpose, VersionedMessage, WithdrawalRequest}, - utils::insertion_ordered_map::InsertionOrderedMap, + utils::{ + insertion_ordered_map::InsertionOrderedMap, insertion_ordered_set::InsertionOrderedSet, + }, }; use candid::Principal; use cksol_types::{DepositStatus, TxFinalizedStatus, WithdrawalStatus}; @@ -89,7 +91,7 @@ pub struct State { minimum_deposit_amount: Lamport, process_deposit_required_cycles: u128, deposit_consolidation_fee: u128, - monitored_accounts: BTreeSet, + monitored_accounts: InsertionOrderedSet, pending_process_deposit_request_guards: BTreeSet, pending_withdrawal_request_guards: BTreeSet, accepted_deposits: InsertionOrderedMap, @@ -244,7 +246,7 @@ impl State { self.balance } - pub fn monitored_accounts(&self) -> &BTreeSet { + pub fn monitored_accounts(&self) -> &InsertionOrderedSet { &self.monitored_accounts } @@ -776,7 +778,7 @@ impl TryFrom for State { minimum_deposit_amount, process_deposit_required_cycles: process_deposit_required_cycles as u128, deposit_consolidation_fee: deposit_consolidation_fee as u128, - monitored_accounts: BTreeSet::new(), + monitored_accounts: InsertionOrderedSet::new(), pending_process_deposit_request_guards: BTreeSet::new(), pending_withdrawal_request_guards: BTreeSet::new(), accepted_deposits: InsertionOrderedMap::new(), diff --git a/minter/src/state/tests.rs b/minter/src/state/tests.rs index a6afa737..e3ef55e3 100644 --- a/minter/src/state/tests.rs +++ b/minter/src/state/tests.rs @@ -17,7 +17,9 @@ use crate::{ runtime::TestCanisterRuntime, signature, sol_rpc_canister_id, valid_init_args, }, - utils::insertion_ordered_map::InsertionOrderedMap, + utils::{ + insertion_ordered_map::InsertionOrderedMap, insertion_ordered_set::InsertionOrderedSet, + }, }; use assert_matches::assert_matches; use cksol_types_internal::{Ed25519KeyName, InitArgs, SolanaNetwork, UpgradeArgs}; @@ -226,7 +228,7 @@ mod state_from_init_args { minimum_withdrawal_amount: MINIMUM_WITHDRAWAL_AMOUNT, minimum_deposit_amount: MINIMUM_DEPOSIT_AMOUNT, process_deposit_required_cycles: PROCESS_DEPOSIT_REQUIRED_CYCLES, - monitored_accounts: BTreeSet::new(), + monitored_accounts: InsertionOrderedSet::new(), pending_process_deposit_request_guards: BTreeSet::new(), pending_withdrawal_request_guards: BTreeSet::new(), accepted_deposits: InsertionOrderedMap::new(), diff --git a/minter/src/utils/insertion_ordered_set.rs b/minter/src/utils/insertion_ordered_set.rs new file mode 100644 index 00000000..2b775aba --- /dev/null +++ b/minter/src/utils/insertion_ordered_set.rs @@ -0,0 +1,60 @@ +use super::insertion_ordered_map::InsertionOrderedMap; + +/// An insertion-ordered set backed by an [`InsertionOrderedMap`]. +/// +/// Provides O(log n) membership tests and O(log n) insertion/removal, +/// while preserving insertion order during iteration. +pub struct InsertionOrderedSet(InsertionOrderedMap); + +impl InsertionOrderedSet { + pub fn new() -> Self { + Self(InsertionOrderedMap::new()) + } + + /// Inserts `key`. Returns `true` if the key was not already present. + pub fn insert(&mut self, key: K) -> bool { + self.0.insert(key, ()).is_none() + } + + /// Removes `key`. Returns `true` if the key was present. + pub fn remove(&mut self, key: &K) -> bool { + self.0.remove(key).is_some() + } + + pub fn contains(&self, key: &K) -> bool { + self.0.contains_key(key) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Iterates over keys in insertion order. + pub fn iter(&self) -> impl Iterator { + self.0.keys() + } +} + +impl Default for InsertionOrderedSet { + fn default() -> Self { + Self::new() + } +} + +impl PartialEq for InsertionOrderedSet { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for InsertionOrderedSet {} + +impl std::fmt::Debug for InsertionOrderedSet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} diff --git a/minter/src/utils/mod.rs b/minter/src/utils/mod.rs index cc87b6c5..5ca7437c 100644 --- a/minter/src/utils/mod.rs +++ b/minter/src/utils/mod.rs @@ -1 +1,2 @@ pub mod insertion_ordered_map; +pub mod insertion_ordered_set;