From 1061b7de0103e23cb0478ea5699f89c785d9289d Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 9 Sep 2024 20:59:52 -0700 Subject: [PATCH 1/2] [skip ci] wip --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 9 ++- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 71 ++++++++++++++++--- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index f320ef110..e40f6fd89 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -335,6 +335,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { }) => ''; + Future getAddressAsync({ + required int index, + required Bip32Slip10Secp256k1 hd, + BitcoinAddressType? addressType, + }) async => + getAddress(index: index, hd: hd, addressType: addressType); + void addBitcoinAddressTypes() { final lastP2wpkh = _addresses .where((addressRecord) => @@ -569,7 +576,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { for (var i = startIndex; i < count + startIndex; i++) { final address = BitcoinAddressRecord( - getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), + await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), index: i, isHidden: isHidden, type: type ?? addressPageType, diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 26a18df00..1e6b1a389 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:typed_data'; import 'package:bech32/bech32.dart'; @@ -30,29 +31,63 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialRegularAddressIndex, super.initialChangeAddressIndex, }) : super(walletInfo) { - topUpMweb(0); + initMwebAddresses(); + // topUpMweb(0); + print("initialized LitecoinWalletAddressesBase"); } final Bip32Slip10Secp256k1 mwebHd; bool mwebEnabled; + int mwebTopUpIndex = 1000; + List mwebAddrs = []; + static Timer? mwebTopUpTimer; List get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; List get spendPubkey => mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; - List mwebAddrs = []; + // Future topUpMweb(int index) async { + // // generate up to index + 1000 addresses: + // while (mwebAddrs.length - index < 1000) { + // final length = mwebAddrs.length; + // final address = await CwMweb.address( + // Uint8List.fromList(scanSecret), + // Uint8List.fromList(spendPubkey), + // length, + // ); + // mwebAddrs.add(address!); + // } + // } - Future topUpMweb(int index) async { - // generate up to index + 1000 addresses: - while (mwebAddrs.length - index < 1000) { - final length = mwebAddrs.length; + Future initMwebAddresses() async { + print("mweb addresses: ${mwebAddrs.length}"); + const int INITIAL_MWEB_ADDRESSES = 25; + // make sure we have at least 20 addresses: + while (mwebAddrs.length < INITIAL_MWEB_ADDRESSES) { final address = await CwMweb.address( Uint8List.fromList(scanSecret), Uint8List.fromList(spendPubkey), - length, + mwebAddrs.length, ); mwebAddrs.add(address!); } + + // set up a periodic task to fill up the mweb addresses to 1000: + mwebTopUpTimer?.cancel(); + mwebTopUpTimer = Timer.periodic(Duration(seconds: 5), (timer) async { + if (mwebAddrs.length >= mwebTopUpIndex) { + return; + } + for (int i = 0; i < 10; i++) { + final address = await CwMweb.address( + Uint8List.fromList(scanSecret), + Uint8List.fromList(spendPubkey), + mwebAddrs.length, + ); + mwebAddrs.add(address!); + } + print("mweb addresses: ${mwebAddrs.length}"); + }); } @override @@ -62,13 +97,28 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with BitcoinAddressType? addressType, }) { if (addressType == SegwitAddresType.mweb) { - topUpMweb(index).then((value) { - return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; - }); + return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; } return generateP2WPKHAddress(hd: hd, index: index, network: network); } + @override + Future getAddressAsync({ + required int index, + required Bip32Slip10Secp256k1 hd, + BitcoinAddressType? addressType, + }) async { + if (addressType == SegwitAddresType.mweb) { + if (index + 1000 > mwebTopUpIndex) { + mwebTopUpIndex = index + 1000; + } + while (mwebAddrs.length <= index) { + await Future.delayed(const Duration(seconds: 1)); + } + } + return getAddress(index: index, hd: hd, addressType: addressType); + } + @action @override Future getChangeAddress({List? outputs, UtxoDetails? utxoDetails}) async { @@ -99,7 +149,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } if (mwebEnabled) { - await topUpMweb(0); return mwebAddrs[0]; } From 150d065dbb31a34062d6339e7a768cea02f57f4c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 10 Sep 2024 11:41:47 -0700 Subject: [PATCH 2/2] [skip ci] testing --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 11 +-- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 71 +++++++------------ cw_bitcoin/lib/litecoin_wallet_service.dart | 4 +- cw_core/lib/get_height_by_date.dart | 22 +----- cw_mweb/lib/cw_mweb.dart | 5 -- lib/bitcoin/cw_bitcoin.dart | 4 +- lib/core/wallet_loading_service.dart | 3 +- lib/di.dart | 2 +- lib/src/screens/rescan/rescan_page.dart | 2 +- .../widgets/unspent_coins_list_item.dart | 16 ++--- .../dashboard/dashboard_view_model.dart | 6 +- 11 files changed, 52 insertions(+), 94 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index e40f6fd89..0653200ee 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -223,8 +223,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { if (walletInfo.type == WalletType.bitcoinCash) { await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); } else if (walletInfo.type == WalletType.litecoin) { - await _generateInitialAddresses(); - await _generateInitialAddresses(type: SegwitAddresType.mweb); + // await _generateInitialAddresses(); + // await _generateInitialAddresses(type: SegwitAddresType.mweb); } else if (walletInfo.type == WalletType.bitcoin) { await _generateInitialAddresses(); await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); @@ -232,6 +232,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { await _generateInitialAddresses(type: SegwitAddresType.p2tr); await _generateInitialAddresses(type: SegwitAddresType.p2wsh); } + updateAddressesByMatch(); updateReceiveAddresses(); updateChangeAddresses(); @@ -607,14 +608,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } void _validateAddresses() { - _addresses.forEach((element) { + _addresses.forEach((element) async { if (!element.isHidden && element.address != - getAddress(index: element.index, hd: mainHd, addressType: element.type)) { + await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) { element.isHidden = true; } else if (element.isHidden && element.address != - getAddress(index: element.index, hd: sideHd, addressType: element.type)) { + await getAddressAsync(index: element.index, hd: sideHd, addressType: element.type)) { element.isHidden = false; } }); diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 1e6b1a389..0e7434029 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -9,6 +9,7 @@ import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_mweb/cw_mweb.dart'; +import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; part 'litecoin_wallet_addresses.g.dart'; @@ -31,9 +32,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialRegularAddressIndex, super.initialChangeAddressIndex, }) : super(walletInfo) { + // start generating mweb addresses in the background: initMwebAddresses(); - // topUpMweb(0); - print("initialized LitecoinWalletAddressesBase"); } final Bip32Slip10Secp256k1 mwebHd; @@ -46,48 +46,30 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with List get spendPubkey => mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; - // Future topUpMweb(int index) async { - // // generate up to index + 1000 addresses: - // while (mwebAddrs.length - index < 1000) { - // final length = mwebAddrs.length; - // final address = await CwMweb.address( - // Uint8List.fromList(scanSecret), - // Uint8List.fromList(spendPubkey), - // length, - // ); - // mwebAddrs.add(address!); - // } - // } - - Future initMwebAddresses() async { - print("mweb addresses: ${mwebAddrs.length}"); - const int INITIAL_MWEB_ADDRESSES = 25; - // make sure we have at least 20 addresses: - while (mwebAddrs.length < INITIAL_MWEB_ADDRESSES) { - final address = await CwMweb.address( - Uint8List.fromList(scanSecret), - Uint8List.fromList(spendPubkey), - mwebAddrs.length, - ); + Future ensureMwebAddressUpToIndexExists(int index) async { + Uint8List scan = Uint8List.fromList(scanSecret); + Uint8List spend = Uint8List.fromList(spendPubkey); + while (mwebAddrs.length <= (index + 1)) { + final address = await CwMweb.address(scan, spend, mwebAddrs.length); mwebAddrs.add(address!); } + } - // set up a periodic task to fill up the mweb addresses to 1000: - mwebTopUpTimer?.cancel(); - mwebTopUpTimer = Timer.periodic(Duration(seconds: 5), (timer) async { - if (mwebAddrs.length >= mwebTopUpIndex) { - return; - } - for (int i = 0; i < 10; i++) { - final address = await CwMweb.address( - Uint8List.fromList(scanSecret), - Uint8List.fromList(spendPubkey), - mwebAddrs.length, - ); - mwebAddrs.add(address!); - } - print("mweb addresses: ${mwebAddrs.length}"); - }); + Future generateNumAddresses(int num) async { + Uint8List scan = Uint8List.fromList(scanSecret); + Uint8List spend = Uint8List.fromList(spendPubkey); + for (int i = 0; i < num; i++) { + final address = await CwMweb.address(scan, spend, mwebAddrs.length); + mwebAddrs.add(address!); + await Future.delayed(Duration.zero); + } + } + + Future initMwebAddresses() async { + for (int i = 0; i < 4; i++) { + await generateNumAddresses(250); + await Future.delayed(const Duration(milliseconds: 1500)); + } } @override @@ -109,12 +91,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with BitcoinAddressType? addressType, }) async { if (addressType == SegwitAddresType.mweb) { - if (index + 1000 > mwebTopUpIndex) { - mwebTopUpIndex = index + 1000; - } - while (mwebAddrs.length <= index) { - await Future.delayed(const Duration(seconds: 1)); - } + await ensureMwebAddressUpToIndexExists(index); } return getAddress(index: index, hd: hd, addressType: addressType); } diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index 25438007a..7d976fa03 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -21,12 +21,12 @@ class LitecoinWalletService extends WalletService< BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials, BitcoinNewWalletCredentials> { - LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect, this.alwaysScan); + LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect); final Box walletInfoSource; final Box unspentCoinsInfoSource; - final bool isDirect; final bool alwaysScan; + final bool isDirect; @override WalletType getType() => WalletType.litecoin; diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 52136fdcd..2f0bbc51d 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -267,11 +267,6 @@ const bitcoinDates = { "2023-01": 769810, }; - -const Map litecoinDates = { - // TODO: add litecoin dates -}; - int getBitcoinHeightByDate({required DateTime date}) { String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; final closestKey = bitcoinDates.keys @@ -305,19 +300,9 @@ DateTime getDateByBitcoinHeight(int height) { return estimatedDate; } -int getLitecoinHeightByDate({required DateTime date}) { - String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; - final closestKey = litecoinDates.keys - .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => litecoinDates.keys.last); - final beginningBlock = litecoinDates[dateKey] ?? litecoinDates[closestKey]!; - - final startOfMonth = DateTime(date.year, date.month); - final daysDifference = date.difference(startOfMonth).inDays; - - // approximately 6 blocks per hour, 24 hours per day - int estimatedBlocksSinceStartOfMonth = (daysDifference * 24 * 6); - - return beginningBlock + estimatedBlocksSinceStartOfMonth; +int getLtcHeightByDate({required DateTime date}) { + // TODO: use the proxy layer to get the height with a binary search of blocked header heights + return 0; } // TODO: enhance all of this global const lists @@ -397,4 +382,3 @@ int getWowneroHeightByDate({required DateTime date}) { return wowDates[closestKey] ?? 0; } - diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index e71ddde97..ab7dfda10 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -55,11 +55,6 @@ class CwMweb { } static Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { - // try { - // return (await CwMwebPlatform.instance.address(scan, spendPub, index))!; - // } catch (e) { - // print("error generating address!: $e"); - // } return CwMwebPlatform.instance.address(scanSecret, spendPub, index); } diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 074398263..33d79b450 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -210,7 +210,7 @@ class CWBitcoin extends Bitcoin { WalletService createLitecoinWalletService(Box walletInfoSource, Box unspentCoinSource, bool alwaysScan, bool isDirect) { - return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect, alwaysScan); + return LitecoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect); } @override @@ -530,7 +530,7 @@ class CWBitcoin extends Bitcoin { int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); @override - int getLitecoinHeightByDate({required DateTime date}) => getLitecoinHeightByDate(date: date); + int getLitecoinHeightByDate({required DateTime date}) => getLtcHeightByDate(date: date); @override Future rescan(Object wallet, {required int height, bool? doSingleScan}) async { diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 0087b1332..e58e14652 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -85,7 +85,8 @@ class WalletLoadingService { authenticatedErrorStreamController.add(corruptedWalletsSeeds); return wallet; - } catch (_) { + } catch (e) { + print(e); // save seeds and show corrupted wallets' seeds to the user try { final seeds = await _getCorruptedWalletSeeds(walletInfo.name, walletInfo.type); diff --git a/lib/di.dart b/lib/di.dart index 30727f33c..9c3c5bcb2 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1000,8 +1000,8 @@ Future setup({ return bitcoin!.createLitecoinWalletService( _walletInfoSource, _unspentCoinsInfoSource, - SettingsStoreBase.walletPasswordDirectInput, getIt.get().mwebAlwaysScan, + SettingsStoreBase.walletPasswordDirectInput, ); case WalletType.ethereum: return ethereum!.createEthereumWalletService( diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index 2d4e86e4f..7dcd17f76 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -35,7 +35,7 @@ class RescanPage extends BasePage { isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, isMwebScan: _rescanViewModel.isMwebScan, doSingleScan: _rescanViewModel.doSingleScan, - hasDatePicker: !_rescanViewModel.isMwebScan, + hasDatePicker: !_rescanViewModel.isMwebScan,// disable date picker for mweb for now toggleSingleScan: () => _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, walletType: _rescanViewModel.wallet.type, diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart index 236d06f4e..42e701bb4 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -103,10 +103,10 @@ class UnspentCoinsListItem extends StatelessWidget { ), maxLines: 1, ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if (isChange) + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // children: [ + if (isChange || true) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -123,7 +123,7 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - if (address.toLowerCase().contains("mweb")) + if (address.toLowerCase().contains("mweb") || true) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -141,7 +141,7 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - if (isSilentPayment) + if (isSilentPayment || true) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -158,8 +158,8 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - ], - ), + // ], + // ), ], ), ), diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 6e8c2070e..7c2c4f549 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -414,8 +414,8 @@ abstract class DashboardViewModelBase with Store { @computed bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay; - @observable - bool mwebScanningActive = false; + @computed + bool get mwebScanningActive => settingsStore.mwebEnabled; @computed bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore; @@ -430,7 +430,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.hasEnabledMwebBefore = true; } - mwebScanningActive = active; + settingsStore.mwebEnabled = active; bitcoin!.setMwebEnabled(wallet, active); }