mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
fix: discovery & startSync [skip ci]
This commit is contained in:
parent
67ac4e8d97
commit
2502a14b50
7 changed files with 337 additions and 220 deletions
|
@ -114,68 +114,92 @@ abstract class BitcoinWalletBase extends ElectrumWallet<BitcoinWalletAddresses>
|
|||
}
|
||||
|
||||
@override
|
||||
void initAddresses() async {
|
||||
// if (didInitAddresses) return;
|
||||
Future<bool?> initAddresses([bool? sync]) async {
|
||||
var isDiscovered = await super.initAddresses(sync);
|
||||
bool? discovered;
|
||||
|
||||
// If already loaded, no need to generate/discover all initial addresses
|
||||
// so skip
|
||||
// if (!walletAddresses.loadedFromNewSnapshot) {
|
||||
for (final walletAddressType in walletAddresses.walletAddressTypes) {
|
||||
if (isHardwareWallet && walletAddressType != SegwitAddressType.p2wpkh) continue;
|
||||
// NOTE: will initiate by priority from the first walletAddressTypes
|
||||
// then proceeds to following ones after got fully discovered response from worker response
|
||||
if (isDiscovered != false) {
|
||||
for (final addressType in walletAddresses.walletAddressTypes) {
|
||||
if (isHardwareWallet && addressType != SegwitAddressType.p2wpkh) continue;
|
||||
|
||||
for (final seedBytesType in hdWallets.keys) {
|
||||
generateInitialAddresses(
|
||||
addressType: walletAddressType,
|
||||
seedBytesType: seedBytesType,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (final seedBytesType in hdWallets.keys) {
|
||||
// p2wpkh has always had the right derivations, skip if creating old derivations
|
||||
if (seedBytesType.isOldDerivation && addressType == SegwitAddressType.p2wpkh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
final bitcoinDerivationInfo = BitcoinAddressUtils.getDerivationFromType(
|
||||
addressType,
|
||||
network: network,
|
||||
isElectrum: seedBytesType.isElectrum,
|
||||
);
|
||||
|
||||
@override
|
||||
@action
|
||||
void generateInitialAddresses({
|
||||
required BitcoinAddressType addressType,
|
||||
required SeedBytesType seedBytesType,
|
||||
BitcoinDerivationInfo? bitcoinDerivationInfo,
|
||||
}) {
|
||||
// p2wpkh has always had the right derivations, skip if creating old derivations
|
||||
if (seedBytesType.isOldDerivation && addressType == SegwitAddressType.p2wpkh) {
|
||||
return;
|
||||
}
|
||||
bool alreadyDidDerivation = false;
|
||||
|
||||
final bitcoinDerivationInfo = BitcoinAddressUtils.getDerivationFromType(
|
||||
addressType,
|
||||
network: network,
|
||||
isElectrum: seedBytesType.isElectrum,
|
||||
);
|
||||
for (final derivationInfo in [
|
||||
bitcoinDerivationInfo,
|
||||
BitcoinDerivationInfos.BIP84,
|
||||
BitcoinDerivationInfos.ELECTRUM,
|
||||
]) {
|
||||
final derivationPath = derivationInfo.derivationPath.toString();
|
||||
|
||||
if (seedBytesType.isOldDerivation) {
|
||||
for (final derivationInfo in [
|
||||
bitcoinDerivationInfo,
|
||||
BitcoinDerivationInfos.ELECTRUM,
|
||||
BitcoinDerivationInfos.BIP84,
|
||||
]) {
|
||||
if (derivationInfo.derivationPath.toString() ==
|
||||
bitcoinDerivationInfo.derivationPath.toString()) {
|
||||
continue;
|
||||
if (alreadyDidDerivation &&
|
||||
derivationPath == bitcoinDerivationInfo.derivationPath.toString()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
alreadyDidDerivation = true;
|
||||
|
||||
for (final isChange in [true, false]) {
|
||||
isDiscovered = walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: derivationPath,
|
||||
isChange: isChange,
|
||||
);
|
||||
|
||||
if (isDiscovered == false) {
|
||||
break;
|
||||
} else if (sync == true)
|
||||
subscribeForStatuses(
|
||||
walletAddresses.addressesRecords
|
||||
.getRecords(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: derivationPath,
|
||||
isChange: isChange,
|
||||
)
|
||||
.whereType<BitcoinAddressRecord>()
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
if (isDiscovered == false) {
|
||||
discovered = await generateInitialAddresses(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
bitcoinDerivationInfo: derivationInfo,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDiscovered == false) break;
|
||||
}
|
||||
|
||||
super.generateInitialAddresses(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
bitcoinDerivationInfo: derivationInfo,
|
||||
);
|
||||
if (isDiscovered == false) break;
|
||||
}
|
||||
} else {
|
||||
super.generateInitialAddresses(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
bitcoinDerivationInfo: bitcoinDerivationInfo,
|
||||
);
|
||||
}
|
||||
|
||||
if (isDiscovered == true && sync == false)
|
||||
initAddresses(true);
|
||||
else if (isDiscovered == true)
|
||||
syncStatus = SyncedSyncStatus();
|
||||
else if (isDiscovered == false && discovered == false) initAddresses(sync);
|
||||
|
||||
return isDiscovered;
|
||||
}
|
||||
|
||||
static Future<BitcoinWallet> create({
|
||||
|
|
|
@ -44,10 +44,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
@override
|
||||
final walletAddressTypes = [
|
||||
SegwitAddressType.p2wpkh,
|
||||
// SegwitAddressType.p2tr,
|
||||
// P2shAddressType.p2wpkhInP2sh,
|
||||
// P2pkhAddressType.p2pkh,
|
||||
// SegwitAddressType.p2wsh,
|
||||
SegwitAddressType.p2tr,
|
||||
P2shAddressType.p2wpkhInP2sh,
|
||||
P2pkhAddressType.p2pkh,
|
||||
SegwitAddressType.p2wsh,
|
||||
];
|
||||
|
||||
@observable
|
||||
|
|
|
@ -181,7 +181,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
break;
|
||||
case ElectrumRequestMethods.scripthashesSubscribeMethod:
|
||||
final response = ElectrumWorkerScripthashesSubscribeResponse.fromJson(messageJson);
|
||||
await onScripthashesStatusResponse(response.result.last);
|
||||
await onScripthashesStatusResponse(response.result);
|
||||
break;
|
||||
case ElectrumRequestMethods.getBalanceMethod:
|
||||
final response = ElectrumWorkerGetBalanceResponse.fromJson(messageJson);
|
||||
|
@ -309,8 +309,44 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
Timer? _updateFeeRateTimer;
|
||||
static const int _autoSaveInterval = 1;
|
||||
|
||||
void initAddresses() {
|
||||
throw UnimplementedError();
|
||||
Future<bool?> initAddresses([bool? sync]) async {
|
||||
bool? isDiscovered = null;
|
||||
|
||||
// NOTE: will initiate by priority from the first walletAddressTypes
|
||||
// then proceeds to following ones after got fully discovered response from worker response
|
||||
for (final addressType in walletAddresses.walletAddressTypes) {
|
||||
if (isHardwareWallet && addressType != SegwitAddressType.p2wpkh) continue;
|
||||
|
||||
final bitcoinDerivationInfo = BitcoinAddressUtils.getDerivationFromType(
|
||||
addressType,
|
||||
network: network,
|
||||
isElectrum: walletAddresses.walletSeedBytesType.isElectrum,
|
||||
);
|
||||
|
||||
for (final isChange in [true, false]) {
|
||||
isDiscovered = walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||
addressType: addressType,
|
||||
seedBytesType: walletAddresses.walletSeedBytesType,
|
||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: isChange,
|
||||
);
|
||||
|
||||
if (isDiscovered == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDiscovered == false) {
|
||||
await generateInitialAddresses(
|
||||
addressType: addressType,
|
||||
seedBytesType: walletAddresses.walletSeedBytesType,
|
||||
bitcoinDerivationInfo: bitcoinDerivationInfo,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isDiscovered;
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
|
@ -324,49 +360,16 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
@override
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
// if (syncStatus is SynchronizingSyncStatus) {
|
||||
// return;
|
||||
// }
|
||||
syncStatus = SynchronizingSyncStatus();
|
||||
|
||||
// syncStatus = SynchronizingSyncStatus();
|
||||
await subscribeForHeaders(true);
|
||||
|
||||
// // INFO: FIRST (always): Call subscribe for headers, wait for completion to update currentChainTip (needed for other methods)
|
||||
// await subscribeForHeaders(true);
|
||||
await initAddresses(false);
|
||||
|
||||
// final responses = await subscribeForStatuses(addresses, true);
|
||||
await updateFeeRates();
|
||||
|
||||
// final addressesWithHistory = walletAddresses.allAddresses
|
||||
// .where((e) => scripthashesWithStatus.contains(e.scriptHash))
|
||||
// .toList();
|
||||
|
||||
// // INFO: SECOND: Start loading transaction histories for every address, this will help discover addresses until the unused gap limit has been reached, which will help finding the full balance and unspents next
|
||||
// await updateTransactions(addressesWithHistory);
|
||||
|
||||
// // INFO: THIRD: Get the full wallet's balance with all addresses considered
|
||||
// await updateBalance(scripthashesWithStatus, true);
|
||||
|
||||
// await updateAllUnspents(scripthashesWithStatus, true);
|
||||
|
||||
// syncStatus = SyncedSyncStatus();
|
||||
|
||||
// // INFO: FOURTH: Get the latest recommended fee rates and start update timer
|
||||
// await updateFeeRates();
|
||||
// _updateFeeRateTimer ??=
|
||||
// Timer.periodic(const Duration(seconds: 5), (timer) async => await updateFeeRates());
|
||||
|
||||
// await save();
|
||||
} catch (e, stacktrace) {
|
||||
printV(stacktrace);
|
||||
printV("startSync $e");
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> syncAddresses(List<BitcoinAddressRecord> addresses) async {
|
||||
try {
|
||||
updateTransactions(addresses);
|
||||
updateBalance(scripthashesWithStatus);
|
||||
updateAllUnspents(scripthashesWithStatus);
|
||||
_updateFeeRateTimer ??=
|
||||
Timer.periodic(const Duration(seconds: 5), (timer) async => await updateFeeRates());
|
||||
|
||||
await save();
|
||||
} catch (e, stacktrace) {
|
||||
|
@ -1239,12 +1242,26 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> onScripthashesStatusResponse(ElectrumWorkerScripthashesResponse result) async {
|
||||
if (result.status == null) {
|
||||
Future<void> onScripthashesStatusResponse(
|
||||
List<ElectrumWorkerScripthashesResponse> result,
|
||||
) async {
|
||||
final noItemsWithStatus = result.length == 1 && result.first.status == null;
|
||||
if (noItemsWithStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
scripthashesWithStatus.add(result.scripthash);
|
||||
final addresses = walletAddresses.allAddresses
|
||||
.where(
|
||||
(address) => result.any((e) => e.status != null && e.scripthash == address.scriptHash),
|
||||
)
|
||||
.toList();
|
||||
|
||||
final scripthashesWithStatus =
|
||||
result.where((e) => e.status != null).map((e) => e.scripthash).toList();
|
||||
|
||||
updateTransactions(addresses);
|
||||
updateBalance(scripthashesWithStatus);
|
||||
updateAllUnspents(scripthashesWithStatus);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -1292,7 +1309,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
final addressesWithHistory = <BitcoinAddressRecord>[];
|
||||
|
||||
if (histories.isNotEmpty) {
|
||||
await Future.wait(histories.map((addressHistory) async {
|
||||
for (final addressHistory in histories) {
|
||||
final txs = addressHistory.txs;
|
||||
|
||||
if (txs.isNotEmpty) {
|
||||
|
@ -1302,20 +1319,11 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
transactionHistory.addOne(tx);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (addressesWithHistory.isNotEmpty) {
|
||||
walletAddresses.updateAddresses(addressesWithHistory);
|
||||
walletAddresses.addAddresses(addressesWithHistory);
|
||||
}
|
||||
} else if (response.completed) {
|
||||
final firstAddress = addressesWithHistory.first;
|
||||
|
||||
discoverNewAddresses(
|
||||
seedBytesType: firstAddress.seedBytesType!,
|
||||
isChange: firstAddress.isChange,
|
||||
addressType: firstAddress.type,
|
||||
derivationInfo: firstAddress.derivationInfo,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1675,7 +1683,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
}
|
||||
|
||||
@action
|
||||
Future<List<ElectrumWorkerScripthashesResponse>> subscribeForStatuses([
|
||||
Future<List<ElectrumWorkerScripthashesResponse>?> subscribeForStatuses([
|
||||
List<BitcoinAddressRecord>? addresses,
|
||||
bool? wait,
|
||||
]) async {
|
||||
|
@ -1686,14 +1694,24 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
addressByScripthashes[addressRecord.scriptHash] = addressRecord.address;
|
||||
});
|
||||
|
||||
return ElectrumWorkerScripthashesSubscribeResponse.fromJson(
|
||||
await waitSendWorker(
|
||||
if (wait == true)
|
||||
return ElectrumWorkerScripthashesSubscribeResponse.fromJson(
|
||||
await waitSendWorker(
|
||||
ElectrumWorkerScripthashesSubscribeRequest(
|
||||
scripthashByAddress: scripthashByAddress,
|
||||
addressByScripthashes: addressByScripthashes,
|
||||
),
|
||||
),
|
||||
).result;
|
||||
else
|
||||
sendWorker(
|
||||
ElectrumWorkerScripthashesSubscribeRequest(
|
||||
scripthashByAddress: scripthashByAddress,
|
||||
addressByScripthashes: addressByScripthashes,
|
||||
),
|
||||
),
|
||||
).result;
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -1823,9 +1841,6 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
syncStatus = ConnectedSyncStatus();
|
||||
}
|
||||
|
||||
// TODO: only once
|
||||
initAddresses();
|
||||
|
||||
break;
|
||||
case ConnectionStatus.disconnected:
|
||||
if (syncStatus is! NotConnectedSyncStatus &&
|
||||
|
@ -1933,29 +1948,69 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
|
||||
@action
|
||||
Future<void> _onAddressesDiscovered(List<BitcoinAddressRecord> addresses) async {
|
||||
try {
|
||||
final scripthashByAddress = await subscribeForStatuses(addresses, true);
|
||||
final scripthashByAddress = await subscribeForStatuses(addresses, true);
|
||||
final noItemsWithStatus =
|
||||
scripthashByAddress!.length == 1 && scripthashByAddress.first.status == null;
|
||||
|
||||
print("addresses: ${addresses.first.address}");
|
||||
final firstAddress = addresses.first;
|
||||
|
||||
if (addresses.first.seedBytesType!.isOldDerivation && scripthashByAddress.length == 1) {
|
||||
// Wrong derivation address with no history, discard and do not add to wallet addresses
|
||||
// Was only used to find transactions if any
|
||||
await save();
|
||||
return;
|
||||
}
|
||||
// NOTE: Did not find any status for old/wrong derivation addresses, discard them
|
||||
// (don't add to addresses list)
|
||||
if (firstAddress.seedBytesType!.isOldDerivation && noItemsWithStatus) {
|
||||
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||
addressType: firstAddress.type,
|
||||
seedBytesType: firstAddress.seedBytesType!,
|
||||
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||
isChange: firstAddress.isChange,
|
||||
discovered: true,
|
||||
);
|
||||
initAddresses();
|
||||
return;
|
||||
}
|
||||
|
||||
walletAddresses.addAddresses(addresses);
|
||||
await syncAddresses(addresses);
|
||||
} catch (_) {}
|
||||
walletAddresses.addAddresses(addresses);
|
||||
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||
addressType: firstAddress.type,
|
||||
seedBytesType: firstAddress.seedBytesType!,
|
||||
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||
isChange: firstAddress.isChange,
|
||||
discovered: true,
|
||||
);
|
||||
|
||||
// NOTE: Has items with status under gap limit, continue discovering
|
||||
if (!noItemsWithStatus)
|
||||
discoverNewAddresses(
|
||||
seedBytesType: firstAddress.seedBytesType!,
|
||||
isChange: firstAddress.isChange,
|
||||
addressType: firstAddress.type,
|
||||
derivationInfo: firstAddress.derivationInfo,
|
||||
scripthashStatuses: scripthashByAddress,
|
||||
);
|
||||
// NOTE: Otherwise, sync all the discovered addresses so far
|
||||
else {
|
||||
subscribeForStatuses(
|
||||
walletAddresses.addressesRecords
|
||||
.getRecords(
|
||||
seedBytesType: firstAddress.seedBytesType!,
|
||||
addressType: firstAddress.type,
|
||||
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||
isChange: firstAddress.isChange,
|
||||
)
|
||||
.whereType<BitcoinAddressRecord>()
|
||||
.toList(),
|
||||
);
|
||||
initAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void generateInitialAddresses({
|
||||
Future<bool> generateInitialAddresses({
|
||||
required BitcoinAddressType addressType,
|
||||
required SeedBytesType seedBytesType,
|
||||
BitcoinDerivationInfo? bitcoinDerivationInfo,
|
||||
}) {
|
||||
}) async {
|
||||
bool discovered = false;
|
||||
|
||||
bitcoinDerivationInfo ??= BitcoinAddressUtils.getDerivationFromType(
|
||||
addressType,
|
||||
network: network,
|
||||
|
@ -1968,15 +2023,32 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: false,
|
||||
);
|
||||
final discoveredExistingReceiveAddresses =
|
||||
walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: false,
|
||||
);
|
||||
|
||||
if (existingReceiveAddresses.length < ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT) {
|
||||
if (!discoveredExistingReceiveAddresses &&
|
||||
existingReceiveAddresses.length < ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT) {
|
||||
discoverNewAddresses(
|
||||
seedBytesType: seedBytesType,
|
||||
isChange: false,
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationInfo: bitcoinDerivationInfo,
|
||||
isChange: false,
|
||||
startIndex: existingReceiveAddresses.length,
|
||||
);
|
||||
discovered = true;
|
||||
} else {
|
||||
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: false,
|
||||
discovered: true,
|
||||
);
|
||||
}
|
||||
|
||||
final existingChangeAddresses = walletAddresses.addressesRecords.getRecords(
|
||||
|
@ -1985,16 +2057,35 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: true,
|
||||
);
|
||||
final discoveredExistingChangeAddresses =
|
||||
walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: true,
|
||||
);
|
||||
|
||||
if (existingChangeAddresses.length < ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT) {
|
||||
if (!discoveredExistingChangeAddresses &&
|
||||
existingChangeAddresses.length < ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT) {
|
||||
discoverNewAddresses(
|
||||
seedBytesType: seedBytesType,
|
||||
isChange: true,
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationInfo: bitcoinDerivationInfo,
|
||||
isChange: true,
|
||||
startIndex: existingChangeAddresses.length,
|
||||
);
|
||||
discovered = true;
|
||||
} else {
|
||||
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: true,
|
||||
discovered: true,
|
||||
);
|
||||
}
|
||||
|
||||
return discovered;
|
||||
}
|
||||
|
||||
void discoverNewAddresses({
|
||||
|
@ -2003,8 +2094,9 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
int? startIndex,
|
||||
List<ElectrumWorkerScripthashesResponse>? scripthashStatuses,
|
||||
}) async {
|
||||
final count = isChange
|
||||
final countToDiscover = isChange
|
||||
? ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT
|
||||
: ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT;
|
||||
|
||||
|
@ -2018,20 +2110,21 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
startIndex ??= recordList.length;
|
||||
|
||||
late bool needsToDiscover;
|
||||
print(addressType);
|
||||
print(seedBytesType);
|
||||
print(derivationInfo.derivationPath.toString());
|
||||
print(isChange);
|
||||
print([addressType, recordList.length]);
|
||||
|
||||
if (recordList.length < count) {
|
||||
if (recordList.length < countToDiscover) {
|
||||
needsToDiscover = true;
|
||||
} else if (recordList.length == count) {
|
||||
} else if (recordList.length == countToDiscover) {
|
||||
needsToDiscover = recordList.any((record) => !record.getIsUsed());
|
||||
} else {
|
||||
needsToDiscover = recordList.sublist(recordList.length - count).any(
|
||||
(record) => record.getIsUsed(),
|
||||
);
|
||||
needsToDiscover = recordList.sublist(recordList.length - countToDiscover).any(
|
||||
(record) {
|
||||
return scripthashStatuses?.any(
|
||||
(scripthashStatus) =>
|
||||
scripthashStatus.scripthash == (record as BitcoinAddressRecord).scriptHash,
|
||||
) ??
|
||||
record.getIsUsed();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!needsToDiscover) {
|
||||
|
@ -2041,7 +2134,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
workerSendPort!.send(
|
||||
ElectrumWorkerDiscoverAddressesRequest(
|
||||
id: _messageId,
|
||||
count: count,
|
||||
count: countToDiscover,
|
||||
walletType: type,
|
||||
startIndex: startIndex,
|
||||
seedBytesType: seedBytesType,
|
||||
|
|
|
@ -28,7 +28,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
BitcoinAddressType? initialAddressPageType,
|
||||
}) : addressesRecords = initialAddressesRecords ?? BitcoinAddressRecordMap(),
|
||||
activeIndexByType = initialActiveAddressIndex ?? {},
|
||||
discoveredAddressTypes = initialDiscoveredAddresses ?? BitcoinDiscoveredAddressesMap(),
|
||||
discoveredAddressesRecord = initialDiscoveredAddresses ?? BitcoinDiscoveredAddressesMap(),
|
||||
addressPageType = initialAddressPageType ??
|
||||
(walletInfo.addressPageType != null
|
||||
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
||||
|
@ -49,6 +49,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
String get xpub => hdWallet.publicKey.toExtended;
|
||||
String get xpriv => hdWallet.privateKey.toExtended;
|
||||
|
||||
// NOTE: order matters in priority
|
||||
List<SeedBytesType> get seedBytesTypes {
|
||||
final seedBytesTypes = <SeedBytesType>[];
|
||||
if (hdWallets.containsKey(SeedBytesType.bip39)) {
|
||||
seedBytesTypes.add(SeedBytesType.bip39);
|
||||
}
|
||||
if (hdWallets.containsKey(SeedBytesType.electrum)) {
|
||||
seedBytesTypes.add(SeedBytesType.electrum);
|
||||
}
|
||||
return seedBytesTypes;
|
||||
}
|
||||
|
||||
SeedBytesType get walletSeedBytesType => hdWallets.containsKey(SeedBytesType.bip39)
|
||||
? SeedBytesType.bip39
|
||||
: (hdWallets.containsKey(SeedBytesType.electrum)
|
||||
|
@ -83,11 +95,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
bool isEnabledAutoGenerateNewAddress = true;
|
||||
|
||||
@observable
|
||||
BitcoinDiscoveredAddressesMap discoveredAddressTypes;
|
||||
BitcoinDiscoveredAddressesMap discoveredAddressesRecord;
|
||||
|
||||
@observable
|
||||
BitcoinAddressRecordMap addressesRecords;
|
||||
|
||||
Set<String> get hiddenAddresses =>
|
||||
allAddresses.where((address) => address.isHidden).map((address) => address.address).toSet();
|
||||
|
||||
@observable
|
||||
List<BitcoinAddressRecord> _allAddresses = [];
|
||||
@computed
|
||||
|
@ -257,12 +272,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
@action
|
||||
void resetActiveAddress() {
|
||||
final activeReceiveAddresses = selectedReceiveAddresses.whereType<BitcoinAddressRecord>();
|
||||
try {
|
||||
final activeReceiveAddresses = selectedReceiveAddresses.whereType<BitcoinAddressRecord>();
|
||||
|
||||
activeBitcoinAddress = activeReceiveAddresses.firstWhereOrNull(
|
||||
(addressRecord) => addressRecord.index == activeIndexByType[addressPageType],
|
||||
) ??
|
||||
activeReceiveAddresses.first;
|
||||
activeBitcoinAddress = activeReceiveAddresses.firstWhereOrNull(
|
||||
(addressRecord) => addressRecord.index == activeIndexByType[addressPageType],
|
||||
) ??
|
||||
activeReceiveAddresses.first;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -272,11 +289,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return activeBitcoinAddress!.address;
|
||||
}
|
||||
|
||||
final receiveAddress = selectedReceiveAddresses
|
||||
.firstWhereOrNull(
|
||||
(addr) => addr.getIsStillReceiveable(isEnabledAutoGenerateNewAddress) && !addr.isChange,
|
||||
)
|
||||
?.address;
|
||||
final receiveAddress = nextReceiveAddress?.address;
|
||||
|
||||
return receiveAddress ?? '';
|
||||
}
|
||||
|
@ -442,22 +455,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void updateAddresses(Iterable<BitcoinAddressRecord> newAddresses) {
|
||||
final replacedAddresses = newAddresses.toList();
|
||||
for (final address in newAddresses) {
|
||||
final index = _allAddresses.indexWhere((element) => element.address == address.address);
|
||||
if (index >= 0) {
|
||||
_allAddresses.replaceRange(index, index + 1, [address]);
|
||||
replacedAddresses.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
if (replacedAddresses.isNotEmpty) {
|
||||
_allAddresses.addAll(replacedAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void addAddresses(List<BitcoinAddressRecord> addresses) {
|
||||
final firstAddress = addresses.first;
|
||||
|
@ -469,28 +466,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
isChange: firstAddress.isChange,
|
||||
addressRecords: addresses,
|
||||
);
|
||||
print("addressesRecords.allRecords().length");
|
||||
print(firstAddress.type);
|
||||
print(firstAddress.seedBytesType!);
|
||||
print(firstAddress.derivationInfo.derivationPath.toString());
|
||||
print(firstAddress.isChange);
|
||||
|
||||
print(addressesRecords
|
||||
.getRecords(
|
||||
addressType: firstAddress.type,
|
||||
seedBytesType: firstAddress.seedBytesType!,
|
||||
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||
isChange: firstAddress.isChange,
|
||||
)
|
||||
.length);
|
||||
|
||||
updateAllAddresses();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json['addressesRecords'] = addressesRecords.toJson();
|
||||
json['discoveredAddressTypes'] = discoveredAddressTypes.toJson();
|
||||
json['discoveredAddressesRecord'] = discoveredAddressesRecord.toJson();
|
||||
json['addressPageType'] = addressPageType.toString();
|
||||
json['activeIndexByType'] = activeIndexByType.map(
|
||||
(key, value) => MapEntry(key.toString(), value),
|
||||
|
@ -508,7 +490,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return {
|
||||
'allAddresses': addresses,
|
||||
'addressesRecords': data['addressesRecords'] as Map<String, dynamic>?,
|
||||
'discoveredAddressTypes': data['discoveredAddressTypes'] as Map<String, dynamic>?,
|
||||
'discoveredAddressesRecord': data['discoveredAddressesRecord'] as Map<String, dynamic>?,
|
||||
'addressPageType': data['addressPageType'] as String?,
|
||||
'activeIndexByType': (data['activeIndexByType'] as Map<dynamic, dynamic>?) ?? {},
|
||||
};
|
||||
|
@ -529,9 +511,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
json['addressesRecords'] as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
if (json['discoveredAddressTypes'] != null)
|
||||
if (json['discoveredAddressesRecord'] != null)
|
||||
initialDiscoveredAddresses ??= BitcoinDiscoveredAddressesMap.fromJson(
|
||||
json['discoveredAddressTypes'] as Map<String, dynamic>,
|
||||
json['discoveredAddressesRecord'] as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
return ElectrumWalletAddresses(
|
||||
|
@ -612,7 +594,24 @@ class BitcoinAddressRecordMap extends ItemsRecordMap<AddressRecordsBySeedType> {
|
|||
);
|
||||
_data[addressType]![seedBytesType]![derivationPath]!.putIfAbsent(isChange, () => []);
|
||||
|
||||
_data[addressType]![seedBytesType]![derivationPath]![isChange]!.addAll(addressRecords);
|
||||
final recordsList = _data[addressType]![seedBytesType]![derivationPath]![isChange]!;
|
||||
|
||||
if (recordsList.isEmpty) {
|
||||
recordsList.addAll(addressRecords);
|
||||
} else {
|
||||
for (final addressRecord in addressRecords) {
|
||||
final existingRecordIndex =
|
||||
recordsList.indexWhere((record) => record.address == addressRecord.address);
|
||||
|
||||
if (existingRecordIndex >= 0) {
|
||||
recordsList.replaceRange(existingRecordIndex, existingRecordIndex + 1, [addressRecord]);
|
||||
} else {
|
||||
recordsList.add(addressRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_data[addressType]![seedBytesType]![derivationPath]![isChange] = recordsList;
|
||||
}
|
||||
|
||||
List<BaseBitcoinAddressRecord> allRecords() {
|
||||
|
@ -697,31 +696,30 @@ class BitcoinDiscoveredAddressesMap extends ItemsRecordMap<DiscoveredAddressReco
|
|||
required BitcoinAddressType addressType,
|
||||
required SeedBytesType seedBytesType,
|
||||
required String derivationPath,
|
||||
required BaseBitcoinAddressRecord addressRecord,
|
||||
required bool isChange,
|
||||
required bool discovered,
|
||||
}) {
|
||||
_data.putIfAbsent(
|
||||
addressType,
|
||||
() => {
|
||||
seedBytesType: {
|
||||
derivationPath: {addressRecord.isChange: discovered},
|
||||
derivationPath: {isChange: discovered},
|
||||
},
|
||||
},
|
||||
);
|
||||
_data[addressType]!.putIfAbsent(
|
||||
seedBytesType,
|
||||
() => {
|
||||
derivationPath: {addressRecord.isChange: discovered},
|
||||
derivationPath: {isChange: discovered},
|
||||
},
|
||||
);
|
||||
_data[addressType]![seedBytesType]!.putIfAbsent(
|
||||
derivationPath,
|
||||
() => {addressRecord.isChange: discovered},
|
||||
() => {isChange: discovered},
|
||||
);
|
||||
_data[addressType]![seedBytesType]![derivationPath]!
|
||||
.putIfAbsent(addressRecord.isChange, () => discovered);
|
||||
_data[addressType]![seedBytesType]![derivationPath]!.putIfAbsent(isChange, () => discovered);
|
||||
|
||||
_data[addressType]![seedBytesType]![derivationPath]![addressRecord.isChange] = discovered;
|
||||
_data[addressType]![seedBytesType]![derivationPath]![isChange] = discovered;
|
||||
}
|
||||
|
||||
bool getIsDiscovered({
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
|
|
|
@ -65,8 +65,8 @@ class ElectrumWorkerScripthashesResponse {
|
|||
|
||||
static ElectrumWorkerScripthashesResponse fromJson(Map<String, dynamic> json) {
|
||||
return ElectrumWorkerScripthashesResponse(
|
||||
address: json['address'] as String,
|
||||
scripthash: json['scripthash'] as String,
|
||||
address: json['address'] as String? ?? '',
|
||||
scripthash: json['scripthash'] as String? ?? '',
|
||||
status: json['status'] as String?,
|
||||
);
|
||||
}
|
||||
|
@ -89,9 +89,17 @@ class ElectrumWorkerScripthashesSubscribeResponse extends ElectrumWorkerResponse
|
|||
@override
|
||||
factory ElectrumWorkerScripthashesSubscribeResponse.fromJson(Map<String, dynamic> json) {
|
||||
return ElectrumWorkerScripthashesSubscribeResponse(
|
||||
result: (json['result'] as List<dynamic>)
|
||||
.map((e) => ElectrumWorkerScripthashesResponse.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
result: (json['result'] as List<dynamic>).map((e) {
|
||||
if (e is String) {
|
||||
return ElectrumWorkerScripthashesResponse.fromJson(
|
||||
jsonDecode(e) as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
return ElectrumWorkerScripthashesResponse.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
);
|
||||
}).toList(),
|
||||
error: json['error'] as String?,
|
||||
id: json['id'] as int?,
|
||||
completed: json['completed'] as bool? ?? false,
|
||||
|
|
|
@ -66,13 +66,6 @@ class TransactionsPage extends StatelessWidget {
|
|||
dashboardViewModel: dashboardViewModel,
|
||||
key: ValueKey('transactions_page_header_row_key'),
|
||||
),
|
||||
Center(
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor,
|
||||
),
|
||||
)),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue