Skip to content

Commit 9727358

Browse files
more work
1 parent dc19c0d commit 9727358

File tree

2 files changed

+132
-37
lines changed

2 files changed

+132
-37
lines changed

key-wallet/src/managed_account/managed_platform_account.rs

Lines changed: 113 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//!
1010
//! The derivation path follows DIP-17: `m/9'/coin_type'/17'/account'/key_class'/index`
1111
12-
use super::address_pool::AddressPool;
12+
use super::address_pool::{AddressPool, KeySource};
1313
use super::metadata::AccountMetadata;
1414
use super::platform_address::PlatformP2PKHAddress;
1515
use crate::error::{Error, Result};
@@ -98,30 +98,63 @@ impl ManagedPlatformAccount {
9898

9999
/// Set the credit balance for a specific address
100100
///
101-
/// This also updates the total balance by recalculating from all address balances.
101+
/// This also updates the total balance by applying the delta.
102+
/// If the address was previously unfunded (balance 0) and becomes funded,
103+
/// and a `KeySource` is provided, the address will be marked as used
104+
/// and the gap limit will be maintained.
102105
pub fn set_address_credit_balance(
103106
&mut self,
104107
address: PlatformP2PKHAddress,
105108
credit_balance: u64,
109+
key_source: Option<&KeySource>,
106110
) {
111+
let old_balance = self.address_balances.get(&address).copied().unwrap_or(0);
112+
let was_unfunded = old_balance == 0;
113+
let is_now_funded = credit_balance > 0;
114+
107115
self.address_balances.insert(address, credit_balance);
108-
self.recalculate_credit_balance();
116+
// Apply delta to total: subtract old, add new
117+
self.credit_balance =
118+
self.credit_balance.saturating_sub(old_balance).saturating_add(credit_balance);
109119
self.metadata.last_used = Some(Self::current_timestamp());
120+
121+
// If address became funded and we have a key source, update address pool
122+
if was_unfunded && is_now_funded {
123+
if let Some(ks) = key_source {
124+
self.mark_and_maintain_gap_limit(&address, ks);
125+
}
126+
}
110127
}
111128

112129
/// Add credits to a specific address balance
113130
///
114131
/// Returns the new credit balance for the address.
132+
/// If the address was previously unfunded (balance 0) and becomes funded,
133+
/// and a `KeySource` is provided, the address will be marked as used
134+
/// and the gap limit will be maintained.
115135
pub fn add_address_credit_balance(
116136
&mut self,
117137
address: PlatformP2PKHAddress,
118138
amount: u64,
139+
key_source: Option<&KeySource>,
119140
) -> u64 {
120141
let current = self.address_balances.get(&address).copied().unwrap_or(0);
142+
let was_unfunded = current == 0;
121143
let new_balance = current.saturating_add(amount);
144+
let is_now_funded = new_balance > 0;
145+
122146
self.address_balances.insert(address, new_balance);
123-
self.recalculate_credit_balance();
147+
// Add the amount to the total (saturating to handle overflow)
148+
self.credit_balance = self.credit_balance.saturating_add(amount);
124149
self.metadata.last_used = Some(Self::current_timestamp());
150+
151+
// If address became funded and we have a key source, update address pool
152+
if was_unfunded && is_now_funded {
153+
if let Some(ks) = key_source {
154+
self.mark_and_maintain_gap_limit(&address, ks);
155+
}
156+
}
157+
125158
new_balance
126159
}
127160

@@ -135,9 +168,12 @@ impl ManagedPlatformAccount {
135168
amount: u64,
136169
) -> u64 {
137170
let current = self.address_balances.get(&address).copied().unwrap_or(0);
171+
// Only subtract what was actually removed (may be less due to saturating)
172+
let actual_removed = current.min(amount);
138173
let new_balance = current.saturating_sub(amount);
139174
self.address_balances.insert(address, new_balance);
140-
self.recalculate_credit_balance();
175+
// Subtract only what was actually removed from the total
176+
self.credit_balance = self.credit_balance.saturating_sub(actual_removed);
141177
self.metadata.last_used = Some(Self::current_timestamp());
142178
new_balance
143179
}
@@ -234,6 +270,34 @@ impl ManagedPlatformAccount {
234270
self.mark_address_used(&dashcore_addr)
235271
}
236272

273+
/// Mark a platform address as used and maintain the gap limit
274+
///
275+
/// This is called internally when an address receives funds for the first time.
276+
/// It marks the address as used in the address pool and generates new addresses
277+
/// if needed to maintain the gap limit.
278+
fn mark_and_maintain_gap_limit(
279+
&mut self,
280+
address: &PlatformP2PKHAddress,
281+
key_source: &KeySource,
282+
) {
283+
// Mark the address as used
284+
self.mark_platform_address_used(address);
285+
286+
// Maintain gap limit - generate new addresses if needed
287+
// We ignore errors here since this is a best-effort operation
288+
let _ = self.addresses.maintain_gap_limit(key_source);
289+
}
290+
291+
/// Maintain the gap limit for the address pool
292+
///
293+
/// This generates new addresses if needed to maintain the gap limit.
294+
/// Returns the newly generated addresses.
295+
pub fn maintain_gap_limit(&mut self, key_source: &KeySource) -> Result<Vec<Address>> {
296+
self.addresses
297+
.maintain_gap_limit(key_source)
298+
.map_err(|e| Error::InvalidParameter(format!("Failed to maintain gap limit: {}", e)))
299+
}
300+
237301
/// Get address info for a given address
238302
pub fn get_address_info(&self, address: &Address) -> Option<super::address_pool::AddressInfo> {
239303
self.addresses.address_info(address).cloned()
@@ -382,28 +446,51 @@ mod tests {
382446
let pool = create_test_pool();
383447
let mut account = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool, false);
384448

385-
// Set total credit balance
386-
account.set_credit_balance(1000);
387-
assert_eq!(account.total_credit_balance(), 1000);
388-
assert_eq!(account.duff_balance(), 1); // 1000 credits = 1 duff
389-
390-
// Set address credit balance
449+
// Set address credit balance (this also updates total)
391450
let addr = PlatformP2PKHAddress::new([0x11; 20]);
392-
account.set_address_credit_balance(addr, 500);
451+
account.set_address_credit_balance(addr, 500, None);
393452
assert_eq!(account.address_credit_balance(&addr), 500);
394-
assert_eq!(account.total_credit_balance(), 500); // Recalculated from address balances
453+
assert_eq!(account.total_credit_balance(), 500);
454+
assert_eq!(account.duff_balance(), 0); // 500 credits = 0 duffs (integer division)
395455

396456
// Add to address credit balance
397-
let new_balance = account.add_address_credit_balance(addr, 200);
398-
assert_eq!(new_balance, 700);
399-
assert_eq!(account.total_credit_balance(), 700);
457+
let new_balance = account.add_address_credit_balance(addr, 500, None);
458+
assert_eq!(new_balance, 1000);
459+
assert_eq!(account.total_credit_balance(), 1000);
460+
assert_eq!(account.duff_balance(), 1); // 1000 credits = 1 duff
461+
462+
// Add more
463+
let new_balance = account.add_address_credit_balance(addr, 200, None);
464+
assert_eq!(new_balance, 1200);
465+
assert_eq!(account.total_credit_balance(), 1200);
400466

401467
// Remove from address credit balance
402468
let new_balance = account.remove_address_credit_balance(addr, 100);
403-
assert_eq!(new_balance, 600);
469+
assert_eq!(new_balance, 1100);
470+
assert_eq!(account.total_credit_balance(), 1100);
471+
472+
// Update address balance directly (replacing existing)
473+
account.set_address_credit_balance(addr, 600, None);
474+
assert_eq!(account.address_credit_balance(&addr), 600);
404475
assert_eq!(account.total_credit_balance(), 600);
405476
}
406477

478+
#[test]
479+
fn test_set_credit_balance_directly() {
480+
let pool = create_test_pool();
481+
let mut account = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool, false);
482+
483+
// set_credit_balance is for direct manipulation (e.g., deserialization)
484+
// When used alone (no address balances), it just sets the total
485+
account.set_credit_balance(1000);
486+
assert_eq!(account.total_credit_balance(), 1000);
487+
assert_eq!(account.duff_balance(), 1);
488+
489+
account.set_credit_balance(2500);
490+
assert_eq!(account.total_credit_balance(), 2500);
491+
assert_eq!(account.duff_balance(), 2);
492+
}
493+
407494
#[test]
408495
fn test_duff_balance_conversion() {
409496
let pool = create_test_pool();
@@ -438,15 +525,15 @@ mod tests {
438525
let addr2 = PlatformP2PKHAddress::new([0x22; 20]);
439526
let addr3 = PlatformP2PKHAddress::new([0x33; 20]);
440527

441-
account.set_address_credit_balance(addr1, 100);
442-
account.set_address_credit_balance(addr2, 200);
443-
account.set_address_credit_balance(addr3, 300);
528+
account.set_address_credit_balance(addr1, 100, None);
529+
account.set_address_credit_balance(addr2, 200, None);
530+
account.set_address_credit_balance(addr3, 300, None);
444531

445532
assert_eq!(account.total_credit_balance(), 600);
446533
assert_eq!(account.funded_address_count(), 3);
447534

448535
// Set one to zero
449-
account.set_address_credit_balance(addr2, 0);
536+
account.set_address_credit_balance(addr2, 0, None);
450537
assert_eq!(account.total_credit_balance(), 400);
451538
assert_eq!(account.funded_address_count(), 2);
452539
}
@@ -457,7 +544,7 @@ mod tests {
457544
let mut account = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool, false);
458545

459546
let addr = PlatformP2PKHAddress::new([0x11; 20]);
460-
account.set_address_credit_balance(addr, 1000);
547+
account.set_address_credit_balance(addr, 1000, None);
461548

462549
account.clear_balances();
463550
assert_eq!(account.total_credit_balance(), 0);
@@ -473,9 +560,9 @@ mod tests {
473560
let addr2 = PlatformP2PKHAddress::new([0x22; 20]);
474561
let addr3 = PlatformP2PKHAddress::new([0x33; 20]);
475562

476-
account.set_address_credit_balance(addr1, 100);
477-
account.set_address_credit_balance(addr2, 0); // Zero balance
478-
account.set_address_credit_balance(addr3, 300);
563+
account.set_address_credit_balance(addr1, 100, None);
564+
account.set_address_credit_balance(addr2, 0, None); // Zero balance
565+
account.set_address_credit_balance(addr3, 300, None);
479566

480567
let funded = account.funded_addresses();
481568
assert_eq!(funded.len(), 2);
@@ -495,7 +582,7 @@ mod tests {
495582
assert!(!account.address_balances.contains_key(&addr));
496583

497584
// Add to balances
498-
account.set_address_credit_balance(addr, 100);
585+
account.set_address_credit_balance(addr, 100, None);
499586
assert!(account.contains_platform_address(&addr));
500587
}
501588
}

key-wallet/src/transaction_checking/platform_checker.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ impl WalletPlatformChecker for ManagedWalletInfo {
9797
// Find the account that contains this address
9898
for account in self.accounts.platform_payment_accounts.values_mut() {
9999
if account.contains_platform_address(address) {
100-
account.set_address_credit_balance(*address, credit_balance);
100+
// Note: Gap limit maintenance should be done at a higher level
101+
// where the Account object is available
102+
account.set_address_credit_balance(*address, credit_balance, None);
101103
return true;
102104
}
103105
}
@@ -111,7 +113,9 @@ impl WalletPlatformChecker for ManagedWalletInfo {
111113
credit_balance: u64,
112114
) -> bool {
113115
if let Some(account) = self.accounts.platform_payment_accounts.get_mut(account_key) {
114-
account.set_address_credit_balance(address, credit_balance);
116+
// Note: Gap limit maintenance should be done at a higher level
117+
// where the Account object is available
118+
account.set_address_credit_balance(address, credit_balance, None);
115119
true
116120
} else {
117121
false
@@ -126,7 +130,9 @@ impl WalletPlatformChecker for ManagedWalletInfo {
126130
// Find the account that contains this address
127131
for account in self.accounts.platform_payment_accounts.values_mut() {
128132
if account.contains_platform_address(address) {
129-
let new_balance = account.add_address_credit_balance(*address, amount);
133+
// Note: Gap limit maintenance should be done at a higher level
134+
// where the Account object is available
135+
let new_balance = account.add_address_credit_balance(*address, amount, None);
130136
return Some(new_balance);
131137
}
132138
}
@@ -140,7 +146,9 @@ impl WalletPlatformChecker for ManagedWalletInfo {
140146
amount: u64,
141147
) -> Option<u64> {
142148
if let Some(account) = self.accounts.platform_payment_accounts.get_mut(account_key) {
143-
let new_balance = account.add_address_credit_balance(address, amount);
149+
// Note: Gap limit maintenance should be done at a higher level
150+
// where the Account object is available
151+
let new_balance = account.add_address_credit_balance(address, amount, None);
144152
Some(new_balance)
145153
} else {
146154
None
@@ -218,7 +226,7 @@ mod tests {
218226

219227
// Add some balance
220228
let addr = PlatformP2PKHAddress::new([0x11; 20]);
221-
account.set_address_credit_balance(addr, 5000);
229+
account.set_address_credit_balance(addr, 5000, None);
222230

223231
let key = PlatformPaymentAccountKey {
224232
account: 0,
@@ -238,13 +246,13 @@ mod tests {
238246
let pool1 = create_test_pool();
239247
let mut account1 = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool1, false);
240248
let addr1 = PlatformP2PKHAddress::new([0x11; 20]);
241-
account1.set_address_credit_balance(addr1, 3000);
249+
account1.set_address_credit_balance(addr1, 3000, None);
242250

243251
// Create second platform account
244252
let pool2 = create_test_pool();
245253
let mut account2 = ManagedPlatformAccount::new(1, 0, Network::Testnet, pool2, false);
246254
let addr2 = PlatformP2PKHAddress::new([0x22; 20]);
247-
account2.set_address_credit_balance(addr2, 2000);
255+
account2.set_address_credit_balance(addr2, 2000, None);
248256

249257
wallet_info.accounts.platform_payment_accounts.insert(
250258
PlatformPaymentAccountKey {
@@ -273,7 +281,7 @@ mod tests {
273281
let pool = create_test_pool();
274282
let mut account = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool, false);
275283
let addr = PlatformP2PKHAddress::new([0x11; 20]);
276-
account.set_address_credit_balance(addr, 1000);
284+
account.set_address_credit_balance(addr, 1000, None);
277285

278286
let key = PlatformPaymentAccountKey {
279287
account: 0,
@@ -300,7 +308,7 @@ mod tests {
300308
let pool = create_test_pool();
301309
let mut account = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool, false);
302310
let addr = PlatformP2PKHAddress::new([0x11; 20]);
303-
account.set_address_credit_balance(addr, 1000);
311+
account.set_address_credit_balance(addr, 1000, None);
304312

305313
let key = PlatformPaymentAccountKey {
306314
account: 0,
@@ -327,7 +335,7 @@ mod tests {
327335
let pool = create_test_pool();
328336
let mut account = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool, false);
329337
let addr = PlatformP2PKHAddress::new([0x11; 20]);
330-
account.set_address_credit_balance(addr, 5000);
338+
account.set_address_credit_balance(addr, 5000, None);
331339

332340
let key = PlatformPaymentAccountKey {
333341
account: 0,
@@ -352,7 +360,7 @@ mod tests {
352360
let pool = create_test_pool();
353361
let mut account = ManagedPlatformAccount::new(0, 0, Network::Testnet, pool, false);
354362
let addr = PlatformP2PKHAddress::new([0x11; 20]);
355-
account.set_address_credit_balance(addr, 3000);
363+
account.set_address_credit_balance(addr, 3000, None);
356364

357365
let key = PlatformPaymentAccountKey {
358366
account: 0,

0 commit comments

Comments
 (0)