Skip to content

Commit 5ee6537

Browse files
committed
refactor(key-wallet): seed checkpoints in ManagedWalletInfo ctors
`WalletInfoInterface::from_wallet` and `from_wallet_with_name` (plus the inherent `ManagedWalletInfo` constructors) now take `birth_height: CoreBlockHeight` and seed both checkpoint heights (`synced_height`, `last_processed_height`) to `birth_height.saturating_sub(1)` so the next block to scan is always `birth_height`. Without this, every wallet-add path had to remember `set_birth_height` after construction. Forgetting it left the checkpoints at `0`, dragging `WalletManager::synced_height` (a min across wallets) back to genesis on every add. Moving the seed into construction makes the invariant a property of the type. `ManagedWalletInfo::with_birth_height` is removed. It set only `birth_height` and had no callers.
1 parent 659a6d5 commit 5ee6537

15 files changed

Lines changed: 91 additions & 76 deletions

File tree

key-wallet-ffi/src/managed_wallet.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ mod tests {
501501

502502
// Create managed wallet info from the wallet heap-allocated like C would do
503503
let wallet_rust = unsafe { &(*wallet).wallet };
504-
let managed_info = ManagedWalletInfo::from_wallet(wallet_rust);
504+
let managed_info = ManagedWalletInfo::from_wallet(wallet_rust, 0);
505505
let ffi_managed = Box::into_raw(Box::new(FFIManagedWalletInfo::new(managed_info)));
506506

507507
// Test get_next_receive_address with valid pointers
@@ -581,7 +581,7 @@ mod tests {
581581

582582
// We need to work with the existing wallet structure
583583
// Create managed wallet info from the existing wallet
584-
let mut managed_info = ManagedWalletInfo::from_wallet(wallet_arc);
584+
let mut managed_info = ManagedWalletInfo::from_wallet(wallet_arc, 0);
585585

586586
let network = key_wallet::Network::Testnet;
587587

@@ -759,7 +759,7 @@ mod tests {
759759

760760
// Create managed wallet info
761761
let wallet_arc = unsafe { &(*wallet_ptr).wallet };
762-
let mut managed_info = ManagedWalletInfo::from_wallet(wallet_arc);
762+
let mut managed_info = ManagedWalletInfo::from_wallet(wallet_arc, 0);
763763

764764
// Set some test balance values
765765
managed_info.balance = WalletCoreBalance::new(1000000, 50000, 10000, 25000);

key-wallet-ffi/src/transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ pub unsafe extern "C" fn wallet_check_transaction(
306306
use key_wallet::transaction_checking::WalletTransactionChecker;
307307
use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo;
308308

309-
let mut managed_info = ManagedWalletInfo::from_wallet(wallet.inner());
309+
let mut managed_info = ManagedWalletInfo::from_wallet(wallet.inner(), 0);
310310

311311
// Check the transaction - wallet is always required now
312312
let wallet_mut = unwrap_or_return!(wallet.inner_mut(), error);

key-wallet-ffi/src/transaction_checking.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub unsafe extern "C" fn wallet_create_managed_wallet(
7979
error: *mut FFIError,
8080
) -> *mut FFIManagedWalletInfo {
8181
let wallet = deref_ptr!(wallet, error);
82-
let managed_info = ManagedWalletInfo::from_wallet(wallet.inner());
82+
let managed_info = ManagedWalletInfo::from_wallet(wallet.inner(), 0);
8383
Box::into_raw(Box::new(FFIManagedWalletInfo::new(managed_info)))
8484
}
8585

key-wallet-manager/src/lib.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ impl<T: WalletInfoInterface + Send + Sync + 'static> WalletManager<T> {
156156

157157
// Create managed wallet info from the wallet to properly initialize accounts
158158
// This ensures the ManagedAccountCollection is synchronized with the Wallet's accounts
159-
let mut managed_info = T::from_wallet(&wallet);
160-
managed_info.set_birth_height(birth_height);
159+
let mut managed_info = T::from_wallet(&wallet, birth_height);
161160
managed_info.set_first_loaded_at(current_timestamp());
162161

163162
// The wallet already has accounts created according to the provided options
@@ -262,8 +261,7 @@ impl<T: WalletInfoInterface + Send + Sync + 'static> WalletManager<T> {
262261
})?;
263262

264263
// Add the wallet to the manager
265-
let mut managed_info = T::from_wallet(&final_wallet);
266-
managed_info.set_birth_height(birth_height);
264+
let mut managed_info = T::from_wallet(&final_wallet, birth_height);
267265
managed_info.set_first_loaded_at(current_timestamp());
268266

269267
self.wallets.insert(wallet_id, final_wallet);
@@ -297,8 +295,7 @@ impl<T: WalletInfoInterface + Send + Sync + 'static> WalletManager<T> {
297295
}
298296

299297
// Create managed wallet info
300-
let mut managed_info = T::from_wallet(&wallet);
301-
managed_info.set_birth_height(self.last_processed_height());
298+
let mut managed_info = T::from_wallet(&wallet, self.last_processed_height());
302299
managed_info.set_first_loaded_at(current_timestamp());
303300

304301
self.wallets.insert(wallet_id, wallet);
@@ -338,8 +335,7 @@ impl<T: WalletInfoInterface + Send + Sync + 'static> WalletManager<T> {
338335
}
339336

