From 19bf4c0df6f833ec6968eb16abf72f92effd23e4 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 31 Jan 2023 17:26:10 -0600 Subject: [PATCH] scan following/followers for paynym history --- .../coins/bitcoin/bitcoin_wallet.dart | 30 +++++- .../mixins/paynym_wallet_interface.dart | 92 ++++++++++++++++--- 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/lib/services/coins/bitcoin/bitcoin_wallet.dart b/lib/services/coins/bitcoin/bitcoin_wallet.dart index 5f18f3273..2d2f5b590 100644 --- a/lib/services/coins/bitcoin/bitcoin_wallet.dart +++ b/lib/services/coins/bitcoin/bitcoin_wallet.dart @@ -40,6 +40,7 @@ import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/paynym_is_api.dart'; import 'package:stackwallet/utilities/prefs.dart'; import 'package:tuple/tuple.dart'; import 'package:uuid/uuid.dart'; @@ -704,16 +705,28 @@ class BitcoinWallet extends CoinServiceAPI ...p2shChangeAddressArray, ]); - // generate to ensure notification address is in db before refreshing transactions - await getMyNotificationAddress(DerivePathType.bip44); + // get own payment code + final myCode = await getPaymentCode(DerivePathType.bip44); // refresh transactions to pick up any received notification transactions await _refreshTransactions(); + final Set codesToCheck = {}; + final nym = await PaynymIsApi().nym(myCode.toString()); + if (nym.value != null) { + for (final follower in nym.value!.followers) { + codesToCheck.add(follower.code); + } + for (final following in nym.value!.following) { + codesToCheck.add(following.code); + } + } + // restore paynym transactions await restoreAllHistory( maxUnusedAddressGap: maxUnusedAddressGap, maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck, + paymentCodeStrings: codesToCheck, ); await _updateUTXOs(); @@ -940,6 +953,17 @@ class BitcoinWallet extends CoinServiceAPI GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId)); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.1, walletId)); + final myCode = await getPaymentCode(DerivePathType.bip44); + final Set codesToCheck = {}; + final nym = await PaynymIsApi().nym(myCode.toString()); + if (nym.value != null) { + for (final follower in nym.value!.followers) { + codesToCheck.add(follower.code); + } + for (final following in nym.value!.following) { + codesToCheck.add(following.code); + } + } final currentHeight = await chainHeight; const storedHeight = 1; //await storedChainHeight; @@ -976,6 +1000,7 @@ class BitcoinWallet extends CoinServiceAPI .fire(RefreshPercentChangedEvent(0.80, walletId)); await fetchFuture; + await checkForNotificationTransactionsTo(codesToCheck); await getAllTxsToWatch(); GlobalEventBus.instance .fire(RefreshPercentChangedEvent(0.90, walletId)); @@ -2828,7 +2853,6 @@ class BitcoinWallet extends CoinServiceAPI // Add transaction output for (var i = 0; i < recipients.length; i++) { - print("OURPUT VALUW: ${satoshiAmounts[i]}"); txb.addOutput(recipients[i], satoshiAmounts[i]); } diff --git a/lib/services/mixins/paynym_wallet_interface.dart b/lib/services/mixins/paynym_wallet_interface.dart index 3db2f453f..a257a7d1e 100644 --- a/lib/services/mixins/paynym_wallet_interface.dart +++ b/lib/services/mixins/paynym_wallet_interface.dart @@ -749,33 +749,99 @@ mixin PaynymWalletInterface { .subTypeEqualTo(TransactionSubType.bip47Notification) .findAll(); - List unBlindedList = []; + List codes = []; for (final tx in txns) { - final unBlinded = await unBlindedPaymentCodeFromTransaction( - transaction: tx, - myNotificationAddress: myAddress, - ); - if (unBlinded != null && - unBlindedList - .where((e) => e.toString() == unBlinded.toString()) - .isEmpty) { - unBlindedList.add(unBlinded); + // tx is sent so we can check the address's otherData for the code String + if (tx.type == TransactionType.outgoing && + tx.address.value?.otherData != null) { + final codeString = + await paymentCodeStringByKey(tx.address.value!.otherData!); + if (codeString != null && + codes.where((e) => e.toString() == codeString).isEmpty) { + codes.add(PaymentCode.fromPaymentCode(codeString, _network)); + } + } else { + // otherwise we need to un blind the code + final unBlinded = await unBlindedPaymentCodeFromTransaction( + transaction: tx, + myNotificationAddress: myAddress, + ); + if (unBlinded != null && + codes.where((e) => e.toString() == unBlinded.toString()).isEmpty) { + codes.add(unBlinded); + } } } - return unBlindedList; + return codes; + } + + Future checkForNotificationTransactionsTo( + Set otherCodeStrings) async { + final sentNotificationTransactions = await _db + .getTransactions(_walletId) + .filter() + .subTypeEqualTo(TransactionSubType.bip47Notification) + .and() + .typeEqualTo(TransactionType.outgoing) + .findAll(); + + final List codes = []; + for (final codeString in otherCodeStrings) { + codes.add(PaymentCode.fromPaymentCode(codeString, _network)); + } + + for (final tx in sentNotificationTransactions) { + if (tx.address.value != null && tx.address.value!.otherData == null) { + final oldAddress = + await _db.getAddress(_walletId, tx.address.value!.value); + for (final code in codes) { + final notificationAddress = code.notificationAddressP2PKH(); + if (notificationAddress == oldAddress!.value) { + final address = Address( + walletId: _walletId, + value: notificationAddress, + publicKey: [], + derivationIndex: 0, + type: oldAddress.type, + subType: AddressSubType.paynymNotification, + otherData: await storeCode(code.toString()), + ); + await _db.updateAddress(oldAddress, address); + } + } + } + } } Future restoreAllHistory({ required int maxUnusedAddressGap, required int maxNumberOfIndexesToCheck, + required Set paymentCodeStrings, }) async { final codes = await getAllPaymentCodesFromNotificationTransactions(); + final List extraCodes = []; + for (final codeString in paymentCodeStrings) { + if (codes.where((e) => e.toString() == codeString).isEmpty) { + final extraCode = PaymentCode.fromPaymentCode(codeString, _network); + if (extraCode.isValid()) { + extraCodes.add(extraCode); + } + } + } + + codes.addAll(extraCodes); + final List> futures = []; for (final code in codes) { - futures.add(restoreHistoryWith( - code, maxUnusedAddressGap, maxNumberOfIndexesToCheck)); + futures.add( + restoreHistoryWith( + code, + maxUnusedAddressGap, + maxNumberOfIndexesToCheck, + ), + ); } await Future.wait(futures);