Merge branch 'mweb_enhancements_4' of https://github.com/cake-tech/cake_wallet into mweb_enhancements_4

This commit is contained in:
fossephate 2024-10-28 10:55:29 -07:00
commit 8dc1ed6363
6 changed files with 124 additions and 84 deletions

View file

@ -52,10 +52,9 @@ part 'electrum_wallet.g.dart';
class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet;
abstract class ElectrumWalletBase extends WalletBase<
ElectrumBalance,
ElectrumTransactionHistory,
ElectrumTransactionInfo> with Store, WalletKeysFile {
abstract class ElectrumWalletBase
extends WalletBase<ElectrumBalance, ElectrumTransactionHistory, ElectrumTransactionInfo>
with Store, WalletKeysFile {
ElectrumWalletBase({
required String password,
required WalletInfo walletInfo,
@ -71,8 +70,8 @@ abstract class ElectrumWalletBase extends WalletBase<
ElectrumBalance? initialBalance,
CryptoCurrency? currency,
this.alwaysScan,
}) : accountHD = getAccountHDWallet(
currency, network, seedBytes, xpub, walletInfo.derivationInfo),
}) : accountHD =
getAccountHDWallet(currency, network, seedBytes, xpub, walletInfo.derivationInfo),
syncStatus = NotConnectedSyncStatus(),
_password = password,
_feeRates = <int>[],
@ -107,12 +106,8 @@ abstract class ElectrumWalletBase extends WalletBase<
sharedPrefs.complete(SharedPreferences.getInstance());
}
static Bip32Slip10Secp256k1 getAccountHDWallet(
CryptoCurrency? currency,
BasedUtxoNetwork network,
Uint8List? seedBytes,
String? xpub,
DerivationInfo? derivationInfo) {
static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network,
Uint8List? seedBytes, String? xpub, DerivationInfo? derivationInfo) {
if (seedBytes == null && xpub == null) {
throw Exception(
"To create a Wallet you need either a seed or an xpub. This should not happen");
@ -123,9 +118,8 @@ abstract class ElectrumWalletBase extends WalletBase<
case CryptoCurrency.btc:
case CryptoCurrency.ltc:
case CryptoCurrency.tbtc:
return Bip32Slip10Secp256k1.fromSeed(seedBytes, getKeyNetVersion(network))
.derivePath(_hardenedDerivationPath(
derivationInfo?.derivationPath ?? electrum_path))
return Bip32Slip10Secp256k1.fromSeed(seedBytes, getKeyNetVersion(network)).derivePath(
_hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path))
as Bip32Slip10Secp256k1;
case CryptoCurrency.bch:
return bitcoinCashHDWallet(seedBytes);
@ -134,13 +128,11 @@ abstract class ElectrumWalletBase extends WalletBase<
}
}
return Bip32Slip10Secp256k1.fromExtendedKey(
xpub!, getKeyNetVersion(network));
return Bip32Slip10Secp256k1.fromExtendedKey(xpub!, getKeyNetVersion(network));
}
static Bip32Slip10Secp256k1 bitcoinCashHDWallet(Uint8List seedBytes) =>
Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'")
as Bip32Slip10Secp256k1;
Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'") as Bip32Slip10Secp256k1;
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
inputsCount * 68 + outputsCounts * 34 + 10;
@ -357,7 +349,7 @@ abstract class ElectrumWalletBase extends WalletBase<
isSingleScan: doSingleScan ?? false,
));
_receiveStream?.cancel();
await _receiveStream?.cancel();
_receiveStream = receivePort.listen((var message) async {
if (message is Map<String, ElectrumTransactionInfo>) {
for (final map in message.entries) {
@ -577,6 +569,29 @@ abstract class ElectrumWalletBase extends WalletBase<
return node!.supportsSilentPayments!;
}
Future<bool> getNodeSupportsMweb() async {
final version = await electrumClient.version();
if (node == null) {
return false;
}
if (version.isNotEmpty) {
final server = version[0];
print("server: $server");
return false;
// if (server.toLowerCase().contains('electrs')) {
// node!.isElectrs = true;
// node!.save();
// return node!.isElectrs!;
// }
}
return node!.supportsMweb!;
}
@action
@override
Future<void> connectToNode({required Node node}) async {
@ -652,9 +667,8 @@ abstract class ElectrumWalletBase extends WalletBase<
ECPrivate? privkey;
bool? isSilentPayment = false;
final hd = utx.bitcoinAddressRecord.isHidden
? walletAddresses.sideHd
: walletAddresses.mainHd;
final hd =
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
@ -1234,8 +1248,7 @@ abstract class ElectrumWalletBase extends WalletBase<
}
}
void setLedgerConnection(ledger.LedgerConnection connection) =>
throw UnimplementedError();
void setLedgerConnection(ledger.LedgerConnection connection) => throw UnimplementedError();
Future<BtcTransaction> buildHardwareWalletTransaction({
required List<BitcoinBaseOutput> outputs,
@ -1594,9 +1607,7 @@ abstract class ElectrumWalletBase extends WalletBase<
final btcAddress = RegexUtils.addressTypeFromStr(addressRecord.address, network);
final privkey = generateECPrivate(
hd: addressRecord.isHidden
? walletAddresses.sideHd
: walletAddresses.mainHd,
hd: addressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
index: addressRecord.index,
network: network);
@ -1778,8 +1789,7 @@ abstract class ElectrumWalletBase extends WalletBase<
if (height != null) {
if (time == null && height > 0) {
time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000)
.round();
time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000).round();
}
if (confirmations == null) {
@ -2044,6 +2054,8 @@ abstract class ElectrumWalletBase extends WalletBase<
library: this.runtimeType.toString(),
));
}
}, onError: (e, s) {
print("sub_listen error: $e $s");
});
}));
}