340337
// Create managed wallet info
341-
let mut managed_info = T::from_wallet(&wallet);
342-
managed_info.set_birth_height(self.last_processed_height());
338+
let mut managed_info = T::from_wallet(&wallet, self.last_processed_height());
343339
managed_info.set_first_loaded_at(current_timestamp());
344340

345341
self.wallets.insert(wallet_id, wallet);
@@ -386,8 +382,7 @@ impl<T: WalletInfoInterface + Send + Sync + 'static> WalletManager<T> {
386382
}
387383

388384
// Create managed wallet info
389-
let mut managed_info = T::from_wallet(&wallet);
390-
managed_info.set_birth_height(self.last_processed_height());
385+
let mut managed_info = T::from_wallet(&wallet, self.last_processed_height());
391386
managed_info.set_first_loaded_at(current_timestamp());
392387

393388
self.wallets.insert(wallet_id, wallet);
@@ -428,11 +423,9 @@ impl<T: WalletInfoInterface + Send + Sync + 'static> WalletManager<T> {
428423
return Err(WalletError::WalletExists(wallet_id));
429424
}
430425

431-
// Create managed wallet info from the imported wallet
432-
let mut managed_info = T::from_wallet(&wallet);
433-
434-
// Use the current height as the birth height since we don't know when it was originally created
435-
managed_info.set_birth_height(self.last_processed_height());
426+
// Create managed wallet info from the imported wallet, using the current chain
427+
// tip as the birth height since the serialized form does not preserve it.
428+
let mut managed_info = T::from_wallet(&wallet, self.last_processed_height());
436429
managed_info.set_first_loaded_at(current_timestamp());
437430

438431
self.wallets.insert(wallet_id, wallet);

