From a69b4f8ed547603e8e2112075720a2d8bb4382b7 Mon Sep 17 00:00:00 2001 From: julian Date: Sat, 13 Jan 2024 15:49:29 -0600 Subject: [PATCH] paynym refactor to tx v2 --- .../models/blockchain_data/v2/input_v2.dart | 6 + .../models/blockchain_data/v2/output_v2.dart | 25 +- lib/utilities/bip47_utils.dart | 8 +- .../crypto_currency/coins/bitcoin.dart | 8 +- .../interfaces/paynym_currency_interface.dart | 9 + lib/wallets/wallet/impl/bitcoin_wallet.dart | 8 +- lib/wallets/wallet/wallet.dart | 28 +++ .../coin_control_interface.dart | 3 +- .../electrumx_interface.dart | 40 ++-- .../paynym_interface.dart | 213 +++++++++++------- 10 files changed, 220 insertions(+), 128 deletions(-) create mode 100644 lib/wallets/crypto_currency/interfaces/paynym_currency_interface.dart diff --git a/lib/models/isar/models/blockchain_data/v2/input_v2.dart b/lib/models/isar/models/blockchain_data/v2/input_v2.dart index 66b1c5ab1..00a67976a 100644 --- a/lib/models/isar/models/blockchain_data/v2/input_v2.dart +++ b/lib/models/isar/models/blockchain_data/v2/input_v2.dart @@ -44,6 +44,7 @@ class OutpointV2 { @Embedded() class InputV2 { late final String? scriptSigHex; + late final String? scriptSigAsm; late final int? sequence; late final OutpointV2? outpoint; late final List addresses; @@ -63,6 +64,7 @@ class InputV2 { static InputV2 isarCantDoRequiredInDefaultConstructor({ required String? scriptSigHex, + String? scriptSigAsm, required int? sequence, required OutpointV2? outpoint, required List addresses, @@ -74,6 +76,7 @@ class InputV2 { }) => InputV2() ..scriptSigHex = scriptSigHex + ..scriptSigAsm = scriptSigAsm ..sequence = sequence ..outpoint = outpoint ..addresses = List.unmodifiable(addresses) @@ -85,6 +88,7 @@ class InputV2 { InputV2 copyWith({ String? scriptSigHex, + String? scriptSigAsm, int? sequence, OutpointV2? outpoint, List? addresses, @@ -96,6 +100,7 @@ class InputV2 { }) { return InputV2.isarCantDoRequiredInDefaultConstructor( scriptSigHex: scriptSigHex ?? this.scriptSigHex, + scriptSigAsm: scriptSigAsm ?? this.scriptSigAsm, sequence: sequence ?? this.sequence, outpoint: outpoint ?? this.outpoint, addresses: addresses ?? this.addresses, @@ -111,6 +116,7 @@ class InputV2 { String toString() { return 'InputV2(\n' ' scriptSigHex: $scriptSigHex,\n' + ' scriptSigAsm: $scriptSigAsm,\n' ' sequence: $sequence,\n' ' outpoint: $outpoint,\n' ' addresses: $addresses,\n' diff --git a/lib/models/isar/models/blockchain_data/v2/output_v2.dart b/lib/models/isar/models/blockchain_data/v2/output_v2.dart index 2b9ee84fb..f096d8a90 100644 --- a/lib/models/isar/models/blockchain_data/v2/output_v2.dart +++ b/lib/models/isar/models/blockchain_data/v2/output_v2.dart @@ -6,6 +6,7 @@ part 'output_v2.g.dart'; @Embedded() class OutputV2 { late final String scriptPubKeyHex; + late final String? scriptPubKeyAsm; late final String valueStringSats; late final List addresses; @@ -18,24 +19,28 @@ class OutputV2 { static OutputV2 isarCantDoRequiredInDefaultConstructor({ required String scriptPubKeyHex, + String? scriptPubKeyAsm, required String valueStringSats, required List addresses, required bool walletOwns, }) => OutputV2() ..scriptPubKeyHex = scriptPubKeyHex + ..scriptPubKeyAsm = scriptPubKeyAsm ..valueStringSats = valueStringSats ..walletOwns = walletOwns ..addresses = List.unmodifiable(addresses); OutputV2 copyWith({ String? scriptPubKeyHex, + String? scriptPubKeyAsm, String? valueStringSats, List? addresses, bool? walletOwns, }) { return OutputV2.isarCantDoRequiredInDefaultConstructor( scriptPubKeyHex: scriptPubKeyHex ?? this.scriptPubKeyHex, + scriptPubKeyAsm: scriptPubKeyAsm ?? this.scriptPubKeyAsm, valueStringSats: valueStringSats ?? this.valueStringSats, addresses: addresses ?? this.addresses, walletOwns: walletOwns ?? this.walletOwns, @@ -61,6 +66,7 @@ class OutputV2 { return OutputV2.isarCantDoRequiredInDefaultConstructor( scriptPubKeyHex: json["scriptPubKey"]["hex"] as String, + scriptPubKeyAsm: json["scriptPubKey"]["asm"] as String?, valueStringSats: parseOutputAmountString( json["value"].toString(), decimalPlaces: decimalPlaces, @@ -100,27 +106,10 @@ class OutputV2 { String toString() { return 'OutputV2(\n' ' scriptPubKeyHex: $scriptPubKeyHex,\n' + ' scriptPubKeyAsm: $scriptPubKeyAsm,\n' ' value: $value,\n' ' walletOwns: $walletOwns,\n' ' addresses: $addresses,\n' ')'; } } - -bool _listEquals(List a, List b) { - if (T != U) { - return false; - } - - if (a.length != b.length) { - return false; - } - - for (int i = 0; i < a.length; i++) { - if (a[i] != b[i]) { - return false; - } - } - - return true; -} diff --git a/lib/utilities/bip47_utils.dart b/lib/utilities/bip47_utils.dart index 5f7b88c45..87730f0df 100644 --- a/lib/utilities/bip47_utils.dart +++ b/lib/utilities/bip47_utils.dart @@ -11,12 +11,12 @@ import 'dart:typed_data'; import 'package:bip47/src/util.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/output.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart'; abstract class Bip47Utils { /// looks at tx outputs and returns a blinded payment code if found - static Uint8List? getBlindedPaymentCodeBytesFrom(Transaction transaction) { + static Uint8List? getBlindedPaymentCodeBytesFrom(TransactionV2 transaction) { for (int i = 0; i < transaction.outputs.length; i++) { final bytes = getBlindedPaymentCodeBytesFromOutput( transaction.outputs.elementAt(i)); @@ -28,7 +28,7 @@ abstract class Bip47Utils { return null; } - static Uint8List? getBlindedPaymentCodeBytesFromOutput(Output output) { + static Uint8List? getBlindedPaymentCodeBytesFromOutput(OutputV2 output) { Uint8List? blindedCodeBytes; List? scriptChunks = output.scriptPubKeyAsm?.split(" "); diff --git a/lib/wallets/crypto_currency/coins/bitcoin.dart b/lib/wallets/crypto_currency/coins/bitcoin.dart index e7a933072..2402a977f 100644 --- a/lib/wallets/crypto_currency/coins/bitcoin.dart +++ b/lib/wallets/crypto_currency/coins/bitcoin.dart @@ -6,9 +6,10 @@ import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; +import 'package:stackwallet/wallets/crypto_currency/interfaces/paynym_currency_interface.dart'; import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart'; -class Bitcoin extends Bip39HDCurrency { +class Bitcoin extends Bip39HDCurrency with PaynymCurrencyInterface { Bitcoin(super.network) { switch (network) { case CryptoCurrencyNetwork.main: @@ -49,11 +50,6 @@ class Bitcoin extends Bip39HDCurrency { fractionDigits: fractionDigits, ); - Amount get dustLimitP2PKH => Amount( - rawValue: BigInt.from(546), - fractionDigits: fractionDigits, - ); - @override coinlib.NetworkParams get networkParams { switch (network) { diff --git a/lib/wallets/crypto_currency/interfaces/paynym_currency_interface.dart b/lib/wallets/crypto_currency/interfaces/paynym_currency_interface.dart new file mode 100644 index 000000000..6f5571009 --- /dev/null +++ b/lib/wallets/crypto_currency/interfaces/paynym_currency_interface.dart @@ -0,0 +1,9 @@ +import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart'; + +mixin PaynymCurrencyInterface on Bip39HDCurrency { + Amount get dustLimitP2PKH => Amount( + rawValue: BigInt.from(546), + fractionDigits: fractionDigits, + ); +} diff --git a/lib/wallets/wallet/impl/bitcoin_wallet.dart b/lib/wallets/wallet/impl/bitcoin_wallet.dart index 43bb406d0..8d21f22b0 100644 --- a/lib/wallets/wallet/impl/bitcoin_wallet.dart +++ b/lib/wallets/wallet/impl/bitcoin_wallet.dart @@ -9,17 +9,18 @@ import 'package:stackwallet/utilities/extensions/extensions.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; +import 'package:stackwallet/wallets/crypto_currency/interfaces/paynym_currency_interface.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; -class BitcoinWallet extends Bip39HDWallet - with ElectrumXInterface, CoinControlInterface, PaynymInterface { +class BitcoinWallet extends Bip39HDWallet + with ElectrumXInterface, CoinControlInterface, PaynymInterface { @override int get isarTransactionVersion => 2; - BitcoinWallet(CryptoCurrencyNetwork network) : super(Bitcoin(network)); + BitcoinWallet(CryptoCurrencyNetwork network) : super(Bitcoin(network) as T); @override FilterOperation? get changeAddressFilterOperation => @@ -153,6 +154,7 @@ class BitcoinWallet extends Bip39HDWallet InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( scriptSigHex: map["scriptSig"]?["hex"] as String?, + scriptSigAsm: map["scriptSig"]?["asm"] as String?, sequence: map["sequence"] as int?, outpoint: outpoint, valueStringSats: valueStringSats, diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 5961a868d..f55dbb547 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -19,6 +19,7 @@ import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/paynym_is_api.dart'; import 'package:stackwallet/utilities/prefs.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/wallets/isar/models/wallet_info.dart'; @@ -45,6 +46,7 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_int import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart'; +import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; @@ -471,6 +473,24 @@ abstract class Wallet { ), ); + // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. + final Set codesToCheck = {}; + if (this is PaynymInterface) { + // isSegwit does not matter here at all + final myCode = + await (this as PaynymInterface).getPaymentCode(isSegwit: false); + + 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); + } + } + } + GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId)); await updateChainHeight(); @@ -505,6 +525,14 @@ abstract class Wallet { GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId)); await fetchFuture; + + // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. + if (this is PaynymInterface && codesToCheck.isNotEmpty) { + await (this as PaynymInterface) + .checkForNotificationTransactionsTo(codesToCheck); + // check utxos again for notification outputs + await updateUTXOs(); + } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.80, walletId)); // await getAllTxsToWatch(); diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart index 221aa5bcb..6cd810ba7 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart @@ -1,6 +1,7 @@ +import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; -mixin CoinControlInterface on Bip39HDWallet { +mixin CoinControlInterface on Bip39HDWallet { // any required here? // currently only used to id which wallets support coin control } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index 7e77e84ca..f9507bbc0 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -20,12 +20,13 @@ import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart'; +import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart'; import 'package:stackwallet/wallets/models/tx_data.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; import 'package:uuid/uuid.dart'; -mixin ElectrumXInterface on Bip39HDWallet { +mixin ElectrumXInterface on Bip39HDWallet { late ElectrumXClient electrumXClient; late CachedElectrumXClient electrumXCachedClient; @@ -533,20 +534,29 @@ mixin ElectrumXInterface on Bip39HDWallet { final address = await mainDB.getAddress(walletId, sd.utxo.address!); if (address?.derivationPath != null) { if (address!.subType == AddressSubType.paynymReceive) { - // TODO paynym - // final code = await paymentCodeStringByKey(address.otherData!); - // - // final bip47base = await getBip47BaseNode(); - // - // final privateKey = await getPrivateKeyForPaynymReceivingAddress( - // paymentCodeString: code!, - // index: address.derivationIndex, - // ); - // - // keys = coinlib.HDPrivateKey.fromKeyAndChainCode( - // privateKey, - // bip47base.chainCode, - // ); + if (this is PaynymInterface) { + final code = await (this as PaynymInterface) + .paymentCodeStringByKey(address.otherData!); + + final bip47base = + await (this as PaynymInterface).getBip47BaseNode(); + + final privateKey = await (this as PaynymInterface) + .getPrivateKeyForPaynymReceivingAddress( + paymentCodeString: code!, + index: address.derivationIndex, + ); + + keys = coinlib.HDPrivateKey.fromKeyAndChainCode( + coinlib.ECPrivateKey.fromHex(privateKey.toHex), + bip47base.chainCode, + ); + } else { + throw Exception( + "$runtimeType tried to fetchBuildTxData for a paynym address" + " in a non PaynymInterface wallet", + ); + } } else { keys = root.derivePath(address.derivationPath!.value); } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart index 91e267a17..6efb4812b 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart @@ -12,6 +12,8 @@ import 'package:isar/isar.dart'; import 'package:pointycastle/digests/sha256.dart'; import 'package:stackwallet/exceptions/wallet/insufficient_balance_exception.dart'; import 'package:stackwallet/exceptions/wallet/paynym_send_exception.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/signing_data.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -20,6 +22,7 @@ import 'package:stackwallet/utilities/bip47_utils.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/wallets/crypto_currency/interfaces/paynym_currency_interface.dart'; import 'package:stackwallet/wallets/models/tx_data.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; @@ -43,12 +46,8 @@ String _sendPaynymAddressDerivationPath( }) => "${_basePaynymDerivePath(testnet: testnet)}/0/$index"; -mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { - Amount get _dustLimitP2PKH => Amount( - rawValue: BigInt.from(546), - fractionDigits: cryptoCurrency.fractionDigits, - ); - +mixin PaynymInterface + on Bip39HDWallet, ElectrumXInterface { btc_dart.NetworkType get networkType => btc_dart.NetworkType( messagePrefix: cryptoCurrency.networkParams.messagePrefix, bech32: cryptoCurrency.networkParams.bech32Hrp, @@ -447,9 +446,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { List? utxos, }) async { try { - // final amountToSend = cryptoCurrency.dustLimitP2PKH; - - final amountToSend = _dustLimitP2PKH; + final amountToSend = cryptoCurrency.dustLimitP2PKH; final List availableOutputs = utxos ?? await mainDB.getUTXOs(walletId).findAll(); final List spendableOutputs = []; @@ -550,14 +547,14 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { } if (satoshisBeingUsed - amountToSend.raw > - feeForNoChange + _dustLimitP2PKH.raw) { + feeForNoChange + cryptoCurrency.dustLimitP2PKH.raw) { // try to add change output due to "left over" amount being greater than // the estimated fee + the dust limit BigInt changeAmount = satoshisBeingUsed - amountToSend.raw - feeForWithChange; // check estimates are correct and build notification tx - if (changeAmount >= _dustLimitP2PKH.raw && + if (changeAmount >= cryptoCurrency.dustLimitP2PKH.raw && satoshisBeingUsed - amountToSend.raw - changeAmount == feeForWithChange) { var txn = await _createNotificationTx( @@ -746,7 +743,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { txb.addOutput( notificationAddress, - (overrideAmountForTesting ?? _dustLimitP2PKH.raw).toInt(), + (overrideAmountForTesting ?? cryptoCurrency.dustLimitP2PKH.raw).toInt(), ); txb.addOutput(opReturnScript, 0); @@ -854,44 +851,67 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { final myNotificationAddress = await getMyNotificationAddress(); - final txns = await mainDB - .getTransactions(walletId) + final txns = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) .filter() .subTypeEqualTo(TransactionSubType.bip47Notification) .findAll(); for (final tx in txns) { - if (tx.type == TransactionType.incoming && - tx.address.value?.value == myNotificationAddress.value) { - final unBlindedPaymentCode = await unBlindedPaymentCodeFromTransaction( - transaction: tx, - ); + switch (tx.type) { + case TransactionType.incoming: + for (final output in tx.outputs) { + for (final outputAddress in output.addresses) { + if (outputAddress == myNotificationAddress.value) { + final unBlindedPaymentCode = + await unBlindedPaymentCodeFromTransaction( + transaction: tx, + ); - if (unBlindedPaymentCode != null && - paymentCodeString == unBlindedPaymentCode.toString()) { - // await _setConnectedCache(paymentCodeString, true); - return true; - } + if (unBlindedPaymentCode != null && + paymentCodeString == unBlindedPaymentCode.toString()) { + // await _setConnectedCache(paymentCodeString, true); + return true; + } - final unBlindedPaymentCodeBad = - await unBlindedPaymentCodeFromTransactionBad( - transaction: tx, - ); + final unBlindedPaymentCodeBad = + await unBlindedPaymentCodeFromTransactionBad( + transaction: tx, + ); - if (unBlindedPaymentCodeBad != null && - paymentCodeString == unBlindedPaymentCodeBad.toString()) { - // await _setConnectedCache(paymentCodeString, true); - return true; - } - } else if (tx.type == TransactionType.outgoing) { - if (tx.address.value?.otherData != null) { - final code = - await paymentCodeStringByKey(tx.address.value!.otherData!); - if (code == paymentCodeString) { - // await _setConnectedCache(paymentCodeString, true); - return true; + if (unBlindedPaymentCodeBad != null && + paymentCodeString == unBlindedPaymentCodeBad.toString()) { + // await _setConnectedCache(paymentCodeString, true); + return true; + } + } + } } - } + + case TransactionType.outgoing: + for (final output in tx.outputs) { + for (final outputAddress in output.addresses) { + final address = await mainDB.isar.addresses + .where() + .walletIdEqualTo(walletId) + .filter() + .subTypeEqualTo(AddressSubType.paynymNotification) + .and() + .valueEqualTo(outputAddress) + .findFirst(); + + if (address?.otherData != null) { + final code = await paymentCodeStringByKey(address!.otherData!); + if (code == paymentCodeString) { + // await _setConnectedCache(paymentCodeString, true); + return true; + } + } + } + } + default: + break; } } @@ -900,7 +920,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { return false; } - Uint8List? _pubKeyFromInput(Input input) { + Uint8List? _pubKeyFromInput(InputV2 input) { final scriptSigComponents = input.scriptSigAsm?.split(" ") ?? []; if (scriptSigComponents.length > 1) { return scriptSigComponents[1].fromHex; @@ -919,7 +939,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { } Future unBlindedPaymentCodeFromTransaction({ - required Transaction transaction, + required TransactionV2 transaction, }) async { try { final blindedCodeBytes = @@ -932,8 +952,8 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { final designatedInput = transaction.inputs.first; - final txPoint = designatedInput.txid.fromHex.reversed.toList(); - final txPointIndex = designatedInput.vout; + final txPoint = designatedInput.outpoint!.txid.fromHex.reversed.toList(); + final txPointIndex = designatedInput.outpoint!.vout; final rev = Uint8List(txPoint.length + 4); Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length); @@ -970,7 +990,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { } Future unBlindedPaymentCodeFromTransactionBad({ - required Transaction transaction, + required TransactionV2 transaction, }) async { try { final blindedCodeBytes = @@ -983,8 +1003,8 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { final designatedInput = transaction.inputs.first; - final txPoint = designatedInput.txid.fromHex.toList(); - final txPointIndex = designatedInput.vout; + final txPoint = designatedInput.outpoint!.txid.fromHex.toList(); + final txPointIndex = designatedInput.outpoint!.vout; final rev = Uint8List(txPoint.length + 4); Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length); @@ -1022,8 +1042,9 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { Future> getAllPaymentCodesFromNotificationTransactions() async { - final txns = await mainDB - .getTransactions(walletId) + final txns = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) .filter() .subTypeEqualTo(TransactionSubType.bip47Notification) .findAll(); @@ -1032,18 +1053,33 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { for (final tx in txns) { // 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, - networkType: networkType, - ), - ); + if (tx.type == TransactionType.outgoing) { + for (final output in tx.outputs) { + for (final outputAddress + in output.addresses.where((e) => e.isNotEmpty)) { + final address = await mainDB.isar.addresses + .where() + .walletIdEqualTo(walletId) + .filter() + .subTypeEqualTo(AddressSubType.paynymNotification) + .and() + .valueEqualTo(outputAddress) + .findFirst(); + + if (address?.otherData != null) { + final codeString = + await paymentCodeStringByKey(address!.otherData!); + if (codeString != null && + codes.where((e) => e.toString() == codeString).isEmpty) { + codes.add( + PaymentCode.fromPaymentCode( + codeString, + networkType: networkType, + ), + ); + } + } + } } } else { // otherwise we need to un blind the code @@ -1072,8 +1108,9 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { Future checkForNotificationTransactionsTo( Set otherCodeStrings) async { - final sentNotificationTransactions = await mainDB - .getTransactions(walletId) + final sentNotificationTransactions = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) .filter() .subTypeEqualTo(TransactionSubType.bip47Notification) .and() @@ -1087,23 +1124,37 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface { } for (final tx in sentNotificationTransactions) { - if (tx.address.value != null && tx.address.value!.otherData == null) { - final oldAddress = - await mainDB.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, - derivationPath: oldAddress.derivationPath, - type: oldAddress.type, - subType: AddressSubType.paynymNotification, - otherData: await storeCode(code.toString()), - ); - await mainDB.updateAddress(oldAddress, address); + for (final output in tx.outputs) { + for (final outputAddress in output.addresses) { + if (outputAddress.isNotEmpty) { + for (final code in codes) { + final notificationAddress = code.notificationAddressP2PKH(); + + if (outputAddress == notificationAddress) { + Address? storedAddress = + await mainDB.getAddress(walletId, outputAddress); + if (storedAddress == null) { + // most likely not mine + storedAddress = Address( + walletId: walletId, + value: notificationAddress, + publicKey: [], + derivationIndex: 0, + derivationPath: null, + type: AddressType.unknown, + subType: AddressSubType.paynymNotification, + otherData: await storeCode(code.toString()), + ); + } else { + storedAddress = storedAddress.copyWith( + subType: AddressSubType.paynymNotification, + otherData: await storeCode(code.toString()), + ); + } + + await mainDB.updateOrPutAddresses([storedAddress]); + } + } } } }