From 56c505da5927794d60b8c4813fbd99f669cbc11b Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 9 Sep 2024 12:46:53 -0700 Subject: [PATCH 1/2] review updates pt.1 --- .github/workflows/pr_test_build_android.yml | 11 +-- .github/workflows/pr_test_build_linux.yml | 11 +-- cw_bitcoin/lib/bitcoin_mnemonic.dart | 3 +- cw_bitcoin/lib/electrum_wallet.dart | 7 +- cw_bitcoin/lib/electrum_wallet_addresses.dart | 87 ++++++++++++------- cw_bitcoin/lib/litecoin_wallet_service.dart | 8 +- cw_core/lib/get_height_by_date.dart | 20 +++++ lib/bitcoin/cw_bitcoin.dart | 3 + lib/src/widgets/blockchain_height_widget.dart | 3 +- scripts/android/build_all.sh | 3 +- scripts/android/build_mwebd.sh | 17 ++-- scripts/ios/build_all.sh | 2 +- scripts/ios/build_mwebd.sh | 13 +-- tool/configure.dart | 1 + 14 files changed, 115 insertions(+), 74 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 2253cc551..6daeb84ee 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -110,15 +110,8 @@ jobs: export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:~/go/bin # build mwebd: - cd /opt/android/cake_wallet - git clone https://github.com/ltcmweb/mwebd - cd /opt/android/cake_wallet/mwebd - git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 - gomobile bind -target=android -androidapi 21 . - mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ - mv ./mwebd.aar $_ - cd .. - rm -rf mwebd + cd /opt/android/cake_wallet/scripts/android/ + ./build_mwebd.sh - name: Generate KeyStore run: | diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 50acdce94..116dc3c95 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -105,15 +105,8 @@ jobs: export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:~/go/bin # build mwebd: - cd /opt/android/cake_wallet - git clone https://github.com/ltcmweb/mwebd - cd /opt/android/cake_wallet/mwebd - git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 - gomobile bind -target=android -androidapi 21 . - mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ - mv ./mwebd.aar $_ - cd .. - rm -rf mwebd + cd /opt/android/cake_wallet/scripts/android/ + ./build_mwebd.sh - name: Generate localization run: | diff --git a/cw_bitcoin/lib/bitcoin_mnemonic.dart b/cw_bitcoin/lib/bitcoin_mnemonic.dart index 0749627e9..21ff3891e 100644 --- a/cw_bitcoin/lib/bitcoin_mnemonic.dart +++ b/cw_bitcoin/lib/bitcoin_mnemonic.dart @@ -8,6 +8,7 @@ import 'package:cw_core/sec_random_native.dart'; import 'package:cw_core/utils/text_normalizer.dart'; const segwit = '100'; +const mweb = 'eb'; final wordlist = englishWordlist; double logBase(num x, num base) => log(x) / log(base); @@ -125,7 +126,7 @@ Future mnemonicToSeedBytes(String mnemonic, return Uint8List.fromList(bytes); } -bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit]).any((el) => el); +bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit, mweb]).any((el) => el); bool validateMnemonic(String mnemonic, {String prefix = segwit}) { try { diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index ea99c6cd8..222a16e91 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1689,12 +1689,15 @@ abstract class ElectrumWalletBase final Map historiesWithDetails = {}; if (type == WalletType.bitcoin) { - await Future.wait(ADDRESS_TYPES + await Future.wait(BITCOIN_ADDRESS_TYPES .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); } else if (type == WalletType.bitcoinCash) { + await Future.wait(BITCOIN_CASH_ADDRESS_TYPES + .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); await fetchTransactionsForAddressType(historiesWithDetails, P2pkhAddressType.p2pkh); } else if (type == WalletType.litecoin) { - await fetchTransactionsForAddressType(historiesWithDetails, SegwitAddresType.p2wpkh); + await Future.wait(LITECOIN_ADDRESS_TYPES + .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); } transactionHistory.transactions.values.forEach((tx) async { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index e442a03e8..f320ef110 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -11,15 +11,23 @@ part 'electrum_wallet_addresses.g.dart'; class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses; -const List ADDRESS_TYPES = [ +const List BITCOIN_ADDRESS_TYPES = [ SegwitAddresType.p2wpkh, P2pkhAddressType.p2pkh, SegwitAddresType.p2tr, SegwitAddresType.p2wsh, - SegwitAddresType.mweb, P2shAddressType.p2wpkhInP2sh, ]; +const List LITECOIN_ADDRESS_TYPES = [ + SegwitAddresType.p2wpkh, + SegwitAddresType.mweb, +]; + +const List BITCOIN_CASH_ADDRESS_TYPES = [ + P2pkhAddressType.p2pkh, +]; + abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { ElectrumWalletAddressesBase( WalletInfo walletInfo, { @@ -327,13 +335,6 @@ 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) => @@ -393,6 +394,37 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { }); } + void addLitecoinAddressTypes() { + final lastP2wpkh = _addresses + .where((addressRecord) => + _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) + .toList() + .last; + if (lastP2wpkh.address != address) { + addressesMap[lastP2wpkh.address] = 'P2WPKH'; + } else { + addressesMap[address] = 'Active - P2WPKH'; + } + + final lastMweb = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); + if (lastMweb.address != address) { + addressesMap[lastMweb.address] = 'MWEB'; + } else { + addressesMap[address] = 'Active - MWEB'; + } + } + + void addBitcoinCashAddressTypes() { + final lastP2pkh = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh)); + if (lastP2pkh.address != address) { + addressesMap[lastP2pkh.address] = 'P2PKH'; + } else { + addressesMap[address] = 'Active - P2PKH'; + } + } + @override Future updateAddressesInBox() async { try { @@ -404,29 +436,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { allAddressesMap[addressRecord.address] = addressRecord.name; }); - if (walletInfo.type == WalletType.bitcoin) { - addBitcoinAddressTypes(); - } - - if (walletInfo.type == WalletType.litecoin) { - final lastP2wpkh = _addresses - .where((addressRecord) => - _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) - .toList() - .last; - if (lastP2wpkh.address != address) { - addressesMap[lastP2wpkh.address] = 'P2WPKH'; - } else { - addressesMap[address] = 'Active - P2WPKH'; - } - - final lastMweb = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); - if (lastMweb.address != address) { - addressesMap[lastMweb.address] = 'MWEB'; - } else { - addressesMap[address] = 'Active - MWEB'; - } + switch (walletInfo.type) { + case WalletType.bitcoin: + addBitcoinAddressTypes(); + break; + case WalletType.litecoin: + addLitecoinAddressTypes(); + break; + case WalletType.bitcoinCash: + addBitcoinCashAddressTypes(); + break; + default: + break; } await saveAddressesInBox(); @@ -548,7 +569,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { for (var i = startIndex; i < count + startIndex; i++) { final address = BitcoinAddressRecord( - await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), + getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), index: i, isHidden: isHidden, type: type ?? addressPageType, diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index da3602024..25438007a 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart'; +import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.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/litecoin_wallet.dart'; import 'package:cw_core/wallet_service.dart'; @@ -150,9 +150,9 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { - // if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { - // throw LitecoinMnemonicIsIncorrectException(); - // } + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { + throw LitecoinMnemonicIsIncorrectException(); + } final wallet = await LitecoinWalletBase.create( password: credentials.password!, diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 204f03d62..52136fdcd 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -267,6 +267,11 @@ 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 @@ -300,6 +305,21 @@ 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; +} + // TODO: enhance all of this global const lists const wowDates = { "2023-12": 583048, diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index e6d189356..0c3e27930 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -529,6 +529,9 @@ class CWBitcoin extends Bitcoin { @override int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); + @override + int getLitecoinHeightByDate({required DateTime date}) => getLitecoinHeightByDate(date: date); + @override Future rescan(Object wallet, {required int height, bool? doSingleScan}) async { final bitcoinWallet = wallet as ElectrumWallet; diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index b9ab45e55..f3bcbc25c 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -168,8 +168,7 @@ class BlockchainHeightState extends State { if (date != null) { int height; if (widget.isMwebScan) { - throw UnimplementedError(); - // height = bitcoin!.getMwebHeightByDate(date: date); + height = bitcoin!.getLitecoinHeightByDate(date: date); } else if (widget.isSilentPaymentsScan) { height = bitcoin!.getHeightByDate(date: date); } else { diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index ec70f02a6..ad4ec984b 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -10,6 +10,7 @@ DIR=$(dirname "$0") case $APP_ANDROID_TYPE in "monero.com") $DIR/build_monero_all.sh ;; "cakewallet") $DIR/build_monero_all.sh - $DIR/build_haven_all.sh ;; + $DIR/build_haven_all.sh + $DIR/build_mwebd.sh ;; "haven") $DIR/build_haven_all.sh ;; esac diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index bbe436ff6..1e4f51be9 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -1,10 +1,13 @@ -# install go > 1.23: -wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz -sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz -export PATH=$PATH:/usr/local/go/bin -export PATH=$PATH:~/go/bin -go install golang.org/x/mobile/cmd/gomobile@latest -gomobile init +if [[ "$1" == "--install" ]]; then + # install go > 1.23: + wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz + export PATH=$PATH:/usr/local/go/bin + export PATH=$PATH:~/go/bin + go install golang.org/x/mobile/cmd/gomobile@latest + gomobile init +fi + # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd diff --git a/scripts/ios/build_all.sh b/scripts/ios/build_all.sh index 565679e2d..ba5c55a1f 100755 --- a/scripts/ios/build_all.sh +++ b/scripts/ios/build_all.sh @@ -9,6 +9,6 @@ DIR=$(dirname "$0") case $APP_IOS_TYPE in "monero.com") $DIR/build_monero_all.sh ;; - "cakewallet") $DIR/build_monero_all.sh && $DIR/build_haven.sh ;; + "cakewallet") $DIR/build_monero_all.sh && $DIR/build_haven.sh && $DIR/build_mwebd.sh ;; "haven") $DIR/build_haven_all.sh ;; esac diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index ee1658800..b0d67115e 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -1,9 +1,12 @@ #!/bin/bash -# install go > 1.23: -brew install go -export PATH=$PATH:~/go/bin -go install golang.org/x/mobile/cmd/gomobile@latest -gomobile init +if [[ "$1" == "--install" ]]; then + # install go > 1.23: + brew install go + export PATH=$PATH:~/go/bin + go install golang.org/x/mobile/cmd/gomobile@latest + gomobile init +fi + # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd diff --git a/tool/configure.dart b/tool/configure.dart index 326a794ba..8ac6c7694 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -214,6 +214,7 @@ abstract class Bitcoin { {int? outputsCount, int? size}); int feeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); int getHeightByDate({required DateTime date}); + int getLitecoinHeightByDate({required DateTime date}); Future rescan(Object wallet, {required int height, bool? doSingleScan}); Future getNodeIsElectrsSPEnabled(Object wallet); void deleteSilentPaymentAddress(Object wallet, String address); From 0f5ee476f18381e407c642589f52eb95b26bc1cc Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 9 Sep 2024 12:49:22 -0700 Subject: [PATCH 2/2] Update cw_bitcoin/lib/litecoin_wallet.dart Co-authored-by: Omar Hatem --- cw_bitcoin/lib/litecoin_wallet.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index b69ba59dd..e3f68888e 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -317,11 +317,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final oldBoxName = "${walletInfo.name.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; final newBoxName = "${newWalletName.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; - final oldBox = await Hive.openBox(oldBoxName); + final oldBox = await CakeHive.openBox(oldBoxName); mwebUtxosBox = await CakeHive.openBox(newBoxName); for (final key in oldBox.keys) { await mwebUtxosBox.put(key, oldBox.get(key)!); } + oldBox.deleteFromDisk(); await super.renameWalletFiles(newWalletName); }