View file

@ -331,7 +331,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
);
silentAddresses.add(address);
updateAddressesByMatch();
Future.delayed(Duration.zero, () => updateAddressesByMatch());
return address;
}
@ -348,7 +348,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
network: network,
);
_addresses.add(address);
updateAddressesByMatch();
Future.delayed(Duration.zero, () => updateAddressesByMatch());
return address;
}

View file

@ -9,6 +9,7 @@ import 'package:crypto/crypto.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/mweb_utxo.dart';
import 'package:cw_core/node.dart';
import 'package:cw_mweb/mwebd.pbgrpc.dart';
import 'package:fixnum/fixnum.dart';
import 'package:bip39/bip39.dart' as bip39;
@ -287,6 +288,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
await (walletAddresses as LitecoinWalletAddresses).ensureMwebAddressUpToIndexExists(1020);
}
@action
@override
Future<void> connectToNode({required Node node}) async {
await super.connectToNode(node: node);
if (await getNodeSupportsMweb()) {
await CwMweb.setNodeUriOverride(node.uri.toString());
}
}
@action
@override
Future<void> startSync() async {
@ -582,55 +592,60 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
if (responseStream == null) {
throw Exception("failed to get utxos stream!");
}
_utxoStream = responseStream.listen((Utxo sUtxo) async {
// we're processing utxos, so our balance could still be innacurate:
if (mwebSyncStatus is! SyncronizingSyncStatus && mwebSyncStatus is! SyncingSyncStatus) {
mwebSyncStatus = SyncronizingSyncStatus();
processingUtxos = true;
_processingTimer?.cancel();
_processingTimer = Timer.periodic(const Duration(seconds: 2), (timer) async {
processingUtxos = false;
timer.cancel();
});
}
final utxo = MwebUtxo(
address: sUtxo.address,
blockTime: sUtxo.blockTime,
height: sUtxo.height,
outputId: sUtxo.outputId,
value: sUtxo.value.toInt(),
);
if (mwebUtxosBox.containsKey(utxo.outputId)) {
// we've already stored this utxo, skip it:
// but do update the utxo height if it's somehow different:
final existingUtxo = mwebUtxosBox.get(utxo.outputId);
if (existingUtxo!.height != utxo.height) {
print(
"updating utxo height for $utxo.outputId: ${existingUtxo.height} -> ${utxo.height}");
existingUtxo.height = utxo.height;
await mwebUtxosBox.put(utxo.outputId, existingUtxo);
_utxoStream = responseStream.listen(
(Utxo sUtxo) async {
// we're processing utxos, so our balance could still be innacurate:
if (mwebSyncStatus is! SyncronizingSyncStatus && mwebSyncStatus is! SyncingSyncStatus) {
mwebSyncStatus = SyncronizingSyncStatus();
processingUtxos = true;
_processingTimer?.cancel();
_processingTimer = Timer.periodic(const Duration(seconds: 2), (timer) async {
processingUtxos = false;
timer.cancel();
});
}
return;
}
await updateUnspent();
await updateBalance();
final utxo = MwebUtxo(
address: sUtxo.address,
blockTime: sUtxo.blockTime,
height: sUtxo.height,
outputId: sUtxo.outputId,
value: sUtxo.value.toInt(),
);
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
if (mwebUtxosBox.containsKey(utxo.outputId)) {
// we've already stored this utxo, skip it:
// but do update the utxo height if it's somehow different:
final existingUtxo = mwebUtxosBox.get(utxo.outputId);
if (existingUtxo!.height != utxo.height) {
print(
"updating utxo height for $utxo.outputId: ${existingUtxo.height} -> ${utxo.height}");
existingUtxo.height = utxo.height;
await mwebUtxosBox.put(utxo.outputId, existingUtxo);
}
return;
}
// don't process utxos with addresses that are not in the mwebAddrs list:
if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) {
return;
}
await updateUnspent();
await updateBalance();
await mwebUtxosBox.put(utxo.outputId, utxo);
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
await handleIncoming(utxo);
}, onError: (error) {
print("error in utxo stream: $error");
});
// don't process utxos with addresses that are not in the mwebAddrs list:
if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) {
return;
}
await mwebUtxosBox.put(utxo.outputId, utxo);
await handleIncoming(utxo);
},
onError: (error) {
print("error in utxo stream: $error");
mwebSyncStatus = FailedSyncStatus(error: error.toString());
},
cancelOnError: true,
);
}
Future<void> checkMwebUtxosSpent() async {

View file

@ -79,6 +79,9 @@ class Node extends HiveObject with Keyable {
@HiveField(9)
bool? supportsSilentPayments;
@HiveField(10)
bool? supportsMweb;
bool get isSSL => useSSL ?? false;
bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;

View file

@ -15,6 +15,16 @@ class CwMweb {
static int? _port;
static const TIMEOUT_DURATION = Duration(seconds: 15);
static Timer? logTimer;
static String? nodeUriOverride;
static Future<void> setNodeUriOverride(String uri) async {
nodeUriOverride = uri;
if (_rpcClient != null) {
await stop();
await _initializeClient();
}
}
static void readFileWithTimer(String filePath) {
final file = File(filePath);

View file

@ -78,7 +78,7 @@
"billing_address_info": "Si se le solicita una dirección de facturación, proporcione su dirección de envío",
"biometric_auth_reason": "Escanee su huella digital para autenticar",
"bitcoin_dark_theme": "Tema oscuro de Bitcoin",
"bitcoin_light_theme": "Tema de la luz de Bitcoin",
"bitcoin_light_theme": "Tema claro de Bitcoin",
"bitcoin_payments_require_1_confirmation": "Los pagos de Bitcoin requieren 1 confirmación, que puede demorar 20 minutos o más. ¡Gracias por su paciencia! Se le enviará un correo electrónico cuando se confirme el pago.",
"block_remaining": "1 bloqueo restante",
"Blocks_remaining": "${status} Bloques restantes",
@ -93,7 +93,7 @@
"buy_with": "Compra con",
"by_cake_pay": "por Cake Pay",
"cake_2fa_preset": "Pastel 2FA preestablecido",
"cake_dark_theme": "Tema oscuro del pastel",
"cake_dark_theme": "Tema oscuro",
"cake_pay_account_note": "Regístrese con solo una dirección de correo electrónico para ver y comprar tarjetas. ¡Algunas incluso están disponibles con descuento!",
"cake_pay_learn_more": "¡Compre y canjee tarjetas de regalo al instante en la aplicación!\nDeslice el dedo de izquierda a derecha para obtener más información.",
"cake_pay_save_order": "La tarjeta debe enviarse a su correo electrónico dentro de 1 día hábil \n Guardar su ID de pedido:",
@ -144,9 +144,9 @@
"confirm_delete_wallet": "Esta acción eliminará esta billetera. ¿Desea continuar?",
"confirm_fee_deduction": "Confirmar la deducción de la tarifa",
"confirm_fee_deduction_content": "¿Acepta deducir la tarifa de la producción?",
"confirm_passphrase": "Confirmar la frase de pases",
"confirm_passphrase": "Confirmar la contraseña",
"confirm_sending": "Confirmar envío",
"confirm_silent_payments_switch_node": "Su nodo actual no admite pagos silenciosos \\ ncake billet cambiará a un nodo compatible, solo para escanear",
"confirm_silent_payments_switch_node": "Su nodo actual no admite pagos silenciosos \\ nCake cambiará a un nodo compatible, solo para escanear",
"confirmations": "Confirmaciones",
"confirmed": "Saldo confirmado",
"confirmed_tx": "Confirmado",
@ -281,7 +281,7 @@
"etherscan_history": "historia de etherscan",
"event": "Evento",
"events": "Eventos",
"exchange": "Intercambio",
"exchange": "Intercambiar",
"exchange_incorrect_current_wallet_for_xmr": "Si desea intercambiar XMR desde su billetera de pastel Monero Balance, primero cambie a su billetera Monero.",
"exchange_new_template": "Nueva plantilla",
"exchange_provider_unsupported": "¡${providerName} ya no es compatible!",
@ -373,8 +373,8 @@
"litecoin_mweb_display_card": "Mostrar tarjeta MWEB",
"litecoin_mweb_enable_later": "Puede elegir habilitar MWEB nuevamente en la configuración de visualización.",
"litecoin_mweb_logs": "Registros de mweb",
"litecoin_mweb_pegin": "Meter",
"litecoin_mweb_pegout": "Estirar la pata",
"litecoin_mweb_pegin": "Convertir",
"litecoin_mweb_pegout": "Recuperar",
"litecoin_mweb_scanning": "Escaneo mweb",
"litecoin_mweb_settings": "Configuración de MWEB",
"litecoin_mweb_warning": "El uso de MWEB inicialmente descargará ~ 600 MB de datos, y puede tomar hasta 30 minutos según la velocidad de la red. Estos datos iniciales solo se descargarán una vez y estarán disponibles para todas las billeteras de Litecoin",
@ -827,7 +827,7 @@
"transaction_priority_slow": "Lento",
"transaction_sent": "Transacción enviada!",
"transaction_sent_notice": "Si la pantalla no continúa después de 1 minuto, revisa un explorador de bloques y tu correo electrónico.",
"transactions": "Actas",
"transactions": "Transacciones",
"transactions_by_date": "Transacciones por fecha",
"trongrid_history": "Historia trongrid",
"trusted": "de confianza",
@ -940,4 +940,4 @@
"you_will_get": "Convertir a",
"you_will_send": "Convertir de",
"yy": "YY"
}
}