key-wallet-manager/tests/integration_test.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,30 +187,36 @@ fn test_block_height_tracking() {
187187
.create_wallet_from_mnemonic(
188188
&mnemonic2.to_string(),
189189
"",
190-
0,
190+
5000,
191191
WalletAccountCreationOptions::Default,
192192
)
193193
.unwrap();
194194

195195
assert_eq!(manager.wallet_count(), 2);
196196

197-
// Verify both wallets have last_processed_height and synced_height of 0 initially
198-
for wallet_info in manager.get_all_wallet_infos().values() {
199-
assert_eq!(wallet_info.last_processed_height(), 0);
200-
assert_eq!(wallet_info.synced_height(), 0);
201-
}
197+
// wallet1 was created with birth_height = 0 so its sync checkpoint stays at 0;
198+
// wallet2 was created with birth_height = 5000 and is seeded to birth_height - 1.
199+
let wallet_info1 = manager.get_wallet_info(&wallet_id1).unwrap();
200+
let wallet_info2 = manager.get_wallet_info(&wallet_id2).unwrap();
201+
assert_eq!(wallet_info1.last_processed_height(), 0);
202+
assert_eq!(wallet_info1.synced_height(), 0);
203+
assert_eq!(wallet_info2.last_processed_height(), 4999);
204+
assert_eq!(wallet_info2.synced_height(), 4999);
205+
// Manager aggregates: synced_height is the min, last_processed_height is the max.
206+
assert_eq!(manager.synced_height(), 0);
207+
assert_eq!(manager.last_processed_height(), 4999);
202208

203209
// Update last-processed height - should propagate to all wallets
204210
manager.update_last_processed_height(12345);
205211
assert_eq!(manager.last_processed_height(), 12345);
206212

207-
// Verify all wallets got updated while synced_height stays at 0
213+
// Verify all wallets got updated; wallet2's synced_height still reflects its seed
208214
let wallet_info1 = manager.get_wallet_info(&wallet_id1).unwrap();
209215
let wallet_info2 = manager.get_wallet_info(&wallet_id2).unwrap();
210216
assert_eq!(wallet_info1.last_processed_height(), 12345);
211217
assert_eq!(wallet_info2.last_processed_height(), 12345);
212218
assert_eq!(wallet_info1.synced_height(), 0);
213-
assert_eq!(wallet_info2.synced_height(), 0);
219+
assert_eq!(wallet_info2.synced_height(), 4999);
214220

215221
// Update synced height - should propagate to all wallets without touching last_processed_height
216222
manager.update_synced_height(20000);

key-wallet-manager/tests/test_serialized_wallets.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#[cfg(test)]
33
mod tests {
44
use key_wallet::wallet::initialization::WalletAccountCreationOptions;
5+
use key_wallet::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface;
56
use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo;
67
use key_wallet::Network;
78
use key_wallet_manager::WalletManager;
@@ -26,6 +27,12 @@ mod tests {
2627
assert!(!bytes.is_empty());
2728
println!("Full wallet ID: {}", hex::encode(wallet_id));
2829

30+
// The wallet's sync checkpoint should be seeded to birth_height - 1.
31+
let info = manager.get_wallet_info(&wallet_id).unwrap();
32+
assert_eq!(info.birth_height(), 100_000);
33+
assert_eq!(info.synced_height(), 99_999);
34+
assert_eq!(info.last_processed_height(), 99_999);
35+
2936
// Test 2: Create watch-only wallet (no private keys)
3037
let mut manager2 = WalletManager::<ManagedWalletInfo>::new(Network::Testnet);
3138
let result = manager2.create_wallet_from_mnemonic_return_serialized_bytes(

key-wallet/src/test_utils/wallet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl TestWalletContext {
3131
let wallet = Wallet::new_random(Network::Testnet, WalletAccountCreationOptions::Default)
3232
.expect("Should create wallet");
3333
let mut managed_wallet =
34-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
34+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
3535

3636
let xpub = wallet
3737
.accounts

key-wallet/src/transaction_checking/transaction_router/tests/identity_transactions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ async fn test_identity_registration_account_routing() {
4444
wallet.add_account(account_type, None).expect("Failed to add account to wallet");
4545

4646
let mut managed_wallet_info =
47-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
47+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
4848

4949
// Get the identity registration account
5050
let account = wallet
@@ -155,7 +155,7 @@ async fn test_normal_payment_to_identity_address_not_detected() {
155155
let mut wallet = Wallet::new_random(network, WalletAccountCreationOptions::Default)
156156
.expect("Failed to create wallet with default options");
157157
let mut managed_wallet_info =
158-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
158+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
159159

160160
let account = wallet
161161
.accounts

key-wallet/src/transaction_checking/transaction_router/tests/provider.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ async fn test_provider_registration_transaction_routing_check_owner_only() {
136136
.expect("Failed to create wallet with default options");
137137

138138
let mut other_managed_wallet_info =
139-
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string());
139+
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string(), 0);
140140

141141
let mut managed_wallet_info =
142-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
142+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
143143

144144
// Get addresses from provider accounts
145145
let managed_owner = managed_wallet_info
@@ -271,10 +271,10 @@ async fn test_provider_registration_transaction_routing_check_voting_only() {
271271
.expect("Failed to create wallet with default options");
272272

273273
let mut other_managed_wallet_info =
274-
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string());
274+
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string(), 0);
275275

276276
let mut managed_wallet_info =
277-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
277+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
278278

279279
// Get addresses from provider accounts
280280
let owner_address = other_managed_wallet_info
@@ -406,10 +406,10 @@ async fn test_provider_registration_transaction_routing_check_operator_only() {
406406
.expect("Failed to create wallet with default options");
407407

408408
let mut other_managed_wallet_info =
409-
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string());
409+
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string(), 0);
410410

411411
let mut managed_wallet_info =
412-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
412+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
413413

414414
// Get addresses from provider accounts
415415
let owner_address = other_managed_wallet_info
@@ -587,10 +587,10 @@ async fn test_provider_registration_transaction_routing_check_platform_only() {
587587
.expect("Failed to create wallet with default options");
588588

589589
let mut other_managed_wallet_info =
590-
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string());
590+
ManagedWalletInfo::from_wallet_with_name(&other_wallet, "Other".to_string(), 0);
591591

592592
let mut managed_wallet_info =
593-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
593+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
594594

595595
// Get addresses from provider accounts
596596
let owner_address = other_managed_wallet_info
@@ -785,7 +785,7 @@ async fn test_provider_update_registrar_with_voting_and_operator() {
785785
.expect("Failed to create wallet with default options");
786786

787787
let mut managed_wallet_info =
788-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
788+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
789789

790790
// Get voting address
791791
let voting_address = managed_wallet_info
@@ -858,7 +858,7 @@ async fn test_provider_revocation_classification_and_routing() {
858858
.expect("Failed to create wallet with default options");
859859

860860
let mut managed_wallet_info =
861-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
861+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
862862

863863
// Get a standard address for collateral return
864864
let account = wallet

key-wallet/src/transaction_checking/transaction_router/tests/routing.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async fn test_transaction_routing_to_bip32_account() {
7777
wallet.add_account(account_type, None).expect("Failed to add account to wallet");
7878

7979
let mut managed_wallet_info =
80-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
80+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
8181

8282
// Get the account's xpub for address derivation
8383
let account = wallet
@@ -159,7 +159,7 @@ async fn test_transaction_routing_to_coinjoin_account() {
159159
wallet.add_account(account_type, None).expect("Failed to add account to wallet");
160160

161161
let mut managed_wallet_info =
162-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
162+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
163163

164164
// Get the account's xpub
165165
let account = wallet
@@ -258,7 +258,7 @@ async fn test_transaction_affects_multiple_accounts() {
258258
wallet.add_account(account_type, None).expect("Failed to add account to wallet");
259259

260260
let mut managed_wallet_info =
261-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
261+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
262262

263263
// Get addresses from different accounts
264264

@@ -362,7 +362,7 @@ fn test_next_address_method_restrictions() {
362362
let wallet = Wallet::new_random(Network::Testnet, WalletAccountCreationOptions::Default)
363363
.expect("Failed to create wallet with default options");
364364
let mut managed_wallet_info =
365-
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string());
365+
ManagedWalletInfo::from_wallet_with_name(&wallet, "Test".to_string(), 0);
366366

367367
// Test that standard BIP44 accounts reject next_address
368368
{

0 commit comments

Comments
 (0)