Merge branch 'mweb' of https://github.com/cake-tech/cake_wallet into mweb-bg-sync-2

This commit is contained in:
Matthew Fosse 2024-09-10 11:42:18 -07:00
commit 5be1284d58
11 changed files with 78 additions and 64 deletions

View file

@ -223,8 +223,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (walletInfo.type == WalletType.bitcoinCash) { if (walletInfo.type == WalletType.bitcoinCash) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
} else if (walletInfo.type == WalletType.litecoin) { } else if (walletInfo.type == WalletType.litecoin) {
await _generateInitialAddresses(); // await _generateInitialAddresses();
await _generateInitialAddresses(type: SegwitAddresType.mweb); // await _generateInitialAddresses(type: SegwitAddresType.mweb);
} else if (walletInfo.type == WalletType.bitcoin) { } else if (walletInfo.type == WalletType.bitcoin) {
await _generateInitialAddresses(); await _generateInitialAddresses();
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); 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.p2tr);
await _generateInitialAddresses(type: SegwitAddresType.p2wsh); await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
} }
updateAddressesByMatch(); updateAddressesByMatch();
updateReceiveAddresses(); updateReceiveAddresses();
updateChangeAddresses(); updateChangeAddresses();
@ -335,6 +336,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}) => }) =>
''; '';
Future<String> getAddressAsync({
required int index,
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) async =>
getAddress(index: index, hd: hd, addressType: addressType);
void addBitcoinAddressTypes() { void addBitcoinAddressTypes() {
final lastP2wpkh = _addresses final lastP2wpkh = _addresses
.where((addressRecord) => .where((addressRecord) =>
@ -569,7 +577,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
for (var i = startIndex; i < count + startIndex; i++) { for (var i = startIndex; i < count + startIndex; i++) {
final address = BitcoinAddressRecord( final address = BitcoinAddressRecord(
getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType),
index: i, index: i,
isHidden: isHidden, isHidden: isHidden,
type: type ?? addressPageType, type: type ?? addressPageType,
@ -600,14 +608,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
} }
void _validateAddresses() { void _validateAddresses() {
_addresses.forEach((element) { _addresses.forEach((element) async {
if (!element.isHidden && if (!element.isHidden &&
element.address != element.address !=
getAddress(index: element.index, hd: mainHd, addressType: element.type)) { await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) {
element.isHidden = true; element.isHidden = true;
} else if (element.isHidden && } else if (element.isHidden &&
element.address != element.address !=
getAddress(index: element.index, hd: sideHd, addressType: element.type)) { await getAddressAsync(index: element.index, hd: sideHd, addressType: element.type)) {
element.isHidden = false; element.isHidden = false;
} }
}); });

View file

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:bech32/bech32.dart'; import 'package:bech32/bech32.dart';
@ -8,6 +9,7 @@ import 'package:cw_bitcoin/utils.dart';
import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_mweb/cw_mweb.dart'; import 'package:cw_mweb/cw_mweb.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
part 'litecoin_wallet_addresses.g.dart'; part 'litecoin_wallet_addresses.g.dart';
@ -30,31 +32,46 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
super.initialRegularAddressIndex, super.initialRegularAddressIndex,
super.initialChangeAddressIndex, super.initialChangeAddressIndex,
}) : super(walletInfo) { }) : super(walletInfo) {
topUpMweb(0); // start generating mweb addresses in the background:
initMwebAddresses();
} }
final Bip32Slip10Secp256k1 mwebHd; final Bip32Slip10Secp256k1 mwebHd;
bool mwebEnabled; bool mwebEnabled;
int mwebTopUpIndex = 1000;
List<String> mwebAddrs = [];
static Timer? mwebTopUpTimer;
List<int> get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; List<int> get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
List<int> get spendPubkey => List<int> get spendPubkey =>
mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed;
List<String> mwebAddrs = []; Future<void> ensureMwebAddressUpToIndexExists(int index) async {
Uint8List scan = Uint8List.fromList(scanSecret);
Future<void> topUpMweb(int index) async { Uint8List spend = Uint8List.fromList(spendPubkey);
// generate up to index + 1000 addresses: while (mwebAddrs.length <= (index + 1)) {
while (mwebAddrs.length - index < 1000) { final address = await CwMweb.address(scan, spend, mwebAddrs.length);
final length = mwebAddrs.length;
final address = await CwMweb.address(
Uint8List.fromList(scanSecret),
Uint8List.fromList(spendPubkey),
length,
);
mwebAddrs.add(address!); mwebAddrs.add(address!);
} }
} }
Future<void> 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<void> initMwebAddresses() async {
for (int i = 0; i < 4; i++) {
await generateNumAddresses(250);
await Future.delayed(const Duration(milliseconds: 1500));
}
}
@override @override
String getAddress({ String getAddress({
required int index, required int index,
@ -62,13 +79,23 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
BitcoinAddressType? addressType, BitcoinAddressType? addressType,
}) { }) {
if (addressType == SegwitAddresType.mweb) { 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); return generateP2WPKHAddress(hd: hd, index: index, network: network);
} }
@override
Future<String> getAddressAsync({
required int index,
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) async {
if (addressType == SegwitAddresType.mweb) {
await ensureMwebAddressUpToIndexExists(index);
}
return getAddress(index: index, hd: hd, addressType: addressType);
}
@action @action
@override @override
Future<String> getChangeAddress({List<BitcoinOutput>? outputs, UtxoDetails? utxoDetails}) async { Future<String> getChangeAddress({List<BitcoinOutput>? outputs, UtxoDetails? utxoDetails}) async {
@ -99,7 +126,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
} }
if (mwebEnabled) { if (mwebEnabled) {
await topUpMweb(0);
return mwebAddrs[0]; return mwebAddrs[0];
} }

View file

@ -21,12 +21,12 @@ class LitecoinWalletService extends WalletService<
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials, BitcoinRestoreWalletFromWIFCredentials,
BitcoinNewWalletCredentials> { BitcoinNewWalletCredentials> {
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect, this.alwaysScan); LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect;
final bool alwaysScan; final bool alwaysScan;
final bool isDirect;
@override @override
WalletType getType() => WalletType.litecoin; WalletType getType() => WalletType.litecoin;

View file

@ -267,11 +267,6 @@ const bitcoinDates = {
"2023-01": 769810, "2023-01": 769810,
}; };
const Map<String, int> litecoinDates = {
// TODO: add litecoin dates
};
int getBitcoinHeightByDate({required DateTime date}) { int getBitcoinHeightByDate({required DateTime date}) {
String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}';
final closestKey = bitcoinDates.keys final closestKey = bitcoinDates.keys
@ -305,19 +300,9 @@ DateTime getDateByBitcoinHeight(int height) {
return estimatedDate; return estimatedDate;
} }
int getLitecoinHeightByDate({required DateTime date}) { int getLtcHeightByDate({required DateTime date}) {
String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; // TODO: use the proxy layer to get the height with a binary search of blocked header heights
final closestKey = litecoinDates.keys return 0;
.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 // TODO: enhance all of this global const lists
@ -397,4 +382,3 @@ int getWowneroHeightByDate({required DateTime date}) {
return wowDates[closestKey] ?? 0; return wowDates[closestKey] ?? 0;
} }

View file

@ -55,11 +55,6 @@ class CwMweb {
} }
static Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) async { static Future<String?> 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); return CwMwebPlatform.instance.address(scanSecret, spendPub, index);
} }

View file

@ -210,7 +210,7 @@ class CWBitcoin extends Bitcoin {
WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource,
Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) { Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) {
return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect, alwaysScan); return LitecoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect);
} }
@override @override
@ -530,7 +530,7 @@ class CWBitcoin extends Bitcoin {
int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date);
@override @override
int getLitecoinHeightByDate({required DateTime date}) => getLitecoinHeightByDate(date: date); int getLitecoinHeightByDate({required DateTime date}) => getLtcHeightByDate(date: date);
@override @override
Future<void> rescan(Object wallet, {required int height, bool? doSingleScan}) async { Future<void> rescan(Object wallet, {required int height, bool? doSingleScan}) async {

View file

@ -85,7 +85,8 @@ class WalletLoadingService {
authenticatedErrorStreamController.add(corruptedWalletsSeeds); authenticatedErrorStreamController.add(corruptedWalletsSeeds);
return wallet; return wallet;
} catch (_) { } catch (e) {
print(e);
// save seeds and show corrupted wallets' seeds to the user // save seeds and show corrupted wallets' seeds to the user
try { try {
final seeds = await _getCorruptedWalletSeeds(walletInfo.name, walletInfo.type); final seeds = await _getCorruptedWalletSeeds(walletInfo.name, walletInfo.type);

View file

@ -1000,8 +1000,8 @@ Future<void> setup({
return bitcoin!.createLitecoinWalletService( return bitcoin!.createLitecoinWalletService(
_walletInfoSource, _walletInfoSource,
_unspentCoinsInfoSource, _unspentCoinsInfoSource,
SettingsStoreBase.walletPasswordDirectInput,
getIt.get<SettingsStore>().mwebAlwaysScan, getIt.get<SettingsStore>().mwebAlwaysScan,
SettingsStoreBase.walletPasswordDirectInput,
); );
case WalletType.ethereum: case WalletType.ethereum:
return ethereum!.createEthereumWalletService( return ethereum!.createEthereumWalletService(

View file

@ -35,7 +35,7 @@ class RescanPage extends BasePage {
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
isMwebScan: _rescanViewModel.isMwebScan, isMwebScan: _rescanViewModel.isMwebScan,
doSingleScan: _rescanViewModel.doSingleScan, doSingleScan: _rescanViewModel.doSingleScan,
hasDatePicker: !_rescanViewModel.isMwebScan, hasDatePicker: !_rescanViewModel.isMwebScan,// disable date picker for mweb for now
toggleSingleScan: () => toggleSingleScan: () =>
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
walletType: _rescanViewModel.wallet.type, walletType: _rescanViewModel.wallet.type,

View file

@ -103,10 +103,10 @@ class UnspentCoinsListItem extends StatelessWidget {
), ),
maxLines: 1, maxLines: 1,
), ),
Row( // Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ // children: [
if (isChange) if (isChange || true)
Container( Container(
height: 17, height: 17,
padding: EdgeInsets.only(left: 6, right: 6), 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( Container(
height: 17, height: 17,
padding: EdgeInsets.only(left: 6, right: 6), padding: EdgeInsets.only(left: 6, right: 6),
@ -141,7 +141,7 @@ class UnspentCoinsListItem extends StatelessWidget {
), ),
), ),
), ),
if (isSilentPayment) if (isSilentPayment || true)
Container( Container(
height: 17, height: 17,
padding: EdgeInsets.only(left: 6, right: 6), padding: EdgeInsets.only(left: 6, right: 6),
@ -158,8 +158,8 @@ class UnspentCoinsListItem extends StatelessWidget {
), ),
), ),
), ),
], // ],
), // ),
], ],
), ),
), ),

View file

@ -414,8 +414,8 @@ abstract class DashboardViewModelBase with Store {
@computed @computed
bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay; bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay;
@observable @computed
bool mwebScanningActive = false; bool get mwebScanningActive => settingsStore.mwebEnabled;
@computed @computed
bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore; bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore;
@ -430,7 +430,7 @@ abstract class DashboardViewModelBase with Store {
settingsStore.hasEnabledMwebBefore = true; settingsStore.hasEnabledMwebBefore = true;
} }
mwebScanningActive = active; settingsStore.mwebEnabled = active;
bitcoin!.setMwebEnabled(wallet, active); bitcoin!.setMwebEnabled(wallet, active);
} }