Skip to content

Commit 5b0ef0d

Browse files
authored
refactor: cleanup WalletTransactionChecker::check_core_transaction (#402)
- Moves parts of it out into separate functions/helpers - Combines the gap limit maintenance cases since the `ManagedAccountType::Standard` is also already handled in `ManagedAccountType::address_pools_mut` - Early return in case of an already known transaction, adding the record again and updating UTXOs is no-op if the transaction is known already.
1 parent fd66fef commit 5b0ef0d

File tree

3 files changed

+309
-323
lines changed

3 files changed

+309
-323
lines changed

key-wallet/src/managed_account/managed_account_collection.rs

Lines changed: 90 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,87 @@ use alloc::vec::Vec;
2121
#[cfg(feature = "serde")]
2222
use serde::{Deserialize, Serialize};
2323

24+
/// Macro to look up an account by CoreAccountTypeMatch, parameterized by accessor methods
25+
macro_rules! get_by_account_type_match_impl {
26+
($self:expr, $match:expr, $get:ident, $as_opt:ident, $values:ident) => {
27+
match $match {
28+
CoreAccountTypeMatch::StandardBIP44 {
29+
account_index,
30+
..
31+
} => $self.standard_bip44_accounts.$get(account_index),
32+
CoreAccountTypeMatch::StandardBIP32 {
33+
account_index,
34+
..
35+
} => $self.standard_bip32_accounts.$get(account_index),
36+
CoreAccountTypeMatch::CoinJoin {
37+
account_index,
38+
..
39+
} => $self.coinjoin_accounts.$get(account_index),
40+
CoreAccountTypeMatch::IdentityRegistration {
41+
..
42+
} => $self.identity_registration.$as_opt(),
43+
CoreAccountTypeMatch::IdentityTopUp {
44+
account_index,
45+
..
46+
} => $self.identity_topup.$get(account_index),
47+
CoreAccountTypeMatch::IdentityTopUpNotBound {
48+
..
49+
} => $self.identity_topup_not_bound.$as_opt(),
50+
CoreAccountTypeMatch::IdentityInvitation {
51+
..
52+
} => $self.identity_invitation.$as_opt(),
53+
CoreAccountTypeMatch::ProviderVotingKeys {
54+
..
55+
} => $self.provider_voting_keys.$as_opt(),
56+
CoreAccountTypeMatch::ProviderOwnerKeys {
57+
..
58+
} => $self.provider_owner_keys.$as_opt(),
59+
CoreAccountTypeMatch::ProviderOperatorKeys {
60+
..
61+
} => $self.provider_operator_keys.$as_opt(),
62+
CoreAccountTypeMatch::ProviderPlatformKeys {
63+
..
64+
} => $self.provider_platform_keys.$as_opt(),
65+
CoreAccountTypeMatch::DashpayReceivingFunds {
66+
account_index,
67+
involved_addresses,
68+
} => $self.dashpay_receival_accounts.$values().find(|account| {
69+
match &account.account_type {
70+
ManagedAccountType::DashpayReceivingFunds {
71+
index,
72+
addresses,
73+
..
74+
} => {
75+
*index == *account_index
76+
&& involved_addresses
77+
.iter()
78+
.any(|addr| addresses.contains_address(&addr.address))
79+
}
80+
_ => false,
81+
}
82+
}),
83+
CoreAccountTypeMatch::DashpayExternalAccount {
84+
account_index,
85+
involved_addresses,
86+
} => $self.dashpay_external_accounts.$values().find(|account| {
87+
match &account.account_type {
88+
ManagedAccountType::DashpayExternalAccount {
89+
index,
90+
addresses,
91+
..
92+
} => {
93+
*index == *account_index
94+
&& involved_addresses
95+
.iter()
96+
.any(|addr| addresses.contains_address(&addr.address))
97+
}
98+
_ => false,
99+
}
100+
}),
101+
}
102+
};
103+
}
104+
24105
/// Collection of managed accounts organized by type
25106
#[derive(Debug, Clone, Default)]
26107
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -728,87 +809,15 @@ impl ManagedAccountCollection {
728809
&self,
729810
account_type_match: &CoreAccountTypeMatch,
730811
) -> Option<&ManagedCoreAccount> {
731-
match account_type_match {
732-
CoreAccountTypeMatch::StandardBIP44 {
733-
account_index,
734-
..
735-
} => self.standard_bip44_accounts.get(account_index),
736-
CoreAccountTypeMatch::StandardBIP32 {
737-
account_index,
738-
..
739-
} => self.standard_bip32_accounts.get(account_index),
740-
CoreAccountTypeMatch::CoinJoin {
741-
account_index,
742-
..
743-
} => self.coinjoin_accounts.get(account_index),
744-
CoreAccountTypeMatch::IdentityRegistration {
745-
..
746-
} => self.identity_registration.as_ref(),
747-
CoreAccountTypeMatch::IdentityTopUp {
748-
account_index,
749-
..
750-
} => self.identity_topup.get(account_index),
751-
CoreAccountTypeMatch::IdentityTopUpNotBound {
752-
..
753-
} => self.identity_topup_not_bound.as_ref(),
754-
CoreAccountTypeMatch::IdentityInvitation {
755-
..
756-
} => self.identity_invitation.as_ref(),
757-
CoreAccountTypeMatch::ProviderVotingKeys {
758-
..
759-
} => self.provider_voting_keys.as_ref(),
760-
CoreAccountTypeMatch::ProviderOwnerKeys {
761-
..
762-
} => self.provider_owner_keys.as_ref(),
763-
CoreAccountTypeMatch::ProviderOperatorKeys {
764-
..
765-
} => self.provider_operator_keys.as_ref(),
766-
CoreAccountTypeMatch::ProviderPlatformKeys {
767-
..
768-
} => self.provider_platform_keys.as_ref(),
769-
CoreAccountTypeMatch::DashpayReceivingFunds {
770-
account_index,
771-
involved_addresses,
772-
} => {
773-
// Match by index and addresses since multiple accounts can share the same index
774-
self.dashpay_receival_accounts.values().find(|account| {
775-
match &account.account_type {
776-
ManagedAccountType::DashpayReceivingFunds {
777-
index,
778-
addresses,
779-
..
780-
} => {
781-
*index == *account_index
782-
&& involved_addresses
783-
.iter()
784-
.any(|addr| addresses.contains_address(&addr.address))
785-
}
786-
_ => false,
787-
}
788-
})
789-
}
790-
CoreAccountTypeMatch::DashpayExternalAccount {
791-
account_index,
792-
involved_addresses,
793-
} => {
794-
// Match by index and addresses since multiple accounts can share the same index
795-
self.dashpay_external_accounts.values().find(|account| {
796-
match &account.account_type {
797-
ManagedAccountType::DashpayExternalAccount {
798-
index,
799-
addresses,
800-
..
801-
} => {
802-
*index == *account_index
803-
&& involved_addresses
804-
.iter()
805-
.any(|addr| addresses.contains_address(&addr.address))
806-
}
807-
_ => false,
808-
}
809-
})
810-
}
811-
}
812+
get_by_account_type_match_impl!(self, account_type_match, get, as_ref, values)
813+
}
814+
815+
/// Get a mutable account reference by AccountTypeMatch
816+
pub fn get_by_account_type_match_mut(
817+
&mut self,
818+
account_type_match: &CoreAccountTypeMatch,
819+
) -> Option<&mut ManagedCoreAccount> {
820+
get_by_account_type_match_impl!(self, account_type_match, get_mut, as_mut, values_mut)
812821
}
813822

814823
/// Remove an account from the collection

key-wallet/src/managed_account/mod.rs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,20 @@ use crate::account::TransactionRecord;
1414
use crate::derivation_bls_bip32::ExtendedBLSPubKey;
1515
#[cfg(any(feature = "bls", feature = "eddsa"))]
1616
use crate::managed_account::address_pool::PublicKeyType;
17+
use crate::transaction_checking::{AccountMatch, TransactionContext};
1718
use crate::utxo::Utxo;
1819
use crate::wallet::balance::WalletCoreBalance;
1920
#[cfg(feature = "eddsa")]
2021
use crate::AddressInfo;
2122
use crate::{ExtendedPubKey, Network};
2223
use alloc::collections::BTreeMap;
2324
use dashcore::blockdata::transaction::OutPoint;
24-
use dashcore::Txid;
2525
use dashcore::{Address, ScriptBuf};
26+
use dashcore::{Transaction, Txid};
2627
use managed_account_type::ManagedAccountType;
2728
#[cfg(feature = "serde")]
2829
use serde::{Deserialize, Serialize};
30+
use std::collections::BTreeSet;
2931

3032
pub mod address_pool;
3133
pub mod managed_account_collection;
@@ -266,6 +268,95 @@ impl ManagedCoreAccount {
266268
self.account_type.mark_address_used(address)
267269
}
268270

271+
/// Add new ones for received outputs, remove spent ones
272+
fn update_utxos(
273+
&mut self,
274+
tx: &Transaction,
275+
account_match: &AccountMatch,
276+
context: TransactionContext,
277+
) {
278+
// Update UTXOs only for spendable account types
279+
match &mut self.account_type {
280+
ManagedAccountType::Standard {
281+
..
282+
}
283+
| ManagedAccountType::CoinJoin {
284+
..
285+
}
286+
| ManagedAccountType::DashpayReceivingFunds {
287+
..
288+
}
289+
| ManagedAccountType::DashpayExternalAccount {
290+
..
291+
} => {
292+
let involved_addrs: BTreeSet<_> = account_match
293+
.account_type_match
294+
.all_involved_addresses()
295+
.iter()
296+
.map(|info| info.address.clone())
297+
.collect();
298+
299+
let txid = tx.txid();
300+
301+
// Insert UTXOs for outputs paying to our addresses
302+
for (vout, output) in tx.output.iter().enumerate() {
303+
if let Ok(addr) = Address::from_script(&output.script_pubkey, self.network) {
304+
if involved_addrs.contains(&addr) {
305+
let outpoint = OutPoint {
306+
txid,
307+
vout: vout as u32,
308+
};
309+
let txout = dashcore::TxOut {
310+
value: output.value,
311+
script_pubkey: output.script_pubkey.clone(),
312+
};
313+
let mut utxo = Utxo::new(
314+
outpoint,
315+
txout,
316+
addr,
317+
context.block_height().unwrap_or(0),
318+
tx.is_coin_base(),
319+
);
320+
utxo.is_confirmed = context.confirmed();
321+
self.utxos.insert(outpoint, utxo);
322+
}
323+
}
324+
}
325+
326+
// Remove UTXOs spent by this transaction
327+
for input in &tx.input {
328+
self.utxos.remove(&input.previous_output);
329+
}
330+
}
331+
_ => {}
332+
}
333+
}
334+
335+
/// Record a new transaction and update UTXOs for spendable account types
336+
pub(crate) fn record_transaction(
337+
&mut self,
338+
tx: &Transaction,
339+
account_match: &AccountMatch,
340+
context: TransactionContext,
341+
) {
342+
let net_amount = account_match.received as i64 - account_match.sent as i64;
343+
let tx_record = TransactionRecord {
344+
transaction: tx.clone(),
345+
txid: tx.txid(),
346+
height: context.block_height(),
347+
block_hash: context.block_hash(),
348+
timestamp: context.timestamp().unwrap_or(0) as u64,
349+
net_amount,
350+
fee: None,
351+
label: None,
352+
is_ours: net_amount < 0,
353+
};
354+
355+
self.transactions.insert(tx.txid(), tx_record);
356+
357+
self.update_utxos(tx, account_match, context);
358+
}
359+
269360
/// Update the account balance
270361
pub fn update_balance(&mut self, synced_height: u32) {
271362
let mut spendable = 0;

0 commit comments

Comments
 (0)