From 141304e33c14a3f2a5a4d7d22ee3494444846ded Mon Sep 17 00:00:00 2001 From: Jeremiah Jacob Date: Tue, 23 Jun 2026 02:27:49 +0100 Subject: [PATCH 1/4] test: cover wallet creation SQLite lifecycle --- test/integration/wallet_creation_test.dart | 121 +++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 test/integration/wallet_creation_test.dart diff --git a/test/integration/wallet_creation_test.dart b/test/integration/wallet_creation_test.dart new file mode 100644 index 0000000..8784673 --- /dev/null +++ b/test/integration/wallet_creation_test.dart @@ -0,0 +1,121 @@ +// @Tags(['integration']) + +import 'dart:io'; + +import 'package:bdk_dart/bdk.dart'; +import 'package:test/test.dart'; + +import '../test_constants.dart'; + +Future _deleteDirectoryWithRetry(Directory directory) async { + for (var attempt = 0; attempt < 10; attempt++) { + try { + if (directory.existsSync()) { + await directory.delete(recursive: true); + } + return; + } on PathAccessException { + await Future.delayed(const Duration(milliseconds: 100)); + } + } + + if (directory.existsSync()) { + await directory.delete(recursive: true); + } +} + +void main() { + group('Wallet creation integration', () { + test('persists derivation state across a SQLite reopen', () { + final tempDir = Directory.systemTemp.createTempSync( + 'bdk_dart_wallet_creation_', + ); + addTearDown(() => _deleteDirectoryWithRetry(tempDir)); + + final dbPath = '${tempDir.path}/wallet.sqlite'; + final descriptor = buildBip84Descriptor(Network.testnet); + final changeDescriptor = buildBip84ChangeDescriptor(Network.testnet); + Persister? persister; + Wallet? wallet; + Persister? reopenedPersister; + Wallet? reopenedWallet; + + try { + expect(File(dbPath).existsSync(), isFalse); + + final initialPersister = Persister.newSqlite(path: dbPath); + persister = initialPersister; + + wallet = Wallet( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + network: Network.testnet, + persister: initialPersister, + lookahead: defaultLookahead, + ); + + final externalAddress = wallet.revealNextAddress( + keychain: KeychainKind.external_, + ); + final internalAddress = wallet.revealNextAddress( + keychain: KeychainKind.internal, + ); + + expect(externalAddress.index, equals(0)); + expect(internalAddress.index, equals(0)); + expect(wallet.persist(persister: initialPersister), isTrue); + + wallet.dispose(); + wallet = null; + persister.dispose(); + persister = null; + + expect(File(dbPath).existsSync(), isTrue); + + reopenedPersister = Persister.newSqlite(path: dbPath); + reopenedWallet = Wallet.load( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + persister: reopenedPersister, + lookahead: defaultLookahead, + ); + + expect(reopenedWallet.network(), equals(Network.testnet)); + expect(reopenedWallet.balance().total.toSat(), equals(0)); + expect( + reopenedWallet.nextDerivationIndex(keychain: KeychainKind.external_), + equals(1), + ); + expect( + reopenedWallet.nextDerivationIndex(keychain: KeychainKind.internal), + equals(1), + ); + + final reopenedExternalAddress = reopenedWallet.peekAddress( + keychain: KeychainKind.external_, + index: externalAddress.index, + ); + final reopenedInternalAddress = reopenedWallet.peekAddress( + keychain: KeychainKind.internal, + index: internalAddress.index, + ); + + expect( + reopenedExternalAddress.address.scriptPubkey().toBytes(), + orderedEquals(externalAddress.address.scriptPubkey().toBytes()), + ); + expect( + reopenedInternalAddress.address.scriptPubkey().toBytes(), + orderedEquals(internalAddress.address.scriptPubkey().toBytes()), + ); + } finally { + reopenedWallet?.dispose(); + reopenedPersister?.dispose(); + wallet?.dispose(); + persister?.dispose(); + descriptor.dispose(); + changeDescriptor.dispose(); + } + }); + }); +} From 48bd64467b6989101d9e8a97fbab59920fe6ce92 Mon Sep 17 00:00:00 2001 From: Jeremiah Jacob Date: Fri, 26 Jun 2026 12:12:51 +0100 Subject: [PATCH 2/4] test: cover multiple SQLite wallet address reveals --- test/integration/wallet_creation_test.dart | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/test/integration/wallet_creation_test.dart b/test/integration/wallet_creation_test.dart index 8784673..334501e 100644 --- a/test/integration/wallet_creation_test.dart +++ b/test/integration/wallet_creation_test.dart @@ -117,5 +117,108 @@ void main() { changeDescriptor.dispose(); } }); + + test('cover multiple SQLite wallet address reveals', () { + final tempDir = Directory.systemTemp.createTempSync( + 'bdk_dart_wallet_creation_test1_', + ); + addTearDown(() => _deleteDirectoryWithRetry(tempDir)); + + final dbPath = '${tempDir.path}/wallet.sqlite'; + final descriptor = buildBip84Descriptor(Network.testnet); + final changeDescriptor = buildBip84ChangeDescriptor(Network.testnet); + Persister? persister; + Wallet? wallet; + Persister? reopenedPersister; + Wallet? reopenedWallet; + + try { + final initialPersister = Persister.newSqlite(path: dbPath); + persister = initialPersister; + + wallet = Wallet( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + network: Network.testnet, + persister: initialPersister, + lookahead: defaultLookahead, + ); + + final ext0 = wallet.revealNextAddress(keychain: KeychainKind.external_); + final ext1 = wallet.revealNextAddress(keychain: KeychainKind.external_); + final ext2 = wallet.revealNextAddress(keychain: KeychainKind.external_); + final int0 = wallet.revealNextAddress(keychain: KeychainKind.internal); + + expect(ext0.index, equals(0)); + expect(ext1.index, equals(1)); + expect(ext2.index, equals(2)); + expect(int0.index, equals(0)); + + expect(wallet.persist(persister: initialPersister), isTrue); + + wallet.dispose(); + wallet = null; + persister.dispose(); + persister = null; + + reopenedPersister = Persister.newSqlite(path: dbPath); + reopenedWallet = Wallet.load( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + persister: reopenedPersister, + lookahead: defaultLookahead, + ); + + expect( + reopenedWallet.nextDerivationIndex(keychain: KeychainKind.external_), + equals(3), + ); + expect( + reopenedWallet.nextDerivationIndex(keychain: KeychainKind.internal), + equals(1), + ); + + final reopenedExt0 = reopenedWallet.peekAddress( + keychain: KeychainKind.external_, + index: 0, + ); + final reopenedExt1 = reopenedWallet.peekAddress( + keychain: KeychainKind.external_, + index: 1, + ); + final reopenedExt2 = reopenedWallet.peekAddress( + keychain: KeychainKind.external_, + index: 2, + ); + final reopenedInt0 = reopenedWallet.peekAddress( + keychain: KeychainKind.internal, + index: 0, + ); + + expect( + reopenedExt0.address.scriptPubkey().toBytes(), + orderedEquals(ext0.address.scriptPubkey().toBytes()), + ); + expect( + reopenedExt1.address.scriptPubkey().toBytes(), + orderedEquals(ext1.address.scriptPubkey().toBytes()), + ); + expect( + reopenedExt2.address.scriptPubkey().toBytes(), + orderedEquals(ext2.address.scriptPubkey().toBytes()), + ); + expect( + reopenedInt0.address.scriptPubkey().toBytes(), + orderedEquals(int0.address.scriptPubkey().toBytes()), + ); + } finally { + reopenedWallet?.dispose(); + reopenedPersister?.dispose(); + wallet?.dispose(); + persister?.dispose(); + descriptor.dispose(); + changeDescriptor.dispose(); + } + }); }); } From a72b1264d87711b3853ac1108dc6b3c5a6400419 Mon Sep 17 00:00:00 2001 From: Jeremiah Jacob Date: Fri, 26 Jun 2026 12:13:04 +0100 Subject: [PATCH 3/4] test: cover explicit SQLite wallet persist contract --- test/integration/wallet_creation_test.dart | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/test/integration/wallet_creation_test.dart b/test/integration/wallet_creation_test.dart index 334501e..dc3e7c3 100644 --- a/test/integration/wallet_creation_test.dart +++ b/test/integration/wallet_creation_test.dart @@ -220,5 +220,66 @@ void main() { changeDescriptor.dispose(); } }); + + test('cover explicit SQLite wallet persist contract', () { + final tempDir = Directory.systemTemp.createTempSync( + 'bdk_dart_wallet_creation_test2_', + ); + addTearDown(() => _deleteDirectoryWithRetry(tempDir)); + + final dbPath = '${tempDir.path}/wallet.sqlite'; + final descriptor = buildBip84Descriptor(Network.testnet); + final changeDescriptor = buildBip84ChangeDescriptor(Network.testnet); + Persister? persister; + Wallet? wallet; + Persister? reopenedPersister; + Wallet? reopenedWallet; + + try { + final initialPersister = Persister.newSqlite(path: dbPath); + persister = initialPersister; + + wallet = Wallet( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + network: Network.testnet, + persister: initialPersister, + lookahead: defaultLookahead, + ); + + final ext0 = wallet.revealNextAddress(keychain: KeychainKind.external_); + expect(ext0.index, equals(0)); + + expect(wallet.persist(persister: initialPersister), isTrue); + + final ext1 = wallet.revealNextAddress(keychain: KeychainKind.external_); + expect(ext1.index, equals(1)); + + wallet.dispose(); + wallet = null; + persister.dispose(); + persister = null; + + reopenedPersister = Persister.newSqlite(path: dbPath); + reopenedWallet = Wallet.load( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + persister: reopenedPersister, + lookahead: defaultLookahead, + ); + + expect( + reopenedWallet.nextDerivationIndex(keychain: KeychainKind.external_), + equals(1), + ); + } finally { + reopenedWallet?.dispose(); + reopenedPersister?.dispose(); + wallet?.dispose(); + persister?.dispose(); + descriptor.dispose(); + changeDescriptor.dispose(); + } + }); }); } From 08997e587da09fdef9e6d289cc5f6d0fb2f5b766 Mon Sep 17 00:00:00 2001 From: Jeremiah Jacob Date: Fri, 26 Jun 2026 12:13:18 +0100 Subject: [PATCH 4/4] test: cover independent SQLite keychain indexes --- test/integration/wallet_creation_test.dart | 91 ++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/test/integration/wallet_creation_test.dart b/test/integration/wallet_creation_test.dart index dc3e7c3..36ad054 100644 --- a/test/integration/wallet_creation_test.dart +++ b/test/integration/wallet_creation_test.dart @@ -281,5 +281,96 @@ void main() { changeDescriptor.dispose(); } }); + + test('cover independent SQLite keychain indexes', () { + final tempDir = Directory.systemTemp.createTempSync( + 'bdk_dart_wallet_creation_test3_', + ); + addTearDown(() => _deleteDirectoryWithRetry(tempDir)); + + final dbPath = '${tempDir.path}/wallet.sqlite'; + final descriptor = buildBip84Descriptor(Network.testnet); + final changeDescriptor = buildBip84ChangeDescriptor(Network.testnet); + Persister? persister; + Wallet? wallet; + Persister? reopenedPersister; + Wallet? reopenedWallet; + + try { + final initialPersister = Persister.newSqlite(path: dbPath); + persister = initialPersister; + + wallet = Wallet( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + network: Network.testnet, + persister: initialPersister, + lookahead: defaultLookahead, + ); + + final ext0 = wallet.revealNextAddress(keychain: KeychainKind.external_); + final ext1 = wallet.revealNextAddress(keychain: KeychainKind.external_); + final ext2 = wallet.revealNextAddress(keychain: KeychainKind.external_); + + final int0 = wallet.revealNextAddress(keychain: KeychainKind.internal); + final int1 = wallet.revealNextAddress(keychain: KeychainKind.internal); + + expect(ext0.index, equals(0)); + expect(ext1.index, equals(1)); + expect(ext2.index, equals(2)); + + expect(int0.index, equals(0)); + expect(int1.index, equals(1)); + + expect(wallet.persist(persister: initialPersister), isTrue); + + wallet.dispose(); + wallet = null; + persister.dispose(); + persister = null; + + reopenedPersister = Persister.newSqlite(path: dbPath); + reopenedWallet = Wallet.load( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + persister: reopenedPersister, + lookahead: defaultLookahead, + ); + + expect( + reopenedWallet.nextDerivationIndex(keychain: KeychainKind.external_), + equals(3), + ); + expect( + reopenedWallet.nextDerivationIndex(keychain: KeychainKind.internal), + equals(2), + ); + + final reopenedExt1 = reopenedWallet.peekAddress( + keychain: KeychainKind.external_, + index: 1, + ); + final reopenedInt0 = reopenedWallet.peekAddress( + keychain: KeychainKind.internal, + index: 0, + ); + + expect( + reopenedExt1.address.scriptPubkey().toBytes(), + orderedEquals(ext1.address.scriptPubkey().toBytes()), + ); + expect( + reopenedInt0.address.scriptPubkey().toBytes(), + orderedEquals(int0.address.scriptPubkey().toBytes()), + ); + } finally { + reopenedWallet?.dispose(); + reopenedPersister?.dispose(); + wallet?.dispose(); + persister?.dispose(); + descriptor.dispose(); + changeDescriptor.dispose(); + } + }); }); }