From 5ad161e6a9d270a276ded2a352c0d6cd7ede45ea Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Apr 2023 10:45:02 -0600 Subject: [PATCH] add initial segwit paynym support --- .../mixins/paynym_wallet_interface.dart | 561 ++++++++++-------- pubspec.lock | 6 +- pubspec.yaml | 2 +- 3 files changed, 316 insertions(+), 253 deletions(-) diff --git a/lib/services/mixins/paynym_wallet_interface.dart b/lib/services/mixins/paynym_wallet_interface.dart index 3093a8877..475885c92 100644 --- a/lib/services/mixins/paynym_wallet_interface.dart +++ b/lib/services/mixins/paynym_wallet_interface.dart @@ -20,7 +20,6 @@ import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/bip32_utils.dart'; import 'package:stackwallet/utilities/bip47_utils.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; -import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/logger.dart'; @@ -30,9 +29,10 @@ const String kPaynymDerivePath = "m/47'/0'/0'"; const String kPaynymNotificationDerivationPath = "$kPaynymDerivePath/0"; const String kPCodeKeyPrefix = "pCode_key_"; -String _receivingPaynymAddressDerivationPath(int index) { - return "$kPaynymDerivePath/$index/0"; -} +String _receivingPaynymAddressDerivationPath(int index) => + "$kPaynymDerivePath/$index/0"; +String _sendPaynymAddressDerivationPath(int index) => + "$kPaynymDerivePath/0/$index"; mixin PaynymWalletInterface { // passed in wallet data @@ -43,6 +43,7 @@ mixin PaynymWalletInterface { late final MainDB _db; late final ElectrumX _electrumXClient; late final SecureStorageInterface _secureStorage; + late final int _dustLimit; late final int _dustLimitP2PKH; late final int _minConfirms; @@ -78,6 +79,7 @@ mixin PaynymWalletInterface { required MainDB db, required ElectrumX electrumXClient, required SecureStorageInterface secureStorage, + required int dustLimit, required int dustLimitP2PKH, required int minConfirms, required Future Function() getMnemonicString, @@ -113,6 +115,7 @@ mixin PaynymWalletInterface { _db = db; _electrumXClient = electrumXClient; _secureStorage = secureStorage; + _dustLimitP2PKH = dustLimit; _dustLimitP2PKH = dustLimitP2PKH; _minConfirms = minConfirms; _getMnemonicString = getMnemonicString; @@ -130,19 +133,38 @@ mixin PaynymWalletInterface { // convenience getter btc_dart.NetworkType get networkType => _network; - Future
currentReceivingPaynymAddress(PaymentCode sender) async { + Future
currentReceivingPaynymAddress({ + required PaymentCode sender, + required bool isSegwit, + }) async { final keys = await lookupKey(sender.toString()); + final address = await _db .getAddresses(_walletId) .filter() .subTypeEqualTo(AddressSubType.paynymReceive) .and() + .group((q) { + if (isSegwit) { + return q + .typeEqualTo(AddressType.p2sh) + .or() + .typeEqualTo(AddressType.p2wpkh); + } else { + return q.typeEqualTo(AddressType.p2pkh); + } + }) + .and() .anyOf(keys, (q, String e) => q.otherDataEqualTo(e)) .sortByDerivationIndexDesc() .findFirst(); if (address == null) { - final generatedAddress = await _generatePaynymReceivingAddress(sender, 0); + final generatedAddress = await _generatePaynymReceivingAddress( + sender: sender, + index: 0, + generateSegwitAddress: isSegwit, + ); final existing = await _db .getAddresses(_walletId) @@ -158,16 +180,20 @@ mixin PaynymWalletInterface { await _db.updateAddress(existing, generatedAddress); } - return currentReceivingPaynymAddress(sender); + return currentReceivingPaynymAddress( + isSegwit: isSegwit, + sender: sender, + ); } else { return address; } } - Future
_generatePaynymReceivingAddress( - PaymentCode sender, - int index, - ) async { + Future
_generatePaynymReceivingAddress({ + required PaymentCode sender, + required int index, + required bool generateSegwitAddress, + }) async { final myPrivateKeyNode = await deriveReceivingPrivateKeyNode( mnemonic: (await _getMnemonicString())!, mnemonicPassphrase: (await _getMnemonicPassphrase())!, @@ -184,21 +210,27 @@ mixin PaynymWalletInterface { final address = await generatePaynymReceivingAddressFromKeyPair( pair: pair, derivationIndex: index, - derivePathType: DerivePathType.bip44, + isSegwit: generateSegwitAddress, fromPaymentCode: sender, ); return address; } - Future checkCurrentPaynymReceivingAddressForTransactions( - PaymentCode sender) async { - final address = await currentReceivingPaynymAddress(sender); + Future checkCurrentPaynymReceivingAddressForTransactions({ + required PaymentCode sender, + required bool isSegwit, + }) async { + final address = await currentReceivingPaynymAddress( + sender: sender, + isSegwit: isSegwit, + ); final txCount = await _getTxCount(address: address.value); if (txCount > 0) { // generate next address and add to db final nextAddress = await _generatePaynymReceivingAddress( - sender, - address.derivationIndex + 1, + sender: sender, + index: address.derivationIndex + 1, + generateSegwitAddress: isSegwit, ); final existing = await _db @@ -215,7 +247,10 @@ mixin PaynymWalletInterface { await _db.updateAddress(existing, nextAddress); } // keep checking until address with no tx history is set as current - await checkCurrentPaynymReceivingAddressForTransactions(sender); + await checkCurrentPaynymReceivingAddressForTransactions( + sender: sender, + isSegwit: isSegwit, + ); } } @@ -223,7 +258,14 @@ mixin PaynymWalletInterface { final codes = await getAllPaymentCodesFromNotificationTransactions(); final List> futures = []; for (final code in codes) { - futures.add(checkCurrentPaynymReceivingAddressForTransactions(code)); + futures.add(checkCurrentPaynymReceivingAddressForTransactions( + sender: code, + isSegwit: true, + )); + futures.add(checkCurrentPaynymReceivingAddressForTransactions( + sender: code, + isSegwit: false, + )); } await Future.wait(futures); } @@ -233,14 +275,15 @@ mixin PaynymWalletInterface { required String mnemonic, required String mnemonicPassphrase, }) async { - final root = await Bip32Utils.getBip32Root( + return _cachedRootNode ??= await Bip32Utils.getBip32Root( mnemonic, mnemonicPassphrase, _network, ); - return root; } + bip32.BIP32? _cachedRootNode; + Future deriveNotificationBip32Node({ required String mnemonic, required String mnemonicPassphrase, @@ -267,15 +310,16 @@ mixin PaynymWalletInterface { } /// fetch or generate this wallet's bip47 payment code - Future getPaymentCode( - DerivePathType derivePathType, [ - bip32.BIP32? bip32Root, - ]) async { - final address = await getMyNotificationAddress(derivePathType, bip32Root); + Future getPaymentCode({ + required bool isSegwit, + }) async { + final address = await getMyNotificationAddress( + isSegwit: isSegwit, + ); final pCodeString = await paymentCodeStringByKey(address.otherData!); final paymentCode = PaymentCode.fromPaymentCode( pCodeString!, - _network, + networkType: _network, ); return paymentCode; } @@ -299,6 +343,7 @@ mixin PaynymWalletInterface { Future> preparePaymentCodeSend({ required PaymentCode paymentCode, + required bool isSegwit, required Amount amount, Map? args, }) async { @@ -313,6 +358,7 @@ mixin PaynymWalletInterface { final sendToAddress = await nextUnusedSendAddressFrom( pCode: paymentCode, privateKeyNode: myPrivateKeyNode, + isSegwit: isSegwit, ); return _prepareSend( @@ -327,6 +373,7 @@ mixin PaynymWalletInterface { /// and your own private key Future
nextUnusedSendAddressFrom({ required PaymentCode pCode, + required bool isSegwit, required bip32.BIP32 privateKeyNode, int startIndex = 0, }) async { @@ -363,8 +410,8 @@ mixin PaynymWalletInterface { final address = await generatePaynymSendAddressFromKeyPair( pair: pair, derivationIndex: i, - derivePathType: DerivePathType.bip44, toPaymentCode: pCode, + isSegwit: isSegwit, ); final storedAddress = await _db.getAddress(_walletId, address.value); @@ -387,6 +434,7 @@ mixin PaynymWalletInterface { Future> prepareNotificationTx({ required int selectedTxFeeRate, required String targetPaymentCodeString, + required bool isSegwit, int additionalOutputs = 0, List? utxos, }) async { @@ -449,8 +497,7 @@ mixin PaynymWalletInterface { targetPaymentCodeString: targetPaymentCodeString, utxoSigningData: utxoSigningData, change: 0, - dustLimit: - satoshisBeingUsed, // override amount to get around absurd fees error + isSegwit: isSegwit, )) .item2; @@ -458,6 +505,7 @@ mixin PaynymWalletInterface { targetPaymentCodeString: targetPaymentCodeString, utxoSigningData: utxoSigningData, change: satoshisBeingUsed - amountToSend, + isSegwit: isSegwit, )) .item2; @@ -495,6 +543,7 @@ mixin PaynymWalletInterface { targetPaymentCodeString: targetPaymentCodeString, utxoSigningData: utxoSigningData, change: changeAmount, + isSegwit: isSegwit, ); int feeBeingPaid = satoshisBeingUsed - amountToSend - changeAmount; @@ -507,6 +556,7 @@ mixin PaynymWalletInterface { targetPaymentCodeString: targetPaymentCodeString, utxoSigningData: utxoSigningData, change: changeAmount, + isSegwit: isSegwit, ); } @@ -526,6 +576,7 @@ mixin PaynymWalletInterface { targetPaymentCodeString: targetPaymentCodeString, utxoSigningData: utxoSigningData, change: 0, + isSegwit: isSegwit, ); int feeBeingPaid = satoshisBeingUsed - amountToSend; @@ -547,6 +598,7 @@ mixin PaynymWalletInterface { targetPaymentCodeString: targetPaymentCodeString, utxoSigningData: utxoSigningData, change: 0, + isSegwit: isSegwit, ); int feeBeingPaid = satoshisBeingUsed - amountToSend; @@ -568,6 +620,7 @@ mixin PaynymWalletInterface { selectedTxFeeRate: selectedTxFeeRate, targetPaymentCodeString: targetPaymentCodeString, additionalOutputs: additionalOutputs + 1, + isSegwit: isSegwit, ); } else { throw InsufficientBalanceException( @@ -583,14 +636,16 @@ mixin PaynymWalletInterface { // equal to its vSize Future> _createNotificationTx({ required String targetPaymentCodeString, + required bool isSegwit, required List utxoSigningData, required int change, - int? dustLimit, }) async { try { - final targetPaymentCode = - PaymentCode.fromPaymentCode(targetPaymentCodeString, _network); - final myCode = await getPaymentCode(DerivePathType.bip44); + final targetPaymentCode = PaymentCode.fromPaymentCode( + targetPaymentCodeString, + networkType: _network, + ); + final myCode = await getPaymentCode(isSegwit: isSegwit); final utxo = utxoSigningData.first.utxo; final txPoint = utxo.txid.fromHex.reversed.toList(); @@ -642,11 +697,21 @@ mixin PaynymWalletInterface { utxoSigningData[i].output!, ); } + final String notificationAddress; + if (isSegwit) { + final publicKey = targetPaymentCode.derivePublicKey(0); + final data = btc_dart.P2WPKH( + data: btc_dart.PaymentData(pubkey: publicKey), + network: networkType, + ); + notificationAddress = data.data.address!; + } else { + notificationAddress = targetPaymentCode.notificationAddressP2PKH(); + } - // todo: modify address once segwit support is in our bip47 txb.addOutput( - targetPaymentCode.notificationAddressP2PKH(), - dustLimit ?? _dustLimitP2PKH, + notificationAddress, + isSegwit ? _dustLimit : _dustLimitP2PKH, ); txb.addOutput(opReturnScript, 0); @@ -687,8 +752,9 @@ mixin PaynymWalletInterface { } } - Future broadcastNotificationTx( - {required Map preparedTx}) async { + Future broadcastNotificationTx({ + required Map preparedTx, + }) async { try { Logging.instance.log("confirmNotificationTx txData: $preparedTx", level: LogLevel.Info); @@ -750,7 +816,9 @@ mixin PaynymWalletInterface { // .findAll(); final myNotificationAddress = - await getMyNotificationAddress(DerivePathTypeExt.primaryFor(_coin)); + await getMyNotificationAddress(isSegwit: false); + final myNotificationAddressSegwit = + await getMyNotificationAddress(isSegwit: true); final txns = await _db .getTransactions(_walletId) @@ -760,10 +828,10 @@ mixin PaynymWalletInterface { for (final tx in txns) { if (tx.type == TransactionType.incoming && - tx.address.value?.value == myNotificationAddress.value) { + (tx.address.value?.value == myNotificationAddress.value || + tx.address.value?.value == myNotificationAddressSegwit.value)) { final unBlindedPaymentCode = await unBlindedPaymentCodeFromTransaction( transaction: tx, - myNotificationAddress: myNotificationAddress, ); if (unBlindedPaymentCode != null && @@ -775,7 +843,6 @@ mixin PaynymWalletInterface { final unBlindedPaymentCodeBad = await unBlindedPaymentCodeFromTransactionBad( transaction: tx, - myNotificationAddress: myNotificationAddress, ); if (unBlindedPaymentCodeBad != null && @@ -820,13 +887,7 @@ mixin PaynymWalletInterface { Future unBlindedPaymentCodeFromTransaction({ required Transaction transaction, - required Address myNotificationAddress, }) async { - if (transaction.address.value != null && - transaction.address.value!.value != myNotificationAddress.value) { - return null; - } - try { final blindedCodeBytes = Bip47Utils.getBlindedPaymentCodeBytesFrom(transaction); @@ -864,7 +925,10 @@ mixin PaynymWalletInterface { unBlind: true, ); - final unBlindedPaymentCode = PaymentCode.fromPayload(unBlindedPayload); + final unBlindedPaymentCode = PaymentCode.fromPayload( + unBlindedPayload, + networkType: _network, + ); return unBlindedPaymentCode; } catch (e) { @@ -878,13 +942,7 @@ mixin PaynymWalletInterface { Future unBlindedPaymentCodeFromTransactionBad({ required Transaction transaction, - required Address myNotificationAddress, }) async { - if (transaction.address.value != null && - transaction.address.value!.value != myNotificationAddress.value) { - return null; - } - try { final blindedCodeBytes = Bip47Utils.getBlindedPaymentCodeBytesFrom(transaction); @@ -922,7 +980,10 @@ mixin PaynymWalletInterface { unBlind: true, ); - final unBlindedPaymentCode = PaymentCode.fromPayload(unBlindedPayload); + final unBlindedPaymentCode = PaymentCode.fromPayload( + unBlindedPayload, + networkType: _network, + ); return unBlindedPaymentCode; } catch (e) { @@ -936,8 +997,6 @@ mixin PaynymWalletInterface { Future> getAllPaymentCodesFromNotificationTransactions() async { - final myAddress = - await getMyNotificationAddress(DerivePathTypeExt.primaryFor(_coin)); final txns = await _db .getTransactions(_walletId) .filter() @@ -954,13 +1013,17 @@ mixin PaynymWalletInterface { await paymentCodeStringByKey(tx.address.value!.otherData!); if (codeString != null && codes.where((e) => e.toString() == codeString).isEmpty) { - codes.add(PaymentCode.fromPaymentCode(codeString, _network)); + codes.add( + PaymentCode.fromPaymentCode( + codeString, + networkType: _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) { @@ -969,7 +1032,6 @@ mixin PaynymWalletInterface { final unBlindedBad = await unBlindedPaymentCodeFromTransactionBad( transaction: tx, - myNotificationAddress: myAddress, ); if (unBlindedBad != null && codes @@ -995,7 +1057,7 @@ mixin PaynymWalletInterface { final List codes = []; for (final codeString in otherCodeStrings) { - codes.add(PaymentCode.fromPaymentCode(codeString, _network)); + codes.add(PaymentCode.fromPaymentCode(codeString, networkType: _network)); } for (final tx in sentNotificationTransactions) { @@ -1031,7 +1093,10 @@ mixin PaynymWalletInterface { final List extraCodes = []; for (final codeString in paymentCodeStrings) { if (codes.where((e) => e.toString() == codeString).isEmpty) { - final extraCode = PaymentCode.fromPaymentCode(codeString, _network); + final extraCode = PaymentCode.fromPaymentCode( + codeString, + networkType: _network, + ); if (extraCode.isValid()) { extraCodes.add(extraCode); } @@ -1044,9 +1109,10 @@ mixin PaynymWalletInterface { for (final code in codes) { futures.add( restoreHistoryWith( - code, - maxUnusedAddressGap, - maxNumberOfIndexesToCheck, + other: code, + maxUnusedAddressGap: maxUnusedAddressGap, + maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck, + checkSegwitAsWell: code.isSegWitEnabled(), ), ); } @@ -1054,11 +1120,12 @@ mixin PaynymWalletInterface { await Future.wait(futures); } - Future restoreHistoryWith( - PaymentCode other, - int maxUnusedAddressGap, - int maxNumberOfIndexesToCheck, - ) async { + Future restoreHistoryWith({ + required PaymentCode other, + required bool checkSegwitAsWell, + required int maxUnusedAddressGap, + required int maxNumberOfIndexesToCheck, + }) async { // https://en.bitcoin.it/wiki/BIP_0047#Path_levels const maxCount = 2147483647; assert(maxNumberOfIndexesToCheck < maxCount); @@ -1080,36 +1147,11 @@ mixin PaynymWalletInterface { int receivingGapCounter = 0; int outgoingGapCounter = 0; + // non segwit receiving for (int i = 0; i < maxNumberOfIndexesToCheck && - (receivingGapCounter < maxUnusedAddressGap || - outgoingGapCounter < maxUnusedAddressGap); + receivingGapCounter < maxUnusedAddressGap; i++) { - if (outgoingGapCounter < maxUnusedAddressGap) { - final paymentAddressSending = PaymentAddress( - paymentCode: other, - bip32Node: mySendBip32Node, - index: i, - networkType: networkType, - ); - final pair = paymentAddressSending.getSendAddressKeyPair(); - final address = await generatePaynymSendAddressFromKeyPair( - pair: pair, - derivationIndex: i, - derivePathType: DerivePathType.bip44, - toPaymentCode: other, - ); - addresses.add(address); - - final count = await _getTxCount(address: address.value); - - if (count > 0) { - outgoingGapCounter = 0; - } else { - outgoingGapCounter++; - } - } - if (receivingGapCounter < maxUnusedAddressGap) { final paymentAddressReceiving = PaymentAddress( paymentCode: other, @@ -1122,7 +1164,7 @@ mixin PaynymWalletInterface { final address = await generatePaynymReceivingAddressFromKeyPair( pair: pair, derivationIndex: i, - derivePathType: DerivePathType.bip44, + isSegwit: false, fromPaymentCode: other, ); addresses.add(address); @@ -1136,52 +1178,131 @@ mixin PaynymWalletInterface { } } } + + // non segwit sends + for (int i = 0; + i < maxNumberOfIndexesToCheck && + outgoingGapCounter < maxUnusedAddressGap; + i++) { + if (outgoingGapCounter < maxUnusedAddressGap) { + final pair = PaymentAddress( + paymentCode: other, + bip32Node: mySendBip32Node, + index: i, + networkType: networkType, + ).getSendAddressKeyPair(); + final address = await generatePaynymSendAddressFromKeyPair( + pair: pair, + derivationIndex: i, + isSegwit: false, + toPaymentCode: other, + ); + addresses.add(address); + + final count = await _getTxCount(address: address.value); + + if (count > 0) { + outgoingGapCounter = 0; + } else { + outgoingGapCounter++; + } + } + } + + if (checkSegwitAsWell) { + int receivingGapCounterSegwit = 0; + int outgoingGapCounterSegwit = 0; + // segwit receiving + for (int i = 0; + i < maxNumberOfIndexesToCheck && + receivingGapCounterSegwit < maxUnusedAddressGap; + i++) { + if (receivingGapCounterSegwit < maxUnusedAddressGap) { + final paymentAddressReceiving = PaymentAddress( + paymentCode: other, + bip32Node: receivingNode.derive(i), + index: 0, + networkType: networkType, + ); + + final pair = paymentAddressReceiving.getReceiveAddressKeyPair(); + final address = await generatePaynymReceivingAddressFromKeyPair( + pair: pair, + derivationIndex: i, + isSegwit: true, + fromPaymentCode: other, + ); + addresses.add(address); + + final count = await _getTxCount(address: address.value); + + if (count > 0) { + receivingGapCounterSegwit = 0; + } else { + receivingGapCounterSegwit++; + } + } + } + + // segwit sends + for (int i = 0; + i < maxNumberOfIndexesToCheck && + outgoingGapCounterSegwit < maxUnusedAddressGap; + i++) { + if (outgoingGapCounterSegwit < maxUnusedAddressGap) { + final pair = PaymentAddress( + paymentCode: other, + bip32Node: mySendBip32Node, + index: i, + networkType: networkType, + ).getSendAddressKeyPair(); + final address = await generatePaynymSendAddressFromKeyPair( + pair: pair, + derivationIndex: i, + isSegwit: true, + toPaymentCode: other, + ); + addresses.add(address); + + final count = await _getTxCount(address: address.value); + + if (count > 0) { + outgoingGapCounterSegwit = 0; + } else { + outgoingGapCounterSegwit++; + } + } + } + } await _db.updateOrPutAddresses(addresses); } Future
generatePaynymSendAddressFromKeyPair({ required btc_dart.ECPair pair, required int derivationIndex, - required DerivePathType derivePathType, + required bool isSegwit, required PaymentCode toPaymentCode, }) async { final data = btc_dart.PaymentData(pubkey: pair.publicKey); - String addressString; - switch (derivePathType) { - case DerivePathType.bip44: - addressString = - btc_dart.P2PKH(data: data, network: _network).data.address!; - break; + final String addressString; - // The following doesn't apply currently - // case DerivePathType.bip49: - // addressString = btc_dart - // .P2SH( - // data: btc_dart.PaymentData( - // redeem: btc_dart - // .P2WPKH( - // data: data, - // network: network, - // ) - // .data), - // network: network, - // ) - // .data - // .address!; - // break; - // - // case DerivePathType.bip84: - // addressString = btc_dart - // .P2WPKH( - // network: network, - // data: data, - // ) - // .data - // .address!; - // break; - default: - throw UnimplementedError("segwit paynyms not implemented yet"); + if (isSegwit) { + addressString = btc_dart + .P2WPKH( + network: _network, + data: data, + ) + .data + .address!; + } else { + addressString = btc_dart + .P2PKH( + data: data, + network: _network, + ) + .data + .address!; } final address = Address( @@ -1189,8 +1310,8 @@ mixin PaynymWalletInterface { value: addressString, publicKey: pair.publicKey, derivationIndex: derivationIndex, - derivationPath: - null, // might as well use null due to complexity of context + derivationPath: DerivationPath() + ..value = _sendPaynymAddressDerivationPath(derivationIndex), type: AddressType.nonWallet, subType: AddressSubType.paynymSend, otherData: await storeCode(toPaymentCode.toString()), @@ -1202,55 +1323,32 @@ mixin PaynymWalletInterface { Future
generatePaynymReceivingAddressFromKeyPair({ required btc_dart.ECPair pair, required int derivationIndex, - required DerivePathType derivePathType, + required bool isSegwit, required PaymentCode fromPaymentCode, }) async { final data = btc_dart.PaymentData(pubkey: pair.publicKey); - String addressString; - AddressType addrType; - switch (derivePathType) { - case DerivePathType.bip44: - addressString = btc_dart - .P2PKH( - data: data, - network: _network, - ) - .data - .address!; - addrType = AddressType.p2pkh; - break; + final String addressString; + final AddressType addressType; - // The following doesn't apply currently - // case DerivePathType.bip49: - // addressString = btc_dart - // .P2SH( - // data: btc_dart.PaymentData( - // redeem: btc_dart - // .P2WPKH( - // data: data, - // network: network, - // ) - // .data), - // network: network, - // ) - // .data - // .address!; - // addrType = AddressType.p2sh; - // break; - // - // case DerivePathType.bip84: - // addressString = btc_dart - // .P2WPKH( - // network: network, - // data: data, - // ) - // .data - // .address!; - // addrType = AddressType.p2wpkh; - // break; - default: - throw UnimplementedError("segwit paynyms not implemented yet"); + if (isSegwit) { + addressString = btc_dart + .P2WPKH( + network: _network, + data: data, + ) + .data + .address!; + addressType = AddressType.p2wpkh; + } else { + addressString = btc_dart + .P2PKH( + data: data, + network: _network, + ) + .data + .address!; + addressType = AddressType.p2pkh; } final address = Address( @@ -1260,12 +1358,12 @@ mixin PaynymWalletInterface { derivationIndex: derivationIndex, derivationPath: DerivationPath() ..value = _receivingPaynymAddressDerivationPath(derivationIndex), - type: addrType, + type: addressType, subType: AddressSubType.paynymReceive, otherData: await storeCode(fromPaymentCode.toString()), ); - final myCode = await getPaymentCode(DerivePathType.bip44); + final myCode = await getPaymentCode(isSegwit: isSegwit); final bip32NetworkType = bip32.NetworkType( wif: _network.wif, @@ -1284,27 +1382,10 @@ mixin PaynymWalletInterface { return address; } - Future
getMyNotificationAddress( - DerivePathType derivePathType, [ - bip32.BIP32? bip32Root, - ]) async { - // TODO: fix when segwit is here - derivePathType = DerivePathType.bip44; - - AddressType type; - switch (derivePathType) { - case DerivePathType.bip44: - type = AddressType.p2pkh; - break; - case DerivePathType.bip49: - type = AddressType.p2sh; - break; - case DerivePathType.bip84: - type = AddressType.p2wpkh; - break; - default: - throw Exception("DerivePathType $derivePathType not supported"); - } + Future
getMyNotificationAddress({ + required bool isSegwit, + }) async { + final AddressType type = isSegwit ? AddressType.p2wpkh : AddressType.p2pkh; final storedAddress = await _db .getAddresses(_walletId) @@ -1320,56 +1401,38 @@ mixin PaynymWalletInterface { if (storedAddress != null) { return storedAddress; } else { - final root = bip32Root ?? - await _getRootNode( - mnemonic: (await _getMnemonicString())!, - mnemonicPassphrase: (await _getMnemonicPassphrase())!, - ); + final root = await _getRootNode( + mnemonic: (await _getMnemonicString())!, + mnemonicPassphrase: (await _getMnemonicPassphrase())!, + ); final node = root.derivePath(kPaynymDerivePath); final paymentCode = PaymentCode.fromBip32Node( node, - _network, + networkType: _network, + shouldSetSegwitBit: isSegwit, ); - String addressString; - final data = - btc_dart.PaymentData(pubkey: paymentCode.notificationPublicKey()); - switch (derivePathType) { - case DerivePathType.bip44: - addressString = btc_dart - .P2PKH( - data: data, - network: _network, - ) - .data - .address!; - break; - // case DerivePathType.bip49: - // addressString = btc_dart - // .P2SH( - // data: btc_dart.PaymentData( - // redeem: btc_dart - // .P2WPKH( - // data: data, - // network: network, - // ) - // .data), - // network: network, - // ) - // .data - // .address!; - // break; - // case DerivePathType.bip84: - // addressString = btc_dart - // .P2WPKH( - // network: network, - // data: data, - // ) - // .data - // .address!; - // break; - default: - throw UnimplementedError("segwit paynyms not implemented yet"); + final String addressString; + final data = btc_dart.PaymentData( + pubkey: paymentCode.notificationPublicKey(), + ); + + if (isSegwit) { + addressString = btc_dart + .P2WPKH( + network: _network, + data: data, + ) + .data + .address!; + } else { + addressString = btc_dart + .P2PKH( + data: data, + network: _network, + ) + .data + .address!; } final address = Address( diff --git a/pubspec.lock b/pubspec.lock index 704bbe10e..a59162d95 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -111,11 +111,11 @@ packages: dependency: "direct main" description: path: "." - ref: "48dd65f88822fba8543826274f6d51c17f735f93" - resolved-ref: "48dd65f88822fba8543826274f6d51c17f735f93" + ref: "3e27b910d92e2ab816d589d202161490c00f986b" + resolved-ref: "3e27b910d92e2ab816d589d202161490c00f986b" url: "https://github.com/cypherstack/bip47.git" source: git - version: "1.0.0" + version: "2.0.0" bitbox: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 9a336d4b1..40a7056e3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -60,7 +60,7 @@ dependencies: bip47: git: url: https://github.com/cypherstack/bip47.git - ref: 48dd65f88822fba8543826274f6d51c17f735f93 + ref: 3e27b910d92e2ab816d589d202161490c00f986b # Utility plugins # provider: ^6.0.1