mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-22 02:24:30 +00:00
add initial segwit paynym support
This commit is contained in:
parent
6fb93b00ff
commit
5ad161e6a9
3 changed files with 316 additions and 253 deletions
|
@ -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<String?> 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<Address> currentReceivingPaynymAddress(PaymentCode sender) async {
|
||||
Future<Address> 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<String, Address>(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<Address> _generatePaynymReceivingAddress(
|
||||
PaymentCode sender,
|
||||
int index,
|
||||
) async {
|
||||
Future<Address> _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<void> checkCurrentPaynymReceivingAddressForTransactions(
|
||||
PaymentCode sender) async {
|
||||
final address = await currentReceivingPaynymAddress(sender);
|
||||
Future<void> 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<Future<void>> 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<bip32.BIP32> deriveNotificationBip32Node({
|
||||
required String mnemonic,
|
||||
required String mnemonicPassphrase,
|
||||
|
@ -267,15 +310,16 @@ mixin PaynymWalletInterface {
|
|||
}
|
||||
|
||||
/// fetch or generate this wallet's bip47 payment code
|
||||
Future<PaymentCode> getPaymentCode(
|
||||
DerivePathType derivePathType, [
|
||||
bip32.BIP32? bip32Root,
|
||||
]) async {
|
||||
final address = await getMyNotificationAddress(derivePathType, bip32Root);
|
||||
Future<PaymentCode> 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<Map<String, dynamic>> preparePaymentCodeSend({
|
||||
required PaymentCode paymentCode,
|
||||
required bool isSegwit,
|
||||
required Amount amount,
|
||||
Map<String, dynamic>? 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<Address> 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<Map<String, dynamic>> prepareNotificationTx({
|
||||
required int selectedTxFeeRate,
|
||||
required String targetPaymentCodeString,
|
||||
required bool isSegwit,
|
||||
int additionalOutputs = 0,
|
||||
List<UTXO>? 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<Tuple2<String, int>> _createNotificationTx({
|
||||
required String targetPaymentCodeString,
|
||||
required bool isSegwit,
|
||||
required List<SigningData> 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<String> broadcastNotificationTx(
|
||||
{required Map<String, dynamic> preparedTx}) async {
|
||||
Future<String> broadcastNotificationTx({
|
||||
required Map<String, dynamic> 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<PaymentCode?> 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<PaymentCode?> 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<List<PaymentCode>>
|
||||
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<PaymentCode> 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<PaymentCode> 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<void> restoreHistoryWith(
|
||||
PaymentCode other,
|
||||
int maxUnusedAddressGap,
|
||||
int maxNumberOfIndexesToCheck,
|
||||
) async {
|
||||
Future<void> 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<Address> 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<Address> 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<Address> 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<Address> 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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -60,7 +60,7 @@ dependencies:
|
|||
bip47:
|
||||
git:
|
||||
url: https://github.com/cypherstack/bip47.git
|
||||
ref: 48dd65f88822fba8543826274f6d51c17f735f93
|
||||
ref: 3e27b910d92e2ab816d589d202161490c00f986b
|
||||
|
||||
# Utility plugins
|
||||
# provider: ^6.0.1
|
||||
|
|
Loading…
Reference in a new issue