From 433686bce3de78f436c75b44f31330ca6dcb89e8 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 30 Oct 2024 12:13:59 -0300 Subject: [PATCH] feat: derivationinfo to address records --- cw_bitcoin/lib/bitcoin_address_record.dart | 21 ++- .../lib/bitcoin_hardware_wallet_service.dart | 9 +- cw_bitcoin/lib/bitcoin_unspent.dart | 5 +- cw_bitcoin/lib/bitcoin_wallet.dart | 57 ++++++- cw_bitcoin/lib/bitcoin_wallet_addresses.dart | 35 +++-- .../lib/electrum_transaction_history.dart | 3 - cw_bitcoin/lib/electrum_wallet.dart | 146 +++++------------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 115 +++++++------- cw_bitcoin/lib/electrum_wallet_snapshot.dart | 3 +- .../lib/litecoin_hardware_wallet_service.dart | 7 +- cw_bitcoin/lib/litecoin_wallet.dart | 11 +- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 18 ++- cw_bitcoin/pubspec.lock | 20 +-- .../lib/src/bitcoin_cash_wallet.dart | 2 + .../src/bitcoin_cash_wallet_addresses.dart | 8 +- cw_core/lib/wallet_info.dart | 5 +- 16 files changed, 253 insertions(+), 212 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index 43c1d5e14..72ca4b23e 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -6,7 +6,7 @@ abstract class BaseBitcoinAddressRecord { BaseBitcoinAddressRecord( this.address, { required this.index, - this.isChange = false, + bool isChange = false, int txCount = 0, int balance = 0, String name = '', @@ -17,7 +17,8 @@ abstract class BaseBitcoinAddressRecord { _balance = balance, _name = name, _isUsed = isUsed, - _isHidden = isHidden ?? isChange; + _isHidden = isHidden ?? isChange, + _isChange = isChange; @override bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address; @@ -25,7 +26,8 @@ abstract class BaseBitcoinAddressRecord { final String address; final bool _isHidden; bool get isHidden => _isHidden; - bool isChange; + final bool _isChange; + bool get isChange => _isChange; final int index; int _txCount; int _balance; @@ -55,9 +57,12 @@ abstract class BaseBitcoinAddressRecord { } class BitcoinAddressRecord extends BaseBitcoinAddressRecord { + final BitcoinDerivationInfo derivationInfo; + BitcoinAddressRecord( super.address, { required super.index, + required this.derivationInfo, super.isHidden, super.isChange = false, super.txCount = 0, @@ -81,6 +86,9 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { return BitcoinAddressRecord( decoded['address'] as String, index: decoded['index'] as int, + derivationInfo: BitcoinDerivationInfo.fromJSON( + decoded['derivationInfo'] as Map, + ), isHidden: decoded['isHidden'] as bool? ?? false, isChange: decoded['isChange'] as bool? ?? false, isUsed: decoded['isUsed'] as bool? ?? false, @@ -101,6 +109,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { String toJSON() => json.encode({ 'address': address, 'index': index, + 'derivationInfo': derivationInfo.toJSON(), 'isHidden': isHidden, 'isChange': isChange, 'isUsed': isUsed, @@ -116,6 +125,8 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { int get labelIndex => index; final String? labelHex; + static bool isChangeAddress(int labelIndex) => labelIndex == 0; + BitcoinSilentPaymentAddressRecord( super.address, { required int labelIndex, @@ -126,9 +137,9 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { super.type = SilentPaymentsAddresType.p2sp, super.isHidden, this.labelHex, - }) : super(index: labelIndex, isChange: labelIndex == 0) { + }) : super(index: labelIndex, isChange: isChangeAddress(labelIndex)) { if (labelIndex != 1 && labelHex == null) { - throw ArgumentError('label must be provided for silent address index > 0'); + throw ArgumentError('label must be provided for silent address index != 1'); } } diff --git a/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart b/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart index c63c1fe3a..415ae0e98 100644 --- a/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart @@ -24,9 +24,14 @@ class BitcoinHardwareWalletService { final xpub = await bitcoinLedgerApp.getXPubKey(derivationPath: derivationPath); final bip32 = Bip32Slip10Secp256k1.fromExtendedKey(xpub).childKey(Bip32KeyIndex(0)); + final fullPath = Bip32PathParser.parse(derivationPath).addElem(Bip32KeyIndex(0)); + + final address = ECPublic.fromBip32(bip32.derive(fullPath).publicKey) + .toP2wpkhAddress() + .toAddress(BitcoinNetwork.mainnet); + accounts.add(HardwareAccountData( - address: P2wpkhAddress.fromBip32(bip32: bip32, isChange: false, index: i) - .toAddress(BitcoinNetwork.mainnet), + address: address, accountIndex: i, derivationPath: derivationPath, masterFingerprint: masterFp, diff --git a/cw_bitcoin/lib/bitcoin_unspent.dart b/cw_bitcoin/lib/bitcoin_unspent.dart index b10eb47f6..6dd741b63 100644 --- a/cw_bitcoin/lib/bitcoin_unspent.dart +++ b/cw_bitcoin/lib/bitcoin_unspent.dart @@ -32,7 +32,10 @@ class BitcoinUnspent extends Unspent { @override bool operator ==(Object o) { - print('BitcoinUnspent operator =='); + if (identical(this, o)) return true; return o is BitcoinUnspent && hash == o.hash && vout == o.vout; } + + @override + int get hashCode => Object.hash(hash, vout); } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 6a0e3f4e7..ec2384a08 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -360,7 +360,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { updatedUnspentCoins.addAll(await fetchUnspent(address)); })); - unspentCoins = updatedUnspentCoins.toSet(); + unspentCoins.addAll(updatedUnspentCoins); if (unspentCoinsInfo.length != updatedUnspentCoins.length) { unspentCoins.forEach((coin) => addCoinInfo(coin)); @@ -1033,3 +1033,58 @@ Future delegatedScan(ScanData scanData) async { // ); // } } + +class ScanNode { + final Uri uri; + final bool? useSSL; + + ScanNode(this.uri, this.useSSL); +} + +class ScanData { + final SendPort sendPort; + final SilentPaymentOwner silentAddress; + final int height; + final ScanNode? node; + final BasedUtxoNetwork network; + final int chainTip; + final List transactionHistoryIds; + final Map labels; + final List labelIndexes; + final bool isSingleScan; + + ScanData({ + required this.sendPort, + required this.silentAddress, + required this.height, + required this.node, + required this.network, + required this.chainTip, + required this.transactionHistoryIds, + required this.labels, + required this.labelIndexes, + required this.isSingleScan, + }); + + factory ScanData.fromHeight(ScanData scanData, int newHeight) { + return ScanData( + sendPort: scanData.sendPort, + silentAddress: scanData.silentAddress, + height: newHeight, + node: scanData.node, + network: scanData.network, + chainTip: scanData.chainTip, + transactionHistoryIds: scanData.transactionHistoryIds, + labels: scanData.labels, + labelIndexes: scanData.labelIndexes, + isSingleScan: scanData.isSingleScan, + ); + } +} + +class SyncResponse { + final int height; + final SyncStatus syncStatus; + + SyncResponse(this.height, this.syncStatus); +} diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index ab7a45d4f..37a297b31 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -39,27 +39,44 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S required bool isChange, required int index, required BitcoinAddressType addressType, + required BitcoinDerivationInfo derivationInfo, }) { switch (addressType) { case P2pkhAddressType.p2pkh: - return P2pkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index); - case SegwitAddresType.p2tr: - return P2trAddress.fromBip32(bip32: bip32, isChange: isChange, index: index); - case SegwitAddresType.p2wsh: - return P2wshAddress.fromBip32(bip32: bip32, isChange: isChange, index: index); - case P2shAddressType.p2wpkhInP2sh: - return P2shAddress.fromBip32( + return P2pkhAddress.fromDerivation( bip32: bip32, + derivationInfo: derivationInfo, + isChange: isChange, + index: index, + ); + case SegwitAddresType.p2tr: + return P2trAddress.fromDerivation( + bip32: bip32, + derivationInfo: derivationInfo, + isChange: isChange, + index: index, + ); + case SegwitAddresType.p2wsh: + return P2wshAddress.fromDerivation( + bip32: bip32, + derivationInfo: derivationInfo, + isChange: isChange, + index: index, + ); + case P2shAddressType.p2wpkhInP2sh: + return P2shAddress.fromDerivation( + bip32: bip32, + derivationInfo: derivationInfo, isChange: isChange, index: index, type: P2shAddressType.p2wpkhInP2sh, ); case SegwitAddresType.p2wpkh: - return P2wpkhAddress.fromBip32( + return P2wpkhAddress.fromDerivation( bip32: bip32, + derivationInfo: derivationInfo, isChange: isChange, index: index, - isElectrum: false, // TODO: ); default: throw ArgumentError('Invalid address type'); diff --git a/cw_bitcoin/lib/electrum_transaction_history.dart b/cw_bitcoin/lib/electrum_transaction_history.dart index b688f097b..f5d11954a 100644 --- a/cw_bitcoin/lib/electrum_transaction_history.dart +++ b/cw_bitcoin/lib/electrum_transaction_history.dart @@ -4,11 +4,8 @@ import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_history.dart'; -import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:mobx/mobx.dart'; -import 'package:cw_core/transaction_history.dart'; -import 'package:cw_bitcoin/electrum_transaction_info.dart'; part 'electrum_transaction_history.g.dart'; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 373b68085..1d0bcfa2d 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'dart:isolate'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -38,7 +37,6 @@ import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger; import 'package:mobx/mobx.dart'; -import 'package:hex/hex.dart'; import 'package:http/http.dart' as http; part 'electrum_wallet.g.dart'; @@ -69,7 +67,8 @@ abstract class ElectrumWalletBase _password = password, _isTransactionUpdating = false, isEnabledAutoGenerateSubaddress = true, - unspentCoins = {}, + // TODO: inital unspent coins + unspentCoins = ObservableSet(), scripthashesListening = {}, balance = ObservableMap.of(currency != null ? { @@ -221,8 +220,7 @@ abstract class ElectrumWalletBase ); String _password; - @observable - Set unspentCoins; + ObservableSet unspentCoins; @observable TransactionPriorities? feeRates; @@ -404,7 +402,7 @@ abstract class ElectrumWalletBase bool _isBelowDust(int amount) => amount <= _dustAmount && network != BitcoinNetwork.testnet; - UtxoDetails _createUTXOS({ + TxCreateUtxoDetails _createUTXOS({ required bool sendAll, required int credentialsAmount, required bool paysToSilentPayment, @@ -484,13 +482,13 @@ abstract class ElectrumWalletBase .toHex(); } - // TODO: isElectrum - final derivationPath = BitcoinAddressUtils.getDerivationPath( - type: utx.bitcoinAddressRecord.type, - account: utx.bitcoinAddressRecord.isChange ? 1 : 0, - index: utx.bitcoinAddressRecord.index, - ); - publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath); + if (utx.bitcoinAddressRecord is BitcoinAddressRecord) { + final derivationPath = (utx.bitcoinAddressRecord as BitcoinAddressRecord) + .derivationInfo + .derivationPath + .toString(); + publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath); + } utxos.add( UtxoWithAddress( @@ -521,7 +519,7 @@ abstract class ElectrumWalletBase throw BitcoinTransactionNoInputsException(); } - return UtxoDetails( + return TxCreateUtxoDetails( availableInputs: availableInputs, unconfirmedCoins: unconfirmedCoins, utxos: utxos, @@ -662,11 +660,7 @@ abstract class ElectrumWalletBase isChange: true, )); - final changeDerivationPath = BitcoinAddressUtils.getDerivationPath( - type: changeAddress.type, - account: changeAddress.isChange ? 1 : 0, - index: changeAddress.index, - ); + final changeDerivationPath = changeAddress.derivationInfo.derivationPath.toString(); utxoDetails.publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath('', changeDerivationPath); @@ -1176,7 +1170,7 @@ abstract class ElectrumWalletBase updatedUnspentCoins.addAll(await fetchUnspent(address)); })); - unspentCoins = updatedUnspentCoins.toSet(); + unspentCoins.addAll(updatedUnspentCoins); if (unspentCoinsInfo.length != updatedUnspentCoins.length) { unspentCoins.forEach((coin) => addCoinInfo(coin)); @@ -1220,17 +1214,7 @@ abstract class ElectrumWalletBase final newUnspentCoins = (await fetchUnspent(addressRecord)).toSet(); await updateCoins(newUnspentCoins); - print([1, unspentCoins.containsAll(newUnspentCoins)]); - if (!unspentCoins.containsAll(newUnspentCoins)) { - newUnspentCoins.forEach((coin) { - print(unspentCoins.contains(coin)); - print([coin.vout, coin.hash]); - print([unspentCoins.first.vout, unspentCoins.first.hash]); - if (!unspentCoins.contains(coin)) { - unspentCoins.add(coin); - } - }); - } + unspentCoins.addAll(newUnspentCoins); // if (unspentCoinsInfo.length != unspentCoins.length) { // unspentCoins.forEach(addCoinInfo); @@ -1400,7 +1384,7 @@ abstract class ElectrumWalletBase if (index + 1 <= script.length) { try { final opReturnData = script[index + 1].toString(); - memo = utf8.decode(HEX.decode(opReturnData)); + memo = StringUtils.decode(BytesUtils.fromHexString(opReturnData)); continue; } catch (_) { throw Exception('Cannot decode OP_RETURN data'); @@ -1708,6 +1692,7 @@ abstract class ElectrumWalletBase isChange: addressRecord.isChange, gap: gapLimit, type: addressRecord.type, + derivationInfo: BitcoinAddressUtils.getDerivationFromType(addressRecord.type), ); } } @@ -1805,21 +1790,17 @@ abstract class ElectrumWalletBase @override Future signMessage(String message, {String? address = null}) async { - Bip32Slip10Secp256k1 HD = bip32; + final record = walletAddresses.getFromAddresses(address!); - final record = walletAddresses.allAddresses.firstWhere((element) => element.address == address); + final path = Bip32PathParser.parse(walletInfo.derivationInfo!.derivationPath!) + .addElem( + Bip32KeyIndex(BitcoinAddressUtils.getAccountFromChange(record.isChange)), + ) + .addElem(Bip32KeyIndex(record.index)); - if (record.isChange) { - HD = HD.childKey(Bip32KeyIndex(1)); - } else { - HD = HD.childKey(Bip32KeyIndex(0)); - } + final priv = ECPrivate.fromHex(bip32.derive(path).privateKey.toHex()); - HD = HD.childKey(Bip32KeyIndex(record.index)); - final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex()); - - String messagePrefix = '\x18Bitcoin Signed Message:\n'; - final hexEncoded = priv.signMessage(utf8.encode(message), messagePrefix: messagePrefix); + final hexEncoded = priv.signMessage(StringUtils.encode(message)); final decodedSig = hex.decode(hexEncoded); return base64Encode(decodedSig); } @@ -1835,7 +1816,7 @@ abstract class ElectrumWalletBase if (signature.endsWith('=')) { sigDecodedBytes = base64.decode(signature); } else { - sigDecodedBytes = hex.decode(signature); + sigDecodedBytes = BytesUtils.fromHexString(signature); } if (sigDecodedBytes.length != 64 && sigDecodedBytes.length != 65) { @@ -1845,7 +1826,7 @@ abstract class ElectrumWalletBase String messagePrefix = '\x18Bitcoin Signed Message:\n'; final messageHash = QuickCrypto.sha256Hash( - BitcoinSignerUtils.magicMessage(utf8.encode(message), messagePrefix)); + BitcoinSignerUtils.magicMessage(StringUtils.encode(message), messagePrefix)); List correctSignature = sigDecodedBytes.length == 65 ? sigDecodedBytes.sublist(1) : List.from(sigDecodedBytes); @@ -1911,14 +1892,14 @@ abstract class ElectrumWalletBase break; case ConnectionStatus.disconnected: - // if (syncStatus is! NotConnectedSyncStatus) { - // syncStatus = NotConnectedSyncStatus(); - // } + if (syncStatus is! NotConnectedSyncStatus) { + syncStatus = NotConnectedSyncStatus(); + } break; case ConnectionStatus.failed: - // if (syncStatus is! LostConnectionSyncStatus) { - // syncStatus = LostConnectionSyncStatus(); - // } + if (syncStatus is! LostConnectionSyncStatus) { + syncStatus = LostConnectionSyncStatus(); + } break; case ConnectionStatus.connecting: if (syncStatus is! ConnectingSyncStatus) { @@ -1989,7 +1970,7 @@ abstract class ElectrumWalletBase if (index + 1 <= script.length) { try { final opReturnData = script[index + 1].toString(); - final decodedString = utf8.decode(HEX.decode(opReturnData)); + final decodedString = StringUtils.decode(BytesUtils.fromHexString(opReturnData)); outputAddresses.add('OP_RETURN:$decodedString'); } catch (_) { outputAddresses.add('OP_RETURN:'); @@ -2005,61 +1986,6 @@ abstract class ElectrumWalletBase } } -class ScanNode { - final Uri uri; - final bool? useSSL; - - ScanNode(this.uri, this.useSSL); -} - -class ScanData { - final SendPort sendPort; - final SilentPaymentOwner silentAddress; - final int height; - final ScanNode? node; - final BasedUtxoNetwork network; - final int chainTip; - final List transactionHistoryIds; - final Map labels; - final List labelIndexes; - final bool isSingleScan; - - ScanData({ - required this.sendPort, - required this.silentAddress, - required this.height, - required this.node, - required this.network, - required this.chainTip, - required this.transactionHistoryIds, - required this.labels, - required this.labelIndexes, - required this.isSingleScan, - }); - - factory ScanData.fromHeight(ScanData scanData, int newHeight) { - return ScanData( - sendPort: scanData.sendPort, - silentAddress: scanData.silentAddress, - height: newHeight, - node: scanData.node, - network: scanData.network, - chainTip: scanData.chainTip, - transactionHistoryIds: scanData.transactionHistoryIds, - labels: scanData.labels, - labelIndexes: scanData.labelIndexes, - isSingleScan: scanData.isSingleScan, - ); - } -} - -class SyncResponse { - final int height; - final SyncStatus syncStatus; - - SyncResponse(this.height, this.syncStatus); -} - class EstimatedTxResult { EstimatedTxResult({ required this.utxos, @@ -2095,7 +2021,7 @@ class PublicKeyWithDerivationPath { final String publicKey; } -class UtxoDetails { +class TxCreateUtxoDetails { final List availableInputs; final List unconfirmedCoins; final List utxos; @@ -2106,7 +2032,7 @@ class UtxoDetails { final bool spendsSilentPayment; final bool spendsUnconfirmedTX; - UtxoDetails({ + TxCreateUtxoDetails({ required this.availableInputs, required this.unconfirmedCoins, required this.utxos, diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index c6600841b..81ed23d28 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -43,7 +43,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { int initialSilentAddressIndex = 0, List? initialMwebAddresses, BitcoinAddressType? initialAddressPageType, - }) : _allAddresses = (initialAddresses ?? []).toSet(), + }) : _allAddresses = ObservableSet.of(initialAddresses ?? []), addressesByReceiveType = ObservableList.of(([]).toSet()), receiveAddresses = ObservableList.of( @@ -63,11 +63,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { mwebAddresses = ObservableList.of((initialMwebAddresses ?? []).toSet()), super(walletInfo) { - silentAddress = SilentPaymentOwner.fromPrivateKeys( - b_scan: ECPrivate.fromHex(bip32.derive(SCAN_PATH).privateKey.toHex()), - b_spend: ECPrivate.fromHex(bip32.derive(SPEND_PATH).privateKey.toHex()), - network: network, - ); + // TODO: initial silent address, not every time + silentAddress = SilentPaymentOwner.fromBip32(bip32); + if (silentAddresses.length == 0) { silentAddresses.add(BitcoinSilentPaymentAddressRecord( silentAddress.toString(), @@ -91,8 +89,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { static const defaultChangeAddressesCount = 17; static const gap = 20; - @observable - final Set _allAddresses; + final ObservableSet _allAddresses; final ObservableList addressesByReceiveType; final ObservableList receiveAddresses; final ObservableList changeAddresses; @@ -119,6 +116,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @computed List get allAddresses => _allAddresses.toList(); + BitcoinAddressRecord getFromAddresses(String address) { + return _allAddresses.firstWhere((element) => element.address == address); + } + @override @computed String get address { @@ -189,7 +190,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } @override - String get primaryAddress => getAddress(isChange: false, index: 0, addressType: addressPageType); + String get primaryAddress => _allAddresses.first.address; Map currentReceiveAddressIndexByType; @@ -250,7 +251,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { updateAddressesByMatch(); updateReceiveAddresses(); updateChangeAddresses(); - _validateAddresses(); await updateAddressesInBox(); if (currentReceiveAddressIndex >= receiveAddresses.length) { @@ -263,15 +263,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } @action - Future getChangeAddress( - {List? inputs, List? outputs, bool isPegIn = false}) async { + Future getChangeAddress({ + List? inputs, + List? outputs, + bool isPegIn = false, + }) async { updateChangeAddresses(); - if (changeAddresses.isEmpty) { - final newAddresses = await _createNewAddresses(gap, isChange: true); - addAddresses(newAddresses); - } - if (currentChangeAddressIndex >= changeAddresses.length) { currentChangeAddressIndex = 0; } @@ -326,13 +324,20 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final newAddressIndex = addressesByReceiveType.fold( 0, (int acc, addressRecord) => addressRecord.isChange == false ? acc + 1 : acc); + final derivationInfo = BitcoinAddressUtils.getDerivationFromType(addressPageType); final address = BitcoinAddressRecord( - getAddress(isChange: false, index: newAddressIndex, addressType: addressPageType), + getAddress( + isChange: false, + index: newAddressIndex, + addressType: addressPageType, + derivationInfo: derivationInfo, + ), index: newAddressIndex, isChange: false, name: label, type: addressPageType, network: network, + derivationInfo: BitcoinAddressUtils.getDerivationFromType(addressPageType), ); _allAddresses.add(address); Future.delayed(Duration.zero, () => updateAddressesByMatch()); @@ -343,6 +348,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { required bool isChange, required int index, required BitcoinAddressType addressType, + required BitcoinDerivationInfo derivationInfo, }) { throw UnimplementedError(); } @@ -351,17 +357,28 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { required bool isChange, required int index, required BitcoinAddressType addressType, + required BitcoinDerivationInfo derivationInfo, }) { - return generateAddress(isChange: isChange, index: index, addressType: addressType) - .toAddress(network); + return generateAddress( + isChange: isChange, + index: index, + addressType: addressType, + derivationInfo: derivationInfo, + ).toAddress(network); } Future getAddressAsync({ required bool isChange, required int index, required BitcoinAddressType addressType, + required BitcoinDerivationInfo derivationInfo, }) async => - getAddress(isChange: isChange, index: index, addressType: addressType); + getAddress( + isChange: isChange, + index: index, + addressType: addressType, + derivationInfo: derivationInfo, + ); @action void addBitcoinAddressTypes() { @@ -551,23 +568,41 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { required bool isChange, required int gap, required BitcoinAddressType type, + required BitcoinDerivationInfo derivationInfo, }) async { - print("_allAddresses: ${_allAddresses.length}"); - final newAddresses = await _createNewAddresses(gap, isChange: isChange, type: type); + final newAddresses = await _createNewAddresses( + gap, + isChange: isChange, + type: type, + derivationInfo: derivationInfo, + ); addAddresses(newAddresses); - print("_allAddresses: ${_allAddresses.length}"); return newAddresses; } @action Future generateInitialAddresses({required BitcoinAddressType type}) async { - await discoverAddresses(isChange: false, gap: defaultReceiveAddressesCount, type: type); - await discoverAddresses(isChange: true, gap: defaultChangeAddressesCount, type: type); + // TODO: try all other derivations + final derivationInfo = BitcoinAddressUtils.getDerivationFromType(type); + + await discoverAddresses( + isChange: false, + gap: defaultReceiveAddressesCount, + type: type, + derivationInfo: derivationInfo, + ); + await discoverAddresses( + isChange: true, + gap: defaultChangeAddressesCount, + type: type, + derivationInfo: derivationInfo, + ); } @action Future> _createNewAddresses( int count, { + required BitcoinDerivationInfo derivationInfo, bool isChange = false, BitcoinAddressType? type, }) async { @@ -580,11 +615,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { isChange: isChange, index: i, addressType: type ?? addressPageType, + derivationInfo: derivationInfo, ), index: i, isChange: isChange, type: type ?? addressPageType, network: network, + derivationInfo: derivationInfo, ); list.add(address); } @@ -618,32 +655,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { updateAddressesByMatch(); } - void _validateAddresses() { - _allAddresses.forEach((element) async { - if (element.type == SegwitAddresType.mweb) { - // this would add a ton of startup lag for mweb addresses since we have 1000 of them - return; - } - if (!element.isChange && - element.address != - await getAddressAsync( - isChange: false, - index: element.index, - addressType: element.type, - )) { - element.isChange = true; - } else if (element.isChange && - element.address != - await getAddressAsync( - isChange: true, - index: element.index, - addressType: element.type, - )) { - element.isChange = false; - } - }); - } - @action Future setAddressType(BitcoinAddressType type) async { _addressPageType = type; diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index f7c2e1a28..959618dcf 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -3,7 +3,6 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_core/encryption_file_utils.dart'; -import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -92,7 +91,7 @@ class ElectrumWalletSnapshot { final derivationType = DerivationType .values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index]; - final derivationPath = data['derivationPath'] as String? ?? electrum_path; + final derivationPath = data['derivationPath'] as String? ?? ELECTRUM_PATH; try { regularAddressIndexByType = { diff --git a/cw_bitcoin/lib/litecoin_hardware_wallet_service.dart b/cw_bitcoin/lib/litecoin_hardware_wallet_service.dart index c2f2aa22e..c53a8713d 100644 --- a/cw_bitcoin/lib/litecoin_hardware_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_hardware_wallet_service.dart @@ -28,7 +28,12 @@ class LitecoinHardwareWalletService { final bip32 = Bip32Slip10Secp256k1.fromExtendedKey(xpub, xpubVersion).childKey(Bip32KeyIndex(0)); - final address = P2wpkhAddress.fromBip32(bip32: bip32, isChange: false, index: 0); + final address = P2wpkhAddress.fromDerivation( + bip32: bip32, + derivationInfo: BitcoinDerivationInfos.LITECOIN, + isChange: false, + index: 0, + ); accounts.add(HardwareAccountData( address: address.toAddress(LitecoinNetwork.mainnet), diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 7c581ab4e..4ad64e0da 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -21,7 +21,6 @@ import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; -import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/pending_transaction.dart'; @@ -243,7 +242,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { walletInfo.derivationInfo ??= DerivationInfo(); // set the default if not present: - walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path; + walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? ELECTRUM_PATH; walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum; Uint8List? seedBytes = null; @@ -435,13 +434,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override - Future rescan({ - required int height, - int? chainTip, - ScanData? scanData, - bool? doSingleScan, - bool? usingElectrs, - }) async { + Future rescan({required int height}) async { _syncTimer?.cancel(); await walletInfo.updateRestoreHeight(height); diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 7ff87bfd5..72e19149b 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -103,6 +103,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with index: e.key, type: SegwitAddresType.mweb, network: network, + derivationInfo: BitcoinAddressUtils.getDerivationFromType(SegwitAddresType.p2wpkh), )) .toList(); addMwebAddresses(addressRecords); @@ -121,12 +122,18 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required bool isChange, required int index, required BitcoinAddressType addressType, + required BitcoinDerivationInfo derivationInfo, }) { if (addressType == SegwitAddresType.mweb) { return MwebAddress.fromAddress(address: mwebAddrs[0], network: network); } - return P2wpkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index); + return P2wpkhAddress.fromDerivation( + bip32: bip32, + derivationInfo: derivationInfo, + isChange: isChange, + index: index, + ); } } @@ -135,12 +142,18 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required bool isChange, required int index, required BitcoinAddressType addressType, + required BitcoinDerivationInfo derivationInfo, }) async { if (addressType == SegwitAddresType.mweb) { await ensureMwebAddressUpToIndexExists(index); } - return getAddress(isChange: isChange, index: index, addressType: addressType); + return getAddress( + isChange: isChange, + index: index, + addressType: addressType, + derivationInfo: derivationInfo, + ); } @action @@ -194,6 +207,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with index: 0, type: SegwitAddresType.mweb, network: network, + derivationInfo: BitcoinAddressUtils.getDerivationFromType(SegwitAddresType.p2wpkh), ); } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index ad03398e3..d02a50e3b 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -411,10 +411,10 @@ packages: dependency: transitive description: name: google_identity_services_web - sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6" + sha256: "0c56c2c5d60d6dfaf9725f5ad4699f04749fb196ee5a70487a46ef184837ccf6" url: "https://pub.dev" source: hosted - version: "0.3.1+4" + version: "0.3.0+2" googleapis_auth: dependency: transitive description: @@ -467,10 +467,10 @@ packages: dependency: "direct main" description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.0" http2: dependency: transitive description: @@ -845,10 +845,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2" + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: @@ -1041,18 +1041,18 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.4.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.3" xdg_directories: dependency: transitive description: diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart index 9c4dba89b..10a8a212f 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart @@ -153,6 +153,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { isChange: addr.isChange, type: P2pkhAddressType.p2pkh, network: BitcoinCashNetwork.mainnet, + derivationInfo: BitcoinAddressUtils.getDerivationFromType(P2pkhAddressType.p2pkh), ); } catch (_) { return BitcoinAddressRecord( @@ -161,6 +162,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { isChange: addr.isChange, type: P2pkhAddressType.p2pkh, network: BitcoinCashNetwork.mainnet, + derivationInfo: BitcoinAddressUtils.getDerivationFromType(P2pkhAddressType.p2pkh), ); } }).toList(), diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart index 704d1e843..34ba748fc 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart @@ -24,6 +24,12 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi required bool isChange, required int index, required BitcoinAddressType addressType, + required BitcoinDerivationInfo derivationInfo, }) => - P2pkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index); + P2pkhAddress.fromDerivation( + bip32: bip32, + derivationInfo: derivationInfo, + isChange: isChange, + index: index, + ); } diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index bd035e30a..53a3930b0 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -189,16 +189,13 @@ class WalletInfo extends HiveObject { @HiveField(22) String? parentAddress; - + @HiveField(23) List? hiddenAddresses; @HiveField(24) List? manualAddresses; - - - String get yatLastUsedAddress => yatLastUsedAddressRaw ?? ''; set yatLastUsedAddress(String address) {