From 2cc115e5574fbb8e6b9270d691c1050fc5e4bfe2 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Fri, 26 Apr 2024 01:30:59 +0300 Subject: [PATCH] Fix issues with Creating Electrum and Restoring Bip39 --- cw_bitcoin/lib/bitcoin_derivations.dart | 8 +-- cw_bitcoin/lib/bitcoin_mnemonic.dart | 10 +-- cw_bitcoin/lib/bitcoin_wallet.dart | 6 +- .../bitcoin_wallet_creation_credentials.dart | 4 -- cw_bitcoin/lib/bitcoin_wallet_service.dart | 14 +---- cw_bitcoin/lib/electrum_wallet.dart | 5 +- cw_bitcoin/lib/electrum_wallet_snapshot.dart | 5 +- cw_core/lib/wallet_credentials.dart | 3 +- cw_core/lib/wallet_info.dart | 8 +-- lib/bitcoin/cw_bitcoin.dart | 62 +++++++++++++++---- lib/entities/default_settings_migration.dart | 2 +- .../wallet_restore_choose_derivation.dart | 2 +- .../screens/restore/wallet_restore_page.dart | 2 +- .../restore/restore_from_qr_vm.dart | 4 +- lib/view_model/wallet_creation_vm.dart | 13 ++-- lib/view_model/wallet_restore_view_model.dart | 6 +- 16 files changed, 92 insertions(+), 62 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_derivations.dart b/cw_bitcoin/lib/bitcoin_derivations.dart index cd7ce5ec2..82444c8e3 100644 --- a/cw_bitcoin/lib/bitcoin_derivations.dart +++ b/cw_bitcoin/lib/bitcoin_derivations.dart @@ -1,11 +1,11 @@ import 'package:cw_core/wallet_info.dart'; Map> bitcoin_derivations = { - DerivationType.electrum2: [ + DerivationType.electrum: [ DerivationInfo( - derivationType: DerivationType.bip39, - derivationPath: "m/0'/1", - description: "Electrum 2", + derivationType: DerivationType.electrum, + derivationPath: "m/0'/0", + description: "Electrum", script_type: "p2wpkh", ), ], diff --git a/cw_bitcoin/lib/bitcoin_mnemonic.dart b/cw_bitcoin/lib/bitcoin_mnemonic.dart index 48a0bc875..c9833e989 100644 --- a/cw_bitcoin/lib/bitcoin_mnemonic.dart +++ b/cw_bitcoin/lib/bitcoin_mnemonic.dart @@ -65,8 +65,7 @@ String bufferToBin(Uint8List data) { return q2; } -String encode(Uint8List originalData) { - final data = Uint8List.fromList(originalData); // Create a modifiable copy +String encode(Uint8List data) { final dataBitLen = data.length * 8; final wordBitLen = logBase(wordlist.length, 2).ceil(); final wordCount = (dataBitLen / wordBitLen).floor(); @@ -98,9 +97,10 @@ Future generateElectrumMnemonic({int strength = 264, String prefix = seg var result = ''; do { - final originalBytes = await secRandom(byteCount); - // create a modifiable copy, however I'm not sure why this is necessary - final bytes = Uint8List.fromList(originalBytes); + // final originalBytes = await secRandom(byteCount); + // // create a modifiable copy, however I'm not sure why this is necessary + // final bytes = Uint8List.fromList(originalBytes); + final bytes = await secRandom(byteCount); maskBytes(bytes, strength); result = encode(bytes); } while (!prefixMatches(result, [prefix]).first); diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 092e9d663..680226536 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -89,7 +89,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { case DerivationType.bip39: seedBytes = await bip39.mnemonicToSeed(mnemonic); break; - case DerivationType.electrum2: + case DerivationType.electrum: default: seedBytes = await mnemonicToSeedBytes(mnemonic); break; @@ -121,7 +121,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network); walletInfo.derivationInfo ??= DerivationInfo( - derivationType: snp.derivationType ?? DerivationType.electrum2, + derivationType: snp.derivationType ?? DerivationType.electrum, derivationPath: snp.derivationPath, ); @@ -131,7 +131,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { late Uint8List seedBytes; switch (walletInfo.derivationInfo!.derivationType) { - case DerivationType.electrum2: + case DerivationType.electrum: seedBytes = await mnemonicToSeedBytes(snp.mnemonic); break; case DerivationType.bip39: diff --git a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart index ca8cda153..af255f97c 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart @@ -10,10 +10,6 @@ class BitcoinNewWalletCredentials extends WalletCredentials { : super( name: name, walletInfo: walletInfo, - derivationInfo: DerivationInfo( - derivationType: derivationType, - derivationPath: derivationPath, - ), ); } diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index 550254b74..032a03d75 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -1,17 +1,11 @@ import 'dart:io'; -import 'dart:typed_data'; -import 'package:cw_bitcoin/address_to_output_script.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; -import 'package:cw_bitcoin/electrum.dart'; -import 'package:cw_bitcoin/script_hash.dart'; -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_bitcoin/bitcoin_wallet.dart'; import 'package:cw_core/pathForWallet.dart'; @@ -19,10 +13,6 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:hive/hive.dart'; import 'package:collection/collection.dart'; -import 'package:mobx/mobx.dart'; -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:cw_bitcoin/bitcoin_derivations.dart'; -import 'package:bip32/bip32.dart' as bip32; import 'package:bip39/bip39.dart' as bip39; class BitcoinWalletService extends WalletService restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { - if (!validateMnemonic(credentials.mnemonic)) { + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { throw BitcoinMnemonicIsIncorrectException(); } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 5bed6a449..73bf25452 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -61,7 +61,8 @@ abstract class ElectrumWalletBase CryptoCurrency? currency}) : hd = currency == CryptoCurrency.bch ? bitcoinCashHDWallet(seedBytes) - : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/0"), + : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType) + .derivePath(walletInfo.derivationInfo?.derivationPath ?? "m/0'/0"), syncStatus = NotConnectedSyncStatus(), _password = password, _feeRates = [], @@ -592,6 +593,8 @@ abstract class ElectrumWalletBase ? SegwitAddresType.p2wpkh.toString() : walletInfo.addressPageType.toString(), 'balance': balance[currency]?.toJSON(), + 'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index, + 'derivationPath': walletInfo.derivationInfo?.derivationPath, }); int feeRate(TransactionPriority priority) { diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 5f1b90e6d..e8e8c6777 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -51,8 +51,9 @@ class ElectrumWalletSnapshot { var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; - final derivationType = data['derivationType'] as DerivationType? ?? DerivationType.bip39; - final derivationPath = data['derivationPath'] as String? ?? "m/0'/1"; + final derivationType = + DerivationType.values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index]; + final derivationPath = data['derivationPath'] as String? ?? "m/0'/0"; try { regularAddressIndexByType = { diff --git a/cw_core/lib/wallet_credentials.dart b/cw_core/lib/wallet_credentials.dart index 79bafefb0..0ce2f80ab 100644 --- a/cw_core/lib/wallet_credentials.dart +++ b/cw_core/lib/wallet_credentials.dart @@ -7,7 +7,7 @@ abstract class WalletCredentials { this.seedPhraseLength, this.walletInfo, this.password, - DerivationInfo? derivationInfo, + this.derivationInfo, }) { if (this.walletInfo != null && derivationInfo != null) { this.walletInfo!.derivationInfo = derivationInfo; @@ -19,4 +19,5 @@ abstract class WalletCredentials { int? seedPhraseLength; String? password; WalletInfo? walletInfo; + DerivationInfo? derivationInfo; } diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 468cf7053..ce3b813dc 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -17,9 +17,7 @@ enum DerivationType { @HiveField(3) bip39, @HiveField(4) - electrum1, - @HiveField(5) - electrum2, + electrum, } @HiveType(typeId: DerivationInfo.typeId) class DerivationInfo extends HiveObject { @@ -28,7 +26,7 @@ class DerivationInfo extends HiveObject { this.derivationPath, this.balance = "", this.address = "", - this.height = 0, + this.transactionsCount = 0, this.script_type, this.description, }); @@ -37,7 +35,7 @@ class DerivationInfo extends HiveObject { String balance; String address; - int height; + int transactionsCount; DerivationType? derivationType; String? derivationPath; final String? script_type; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 2dcbb22ad..e0b660f2a 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -260,10 +260,10 @@ class CWBitcoin extends Bitcoin { Future> compareDerivationMethods( {required String mnemonic, required Node node}) async { if (await checkIfMnemonicIsElectrum2(mnemonic)) { - return [DerivationType.electrum2]; + return [DerivationType.electrum]; } - return [DerivationType.bip39, DerivationType.electrum2]; + return [DerivationType.bip39, DerivationType.electrum]; } int _countOccurrences(String str, String charToCount) { @@ -286,7 +286,7 @@ class CWBitcoin extends Bitcoin { for (DerivationType dType in bitcoin_derivations.keys) { late Uint8List seedBytes; - if (dType == DerivationType.electrum2) { + if (dType == DerivationType.electrum) { seedBytes = await mnemonicToSeedBytes(mnemonic); } else if (dType == DerivationType.bip39) { seedBytes = bip39.mnemonicToSeed(mnemonic); @@ -300,7 +300,7 @@ class CWBitcoin extends Bitcoin { description: dInfo.description, script_type: dInfo.script_type, ); - var node = bip32.BIP32.fromSeed(seedBytes); + var hd = bip32.BIP32.fromSeed(seedBytes); String derivationPath = dInfoCopy.derivationPath!; int derivationDepth = _countOccurrences(derivationPath, "/"); @@ -308,29 +308,67 @@ class CWBitcoin extends Bitcoin { derivationPath += "/0/0"; dInfoCopy.derivationPath = dInfoCopy.derivationPath! + "/0"; } - node = node.derivePath(derivationPath); + hd = hd.derivePath(derivationPath); + + // var hd = btc.HDWallet.fromSeed( + // seedBytes, + // network: node.type == WalletType.bitcoin ? btc.bitcoin : litecoinNetwork, + // ).derivePath(dInfoCopy.derivationPath!); + + // if (addressType == P2pkhAddressType.p2pkh) + // return generateP2PKHAddress(hd: hd, index: index, network: network); + // + // if (addressType == SegwitAddresType.p2tr) + // return generateP2TRAddress(hd: hd, index: index, network: network); + // + // if (addressType == SegwitAddresType.p2wsh) + // return generateP2WSHAddress(hd: hd, index: index, network: network); + // + // if (addressType == P2shAddressType.p2wpkhInP2sh) + // return generateP2SHAddress(hd: hd, index: index, network: network); String? address; switch (dInfoCopy.script_type) { case "p2wpkh": + // address = generateP2WPKHAddress( + // hd: hd, + // index: 0, + // network: node.type == WalletType.bitcoin + // ? BitcoinNetwork.mainnet + // : LitecoinNetwork.mainnet); address = btc .P2WPKH( - data: new btc.PaymentData(pubkey: node.publicKey), + data: new btc.PaymentData(pubkey: hd.publicKey), network: btc.bitcoin, ) .data .address; break; case "p2pkh": - default: + // address = generateP2PKHAddress( + // hd: hd, + // index: 0, + // network: node.type == WalletType.bitcoin + // ? BitcoinNetwork.mainnet + // : LitecoinNetwork.mainnet); address = btc .P2PKH( - data: new btc.PaymentData(pubkey: node.publicKey), - network: btc.bitcoin, - ) + data: new btc.PaymentData(pubkey: hd.publicKey), + network: btc.bitcoin, + ) .data .address; break; + // case "p2wpkh-p2sh": + // address = generateP2SHAddress( + // hd: hd, + // index: 0, + // network: node.type == WalletType.bitcoin + // ? BitcoinNetwork.mainnet + // : LitecoinNetwork.mainnet); + // break; + default: + continue; } final sh = scriptHash(address!, network: BitcoinNetwork.mainnet); @@ -339,7 +377,7 @@ class CWBitcoin extends Bitcoin { final balance = await electrumClient.getBalance(sh); dInfoCopy.balance = balance.entries.first.value.toString(); dInfoCopy.address = address; - dInfoCopy.height = history.length; + dInfoCopy.transactionsCount = history.length; list.add(dInfoCopy); } catch (e) { @@ -349,7 +387,7 @@ class CWBitcoin extends Bitcoin { } // sort the list such that derivations with the most transactions are first: - list.sort((a, b) => b.height.compareTo(a.height)); + list.sort((a, b) => b.transactionsCount.compareTo(a.transactionsCount)); return list; } diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 0ceba2d78..99178c815 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -767,7 +767,7 @@ Future updateBtcNanoWalletInfos(Box walletsInfoSource) async { derivationPath: walletInfo.derivationPath, derivationType: walletInfo.derivationType, address: walletInfo.address, - height: walletInfo.restoreHeight, + transactionsCount: walletInfo.restoreHeight, ); await walletInfo.save(); } diff --git a/lib/src/screens/restore/wallet_restore_choose_derivation.dart b/lib/src/screens/restore/wallet_restore_choose_derivation.dart index 4bb661f3c..9a7d8eb67 100644 --- a/lib/src/screens/restore/wallet_restore_choose_derivation.dart +++ b/lib/src/screens/restore/wallet_restore_choose_derivation.dart @@ -101,7 +101,7 @@ class WalletRestoreChooseDerivationPage extends BasePage { ), ), Text( - "${S.current.transactions}: ${derivation.height}", + "${S.current.transactions}: ${derivation.transactionsCount}", style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith( fontSize: 16, fontWeight: FontWeight.w500, diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index d0c704d44..cd883d096 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -360,7 +360,7 @@ class WalletRestorePage extends BasePage { int derivationsWithHistory = 0; int derivationWithHistoryIndex = 0; for (int i = 0; i < derivations.length; i++) { - if (derivations[i].height > 0) { + if (derivations[i].transactionsCount > 0) { derivationsWithHistory++; derivationWithHistoryIndex = i; } diff --git a/lib/view_model/restore/restore_from_qr_vm.dart b/lib/view_model/restore/restore_from_qr_vm.dart index 57d44570c..dda79662d 100644 --- a/lib/view_model/restore/restore_from_qr_vm.dart +++ b/lib/view_model/restore/restore_from_qr_vm.dart @@ -101,7 +101,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password, - derivationType: derivationInfo.derivationType!, + derivationType: derivationInfo!.derivationType!, derivationPath: derivationInfo.derivationPath!, ); case WalletType.bitcoinCash: @@ -115,7 +115,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password, - derivationType: derivationInfo.derivationType!); + derivationType: derivationInfo!.derivationType!); case WalletType.polygon: return polygon!.createPolygonRestoreWalletFromSeedCredentials( name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index ba102f725..a7baf4ffd 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -71,9 +71,9 @@ abstract class WalletCreationVMBase with Store { dirPath: dirPath, address: '', showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven, - derivationInfo: getDefaultDerivation(), + derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(), ); - + credentials.walletInfo = walletInfo; final wallet = restoreWallet != null ? await processFromRestoredWallet(credentials, restoreWallet) @@ -89,7 +89,7 @@ abstract class WalletCreationVMBase with Store { } } - DerivationInfo getDefaultDerivation() { + DerivationInfo? getDefaultDerivation() { switch (this.type) { case WalletType.nano: return DerivationInfo( @@ -97,11 +97,12 @@ abstract class WalletCreationVMBase with Store { ); case WalletType.bitcoin: case WalletType.litecoin: - default: return DerivationInfo( - derivationType: DerivationType.electrum2, - derivationPath: "m/0'/1", + derivationType: DerivationType.electrum, + derivationPath: "m/0'/0", ); + default: + return null; } } diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index ac6b75906..d6d439008 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -204,6 +204,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { switch (walletType) { case WalletType.bitcoin: + case WalletType.litecoin: String? mnemonic = credentials['seed'] as String?; return bitcoin!.getDerivationsFromMnemonic(mnemonic: mnemonic!, node: node); case WalletType.nano: @@ -226,7 +227,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { derivationType: DerivationType.nano, balance: nanoUtil!.getRawAsUsableString(standardInfo!.balance, nanoUtil!.rawPerNano), address: standardInfo.address!, - height: standardInfo.confirmationHeight, + transactionsCount: standardInfo.confirmationHeight, )); } @@ -235,7 +236,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { derivationType: DerivationType.bip39, balance: nanoUtil!.getRawAsUsableString(bip39Info!.balance, nanoUtil!.rawPerNano), address: bip39Info.address!, - height: bip39Info.confirmationHeight, + transactionsCount: bip39Info.confirmationHeight, )); } break; @@ -254,6 +255,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { switch (type) { case WalletType.bitcoin: + case WalletType.litecoin: return bitcoin!.compareDerivationMethods(mnemonic: mnemonic!, node: node); case WalletType.nano: return nanoUtil!.compareDerivationMethods(