From dd532b4fe0e175f85e3d5c4692b2b45810404ac7 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 11 Mar 2024 09:46:20 -0300 Subject: [PATCH] feat: new electrs API & changes, fixes for last block scanning --- cw_bitcoin/lib/bitcoin_address_record.dart | 7 +- cw_bitcoin/lib/electrum.dart | 7 +- cw_bitcoin/lib/electrum_balance.dart | 9 +- cw_bitcoin/lib/electrum_wallet.dart | 275 +++++++++--------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 12 +- cw_bitcoin/pubspec.lock | 2 +- cw_core/lib/sync_status.dart | 7 + lib/core/sync_status_title.dart | 4 + lib/entities/default_settings_migration.dart | 4 +- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + 35 files changed, 205 insertions(+), 148 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index f5731f0f1..c6d5cfe2c 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -126,7 +126,8 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { super.isUsed = false, required this.silentPaymentTweak, required super.network, - }) : super(type: SilentPaymentsAddresType.p2sp); + required super.type, + }) : super(); factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { @@ -144,6 +145,10 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { ? network : BasedUtxoNetwork.fromName(decoded['network'] as String), silentPaymentTweak: decoded['silent_payment_tweak'] as String?, + type: decoded['type'] != null && decoded['type'] != '' + ? BitcoinAddressType.values + .firstWhere((type) => type.toString() == decoded['type'] as String) + : SilentPaymentsAddresType.p2sp, ); } diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index d9e068d65..4aefa3bd1 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -279,8 +279,11 @@ class ElectrumClient { Future> getHeader({required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) as Map; - Future> getTweaks({required int height}) async => - await callWithTimeout(method: 'blockchain.block.tweaks', params: [height]) as List; + Future> getTweaks({required int height, required int count}) async => + await callWithTimeout( + method: 'blockchain.block.tweaks', + params: [height, count], + timeout: 10000) as Map; Future estimatefee({required int p}) => call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) { diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 165ea447e..45de7de6d 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -3,8 +3,11 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_core/balance.dart'; class ElectrumBalance extends Balance { - const ElectrumBalance({required this.confirmed, required this.unconfirmed, required this.frozen}) - : super(confirmed, unconfirmed); + ElectrumBalance({ + required this.confirmed, + required this.unconfirmed, + required this.frozen, + }) : super(confirmed, unconfirmed); static ElectrumBalance? fromJSON(String? jsonSource) { if (jsonSource == null) { @@ -19,7 +22,7 @@ class ElectrumBalance extends Balance { frozen: decoded['frozen'] as int? ?? 0); } - final int confirmed; + int confirmed; final int unconfirmed; final int frozen; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 0220c440e..e0bdfde28 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -44,6 +44,8 @@ import 'package:http/http.dart' as http; part 'electrum_wallet.g.dart'; +const SCANNING_BLOCK_COUNT = 50; + class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet; abstract class ElectrumWalletBase @@ -72,8 +74,12 @@ abstract class ElectrumWalletBase _scripthashesUpdateSubject = {}, balance = ObservableMap.of(currency != null ? { - currency: - initialBalance ?? const ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0) + currency: initialBalance ?? + ElectrumBalance( + confirmed: 0, + unconfirmed: 0, + frozen: 0, + ) } : {}), this.unspentCoinsInfo = unspentCoinsInfo, @@ -194,9 +200,9 @@ abstract class ElectrumWalletBase )); await for (var message in receivePort) { - if (message is bool) { + if (message is bool && message == false) { nodeSupportsSilentPayments = message; - syncStatus = UnsupportedSyncStatus(); + syncStatus = TimedOutSyncStatus(); } if (message is Map) { @@ -238,7 +244,7 @@ abstract class ElectrumWalletBase await transactionHistory.save(); await updateUnspent(); - await save(); + await updateBalance(); } } } @@ -320,7 +326,6 @@ abstract class ElectrumWalletBase {int? inputsCount, bool? hasSilentPayment}) async { final utxos = []; - List privateKeys = []; var leftAmount = credentialsAmount; var allInputsAmount = 0; @@ -340,13 +345,15 @@ abstract class ElectrumWalletBase final address = _addressTypeFromStr(utx.address, network); ECPrivate? privkey; + bool? isSilentPayment = false; if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { - privkey = walletAddresses.silentAddress!.b_spend.clone().tweakAdd( - BigintUtils.fromBytes(BytesUtils.fromHexString( - (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord) - .silentPaymentTweak!)), - ); + privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( + BigintUtils.fromBytes(BytesUtils.fromHexString( + (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord) + .silentPaymentTweak!)), + ); spendsSilentPayment = true; + isSilentPayment = true; } else { privkey = generateECPrivate( hd: utx.bitcoinAddressRecord.isHidden @@ -356,7 +363,6 @@ abstract class ElectrumWalletBase network: network); } - privateKeys.add(privkey); inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr)); inputPubKeys.add(privkey.getPublic()); vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); @@ -368,6 +374,7 @@ abstract class ElectrumWalletBase value: BigInt.from(utx.value), vout: utx.vout, scriptType: _getScriptType(address), + isSilentPayment: isSilentPayment, ), ownerDetails: UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address), @@ -485,7 +492,7 @@ abstract class ElectrumWalletBase return EstimatedTxResult( utxos: utxos, - privateKeys: privateKeys, + inputPrivKeyInfos: inputPrivKeyInfos, fee: fee, amount: amount, spendsSilentPayment: spendsSilentPayment); @@ -535,8 +542,13 @@ abstract class ElectrumWalletBase } final estimatedTx = await _estimateTxFeeAndInputsToUse( - credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials, - hasSilentPayment: hasSilentPayment); + credentialsAmount, + sendAll, + outputAddresses, + outputs, + transactionCredentials, + hasSilentPayment: hasSilentPayment, + ); final txb = BitcoinTransactionBuilder( utxos: estimatedTx.utxos, @@ -545,17 +557,21 @@ abstract class ElectrumWalletBase network: network); final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) { - final key = estimatedTx.privateKeys - .firstWhereOrNull((element) => element.getPublic().toHex() == publicKey); + final key = estimatedTx.inputPrivKeyInfos + .firstWhereOrNull((element) => element.privkey.getPublic().toHex() == publicKey); if (key == null) { throw Exception("Cannot find private key"); } if (utxo.utxo.isP2tr()) { - return key.signTapRoot(txDigest, sighash: sighash); + return key.privkey.signTapRoot( + txDigest, + sighash: sighash, + tweak: utxo.utxo.isSilentPayment != true, + ); } else { - return key.signInput(txDigest, sigHash: sighash); + return key.privkey.signInput(txDigest, sigHash: sighash); } }); @@ -820,7 +836,9 @@ abstract class ElectrumWalletBase (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$hash/status"))).body); time = status["block_time"] as int?; - confirmations = currentChainTip! - (status["block_height"] as int? ?? 0); + final height = status["block_height"] as int? ?? 0; + confirmations = + height > 0 ? (await electrumClient.getCurrentBlockChainTip())! - height + 1 : 0; } else { final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash); @@ -1056,6 +1074,19 @@ abstract class ElectrumWalletBase Future updateBalance() async { balance[currency] = await _fetchBalances(); + + // Update balance stored from scanned silent payment transactions + try { + transactionHistory.transactions.values.forEach((tx) { + if (tx.unspents != null) { + balance[currency]!.confirmed += tx.unspents! + .where((unspent) => unspent.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) + .map((e) => e.value) + .reduce((value, element) => value + element); + } + }); + } catch (_) {} + await save(); } @@ -1194,9 +1225,9 @@ Future startRefresh(ScanData scanData) async { try { final electrumClient = await getElectrumConnection(); - List? tweaks; + Map? tweaks; try { - tweaks = await electrumClient.getTweaks(height: syncHeight); + tweaks = await electrumClient.getTweaks(height: syncHeight, count: SCANNING_BLOCK_COUNT); } catch (e) { if (e is RequestFailedTimeoutException) { return scanData.sendPort.send(false); @@ -1208,133 +1239,103 @@ Future startRefresh(ScanData scanData) async { SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); } - for (var i = 0; i < tweaks!.length; i++) { + final blockHeights = tweaks!.keys; + for (var i = 0; i < blockHeights.length; i++) { try { - final details = tweaks[i] as Map; - final outputPubkeys = (details["output_pubkeys"] as List); - final tweak = details["tweak"].toString(); + final blockHeight = blockHeights.elementAt(i).toString(); + final blockTweaks = tweaks[blockHeight] as Map; - // TODO: if tx already scanned & stored skip - // if (scanData.transactionHistoryIds.contains(txid)) { - // // already scanned tx, continue to next tx - // pos++; - // continue; - // } + for (var j = 0; j < blockTweaks.keys.length; j++) { + final txid = blockTweaks.keys.elementAt(j); + final details = blockTweaks[txid] as Map; + final outputPubkeys = (details["output_pubkeys"] as Map); + final tweak = details["tweak"].toString(); - final spb = SilentPaymentBuilder(receiverTweak: tweak); - final result = spb.scanOutputs( - scanData.silentAddress.b_scan, - scanData.silentAddress.B_spend, - outputPubkeys - .map((output) => - BytesUtils.toHexString(BytesUtils.fromHexString(output.toString()).sublist(2)) - .toString()) - .toList(), - precomputedLabels: scanData.labels, - ); - - if (result.isEmpty) { - // no results tx, continue to next tx - continue; - } - - result.forEach((key, value) async { - final t_k = value[0]; - final address = ECPublic.fromHex(key).toTaprootAddress().toAddress(scanData.network); - - final listUnspent = - await electrumClient.getListUnspentWithAddress(address, scanData.network); - - BitcoinUnspent? info; - await Future.forEach>(listUnspent, (unspent) async { - try { - final addressRecord = BitcoinSilentPaymentAddressRecord(address, - index: 0, - isHidden: false, - isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k); - info = BitcoinUnspent.fromJSON(addressRecord, unspent); - } catch (_) {} - }); - - if (info == null) { - return; - } - - // final tweak = value[0]; - // String? label; - // if (value.length > 1) label = value[1]; - - final tx = info!; - final txInfo = ElectrumTransactionInfo( - WalletType.bitcoin, - id: tx.hash, - height: syncHeight, - amount: 0, // will be added later via unspent - fee: 0, - direction: TransactionDirection.incoming, - isPending: false, - date: DateTime.now(), - confirmations: currentChainTip - syncHeight - 1, - to: scanData.silentAddress.toString(), - unspents: [tx], + final spb = SilentPaymentBuilder(receiverTweak: tweak); + final addToWallet = spb.scanOutputs( + scanData.silentAddress.b_scan, + scanData.silentAddress.B_spend, + outputPubkeys.values + .map((o) => getScriptFromOutput( + o["pubkey"].toString(), int.parse(o["amount"].toString()))) + .toList(), + precomputedLabels: scanData.labels, ); - // bool spent = false; - // for (final s in status) { - // if ((s["spent"] as bool) == true) { - // spent = true; + if (addToWallet.isEmpty) { + // no results tx, continue to next tx + continue; + } - // scanData.sendPort.send({txid: txInfo}); + addToWallet.forEach((key, value) async { + final t_k = value.tweak; - // final sentTxId = s["txid"] as String; - // final sentTx = json.decode( - // (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId"))) - // .body); + final addressRecord = BitcoinSilentPaymentAddressRecord( + value.output.address.toAddress(scanData.network), + index: 0, + isHidden: false, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddresType.p2tr, + ); - // int amount = 0; - // for (final out in (sentTx["vout"] as List)) { - // amount += out["value"] as int; - // } + int? amount; + int? pos; + outputPubkeys.entries.firstWhere((k) { + final matches = k.value["pubkey"] == key; + if (matches) { + amount = int.parse(k.value["amount"].toString()); + pos = int.parse(k.key.toString()); + return true; + } + return false; + }); - // final height = s["status"]["block_height"] as int; + final json = { + 'address_record': addressRecord.toJSON(), + 'tx_hash': txid, + 'value': amount!, + 'tx_pos': pos!, + 'silent_payment_tweak': t_k, + }; - // scanData.sendPort.send({ - // sentTxId: ElectrumTransactionInfo( - // WalletType.bitcoin, - // id: sentTxId, - // height: height, - // amount: amount, - // fee: 0, - // direction: TransactionDirection.outgoing, - // isPending: false, - // date: DateTime.fromMillisecondsSinceEpoch( - // (s["status"]["block_time"] as int) * 1000), - // confirmations: currentChainTip - height, - // ) - // }); - // } - // } + final tx = BitcoinUnspent.fromJSON(addressRecord, json); - // if (spent) { - // return; - // } + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: tx.hash, + height: syncHeight, + amount: 0, // will be added later via unspent + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + date: DateTime.now(), + confirmations: currentChainTip - syncHeight - 1, + to: value.label != null + ? SilentPaymentAddress( + version: scanData.silentAddress.version, + B_scan: scanData.silentAddress.B_scan + .tweakAdd(BigintUtils.fromBytes(BytesUtils.fromHexString(value.tweak))), + B_spend: scanData.silentAddress.B_spend, + hrp: scanData.silentAddress.hrp, + ).toString() + : scanData.silentAddress.toString(), + unspents: [tx], + ); - // found utxo for tx, send unspent coin to main isolate - // scanData.sendPort.send(txInfo); + scanData.sendPort.send({txInfo.id: txInfo}); + }); + } + } catch (e, s) { + print([e, s]); + } - // also send tx data for tx history - scanData.sendPort.send({txInfo.id: txInfo}); - }); - } catch (_) {} + // Finished scanning block, add 1 to height and continue to next block in loop + syncHeight += 1; + scanData.sendPort.send(SyncResponse(syncHeight, + SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); } - - // Finished scanning block, add 1 to height and continue to next block in loop - syncHeight += 1; - currentChainTip = await getNodeHeightOrUpdate(syncHeight); - scanData.sendPort.send(SyncResponse(syncHeight, - SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -1348,13 +1349,13 @@ Future startRefresh(ScanData scanData) async { class EstimatedTxResult { EstimatedTxResult( {required this.utxos, - required this.privateKeys, + required this.inputPrivKeyInfos, required this.fee, required this.amount, required this.spendsSilentPayment}); final List utxos; - final List privateKeys; + final List inputPrivKeyInfos; final int fee; final int amount; final bool spendsSilentPayment; diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index cddb8f1f3..270ad855f 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -57,8 +57,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'); if (silentAddresses.length == 0) - silentAddresses.add(BitcoinSilentPaymentAddressRecord(silentAddress.toString(), - index: 1, isHidden: false, name: "", silentPaymentTweak: null, network: network)); + silentAddresses.add(BitcoinSilentPaymentAddressRecord( + silentAddress.toString(), + index: 1, + isHidden: false, + name: "", + silentPaymentTweak: null, + network: network, + type: SilentPaymentsAddresType.p2sp, + )); } updateAddressesByMatch(); @@ -267,6 +274,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { silentPaymentTweak: BytesUtils.toHexString(silentAddress!.generateLabel(currentSilentAddressIndex)), network: network, + type: SilentPaymentsAddresType.p2sp, ); silentAddresses.add(address); diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 91af2d807..8d6f6268b 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v2 - resolved-ref: f45e34d18ddff52764101a2ec96dcbc2be730555 + resolved-ref: fb4c0a0b6cf24628ddad7d3cdc58e4c918eff714 url: "https://github.com/cake-tech/bitcoin_base" source: git version: "4.0.0" diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index f562e7ce3..dd2d9ca67 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -63,6 +63,13 @@ class UnsupportedSyncStatus extends SyncStatus { double progress() => 1.0; } +class TimedOutSyncStatus extends SyncStatus { + @override + double progress() => 1.0; + @override + String toString() => 'Timed out'; +} + class LostConnectionSyncStatus extends SyncStatus { @override double progress() => 1.0; diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index 23bfb0db2..c743caf55 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -40,5 +40,9 @@ String syncStatusTitle(SyncStatus syncStatus) { return S.current.sync_status_unsupported; } + if (syncStatus is TimedOutSyncStatus) { + return S.current.sync_status_timed_out; + } + return ''; } diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index fb3e9e80c..d12140cb9 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -23,8 +23,8 @@ import 'package:collection/collection.dart'; const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081'; const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002'; -const publicBitcoinTestnetElectrumAddress = 'electrum.blockstream.info'; -const publicBitcoinTestnetElectrumPort = '60002'; +const publicBitcoinTestnetElectrumAddress = '198.58.111.154'; +const publicBitcoinTestnetElectrumPort = '50002'; const publicBitcoinTestnetElectrumUri = '$publicBitcoinTestnetElectrumAddress:$publicBitcoinTestnetElectrumPort'; const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002'; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index b02e3604f..452b20933 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "بدء المزامنة", "sync_status_syncronized": "متزامن", "sync_status_syncronizing": "يتم المزامنة", + "sync_status_timed_out": "نفد وقته", "sync_status_unsupported": "عقدة غير مدعومة", "syncing_wallet_alert_content": "قد لا يكتمل رصيدك وقائمة المعاملات الخاصة بك حتى تظهر عبارة “SYNCHRONIZED“ في الأعلى. انقر / اضغط لمعرفة المزيد.", "syncing_wallet_alert_title": "محفظتك تتم مزامنتها", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 89c4cc24b..1f02060ec 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ", "sync_status_syncronized": "СИНХРОНИЗИРАНО", "sync_status_syncronizing": "СИНХРОНИЗИРАНЕ", + "sync_status_timed_out": "ВРЕМЕТО ИЗТЕЧЕ", "sync_status_unsupported": "Неподдържан възел", "syncing_wallet_alert_content": "Списъкът ви с баланс и транзакции може да не е пълен, докато в горната част не пише „СИНХРОНИЗИРАН“. Кликнете/докоснете, за да научите повече.", "syncing_wallet_alert_title": "Вашият портфейл се синхронизира", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index e536a7338..cf045754d 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "SPOUŠTĚNÍ SYNCHRONIZACE", "sync_status_syncronized": "SYNCHRONIZOVÁNO", "sync_status_syncronizing": "SYNCHRONIZUJI", + "sync_status_timed_out": "ČAS VYPRŠEL", "sync_status_unsupported": "Nepodporovaný uzel", "syncing_wallet_alert_content": "Váš seznam zůstatků a transakcí nemusí být úplný, dokud nebude nahoře uvedeno „SYNCHRONIZOVANÉ“. Kliknutím/klepnutím se dozvíte více.", "syncing_wallet_alert_title": "Vaše peněženka se synchronizuje", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index f1179fc8a..2ab5e6c9f 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "STARTE SYNCHRONISIERUNG", "sync_status_syncronized": "SYNCHRONISIERT", "sync_status_syncronizing": "SYNCHRONISIERE", + "sync_status_timed_out": "Zeitlich abgestimmt", "sync_status_unsupported": "Nicht unterstützter Knoten", "syncing_wallet_alert_content": "Ihr Kontostand und Ihre Transaktionsliste sind möglicherweise erst vollständig, wenn oben „SYNCHRONISIERT“ steht. Klicken/tippen Sie, um mehr zu erfahren.", "syncing_wallet_alert_title": "Ihr Wallet wird synchronisiert", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 5b77a4c61..78d7dd777 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "STARTING SYNC", "sync_status_syncronized": "SYNCHRONIZED", "sync_status_syncronizing": "SYNCHRONIZING", + "sync_status_timed_out": "TIMED OUT", "sync_status_unsupported": "UNSUPPORTED NODE", "syncing_wallet_alert_content": "Your balance and transaction list may not be complete until it says “SYNCHRONIZED” at the top. Click/tap to learn more.", "syncing_wallet_alert_title": "Your wallet is syncing", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index c27c8829b..f1c27763c 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "EMPEZANDO A SINCRONIZAR", "sync_status_syncronized": "SINCRONIZADO", "sync_status_syncronizing": "SINCRONIZANDO", + "sync_status_timed_out": "CADUCADO", "sync_status_unsupported": "Nodo no compatible", "syncing_wallet_alert_content": "Es posible que su lista de saldo y transacciones no esté completa hasta que diga \"SINCRONIZADO\" en la parte superior. Haga clic/toque para obtener más información.", "syncing_wallet_alert_title": "Tu billetera se está sincronizando", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 8068c67a9..7f2ed7799 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "DÉBUT DE SYNCHRO", "sync_status_syncronized": "SYNCHRONISÉ", "sync_status_syncronizing": "SYNCHRONISATION EN COURS", + "sync_status_timed_out": "FIN DU TEMPS", "sync_status_unsupported": "Nœud non pris en charge", "syncing_wallet_alert_content": "Votre solde et votre liste de transactions peuvent ne pas être à jour tant que la mention « SYNCHRONISÉ » n'apparaît en haut de l'écran. Cliquez/appuyez pour en savoir plus.", "syncing_wallet_alert_title": "Votre portefeuille (wallet) est en cours de synchronisation", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 5b79f8e9e..5a52af001 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "KWAFI", "sync_status_syncronized": "KYAU", "sync_status_syncronizing": "KWAFI", + "sync_status_timed_out": "ATED Out", "sync_status_unsupported": "Ba a Taimako ba", "syncing_wallet_alert_content": "Ma'aunin ku da lissafin ma'amala bazai cika ba har sai an ce \"SYNCHRONIZED\" a saman. Danna/matsa don ƙarin koyo.", "syncing_wallet_alert_title": "Walat ɗin ku yana aiki tare", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 35b5d5d01..1aa9d3ca0 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "सिताज़ा करना", "sync_status_syncronized": "सिंक्रनाइज़", "sync_status_syncronizing": "सिंक्रनाइज़ करने", + "sync_status_timed_out": "समय समााप्त", "sync_status_unsupported": "असमर्थित नोड", "syncing_wallet_alert_content": "आपकी शेष राशि और लेनदेन सूची तब तक पूरी नहीं हो सकती जब तक कि शीर्ष पर \"सिंक्रनाइज़्ड\" न लिखा हो। अधिक जानने के लिए क्लिक/टैप करें।", "syncing_wallet_alert_title": "आपका वॉलेट सिंक हो रहा है", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 4bbcdd470..b8bc9f3b6 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "ZAPOČINJEMO SINKRONIZIRANJE", "sync_status_syncronized": "SINKRONIZIRANO", "sync_status_syncronizing": "SINKRONIZIRANJE", + "sync_status_timed_out": "ISTEKLO", "sync_status_unsupported": "Nepodržani čvor", "syncing_wallet_alert_content": "Vaš saldo i popis transakcija možda neće biti potpuni sve dok na vrhu ne piše \"SINKRONIZIRANO\". Kliknite/dodirnite da biste saznali više.", "syncing_wallet_alert_title": "Vaš novčanik se sinkronizira", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 04e85b5b8..a6f11c1ff 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -639,6 +639,7 @@ "sync_status_starting_sync": "MULAI SINKRONISASI", "sync_status_syncronized": "SUDAH TERSINKRONISASI", "sync_status_syncronizing": "SEDANG SINKRONISASI", + "sync_status_timed_out": "WAKTU HABIS", "sync_status_unsupported": "Node yang tidak didukung", "syncing_wallet_alert_content": "Saldo dan daftar transaksi Anda mungkin belum lengkap sampai tertulis “SYNCHRONIZED” di bagian atas. Klik/ketuk untuk mempelajari lebih lanjut.", "syncing_wallet_alert_title": "Dompet Anda sedang disinkronkan", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 07be97938..7bdba86d1 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "INIZIO SINC", "sync_status_syncronized": "SINCRONIZZATO", "sync_status_syncronizing": "SINCRONIZZAZIONE", + "sync_status_timed_out": "FUORI TEMPO", "sync_status_unsupported": "Nodo non supportato", "syncing_wallet_alert_content": "Il saldo e l'elenco delle transazioni potrebbero non essere completi fino a quando non viene visualizzato \"SYNCHRONIZED\" in alto. Clicca/tocca per saperne di più.", "syncing_wallet_alert_title": "Il tuo portafoglio si sta sincronizzando", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 0b5461401..84c780b4b 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "同期の開始", "sync_status_syncronized": "同期された", "sync_status_syncronizing": "同期", + "sync_status_timed_out": "タイムアウトしました", "sync_status_unsupported": "サポートされていないノード", "syncing_wallet_alert_content": "上部に「同期済み」と表示されるまで、残高と取引リストが完了していない可能性があります。詳細については、クリック/タップしてください。", "syncing_wallet_alert_title": "ウォレットは同期中です", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index a30498bf2..4adf10d43 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "동기화 시작", "sync_status_syncronized": "동기화", "sync_status_syncronizing": "동기화", + "sync_status_timed_out": "시간 초과", "sync_status_unsupported": "지원되지 않은 노드", "syncing_wallet_alert_content": "상단에 \"동기화됨\"이라고 표시될 때까지 잔액 및 거래 목록이 완전하지 않을 수 있습니다. 자세히 알아보려면 클릭/탭하세요.", "syncing_wallet_alert_title": "지갑 동기화 중", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index d4e85a195..92a1784aa 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "စင့်ခ်လုပ်ခြင်း။", "sync_status_syncronized": "ထပ်တူပြုထားသည်။", "sync_status_syncronizing": "ထပ်တူပြုခြင်း။", + "sync_status_timed_out": "ထွက်အချိန်ကုန်", "sync_status_unsupported": "node မထောက်ပံ့ node ကို", "syncing_wallet_alert_content": "သင်၏လက်ကျန်နှင့် ငွေပေးငွေယူစာရင်းသည် ထိပ်တွင် \"Synchronizeed\" ဟုပြောသည်အထိ မပြီးမြောက်နိုင်ပါ။ ပိုမိုလေ့လာရန် နှိပ်/နှိပ်ပါ။", "syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 89c305441..dd5445fdf 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "BEGINNEN MET SYNCHRONISEREN", "sync_status_syncronized": "SYNCHRONIZED", "sync_status_syncronizing": "SYNCHRONISEREN", + "sync_status_timed_out": "Uitgeput", "sync_status_unsupported": "Niet ondersteund knooppunt", "syncing_wallet_alert_content": "Uw saldo- en transactielijst is mogelijk pas compleet als er bovenaan 'GESYNCHRONISEERD' staat. Klik/tik voor meer informatie.", "syncing_wallet_alert_title": "Uw portemonnee wordt gesynchroniseerd", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 3f301b84c..4f5e6c279 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "ROZPOCZĘCIE SYNCHRONIZACJI", "sync_status_syncronized": "ZSYNCHRONIZOWANO", "sync_status_syncronizing": "SYNCHRONIZACJA", + "sync_status_timed_out": "PRZEKROCZONO LIMIT CZASU", "sync_status_unsupported": "Nieobsługiwany węzeł", "syncing_wallet_alert_content": "Twoje saldo i lista transakcji mogą nie być kompletne, dopóki u góry nie pojawi się napis „SYNCHRONIZOWANY”. Kliknij/stuknij, aby dowiedzieć się więcej.", "syncing_wallet_alert_title": "Twój portfel się synchronizuje", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 1fa193b48..1621b2d96 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "INICIANDO SINCRONIZAÇÃO", "sync_status_syncronized": "SINCRONIZADO", "sync_status_syncronizing": "SINCRONIZANDO", + "sync_status_timed_out": "TEMPO ESGOTADO", "sync_status_unsupported": "Nó não suportado", "syncing_wallet_alert_content": "Seu saldo e lista de transações podem não estar completos até que diga “SYNCHRONIZED” no topo. Clique/toque para saber mais.", "syncing_wallet_alert_title": "Sua carteira está sincronizando", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index b8a58defe..0e8c6ab85 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "НАЧАЛО СИНХРОНИЗАЦИИ", "sync_status_syncronized": "СИНХРОНИЗИРОВАН", "sync_status_syncronizing": "СИНХРОНИЗАЦИЯ", + "sync_status_timed_out": "ВРЕМЯ ВЫШЛО", "sync_status_unsupported": "Неподдерживаемый узел", "syncing_wallet_alert_content": "Ваш баланс и список транзакций могут быть неполными, пока вверху не будет написано «СИНХРОНИЗИРОВАНО». Щелкните/коснитесь, чтобы узнать больше.", "syncing_wallet_alert_title": "Ваш кошелек синхронизируется", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 3cb70a13d..e5ce652e6 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "กำลังเริ่มซิงโครไนซ์", "sync_status_syncronized": "ซิงโครไนซ์แล้ว", "sync_status_syncronizing": "กำลังซิงโครไนซ์", + "sync_status_timed_out": "หมดเวลา", "sync_status_unsupported": "โหนดที่ไม่ได้รับการสนับสนุน", "syncing_wallet_alert_content": "รายการยอดเงินและธุรกรรมของคุณอาจไม่สมบูรณ์จนกว่าจะมีข้อความว่า “ซิงโครไนซ์” ที่ด้านบน คลิก/แตะเพื่อเรียนรู้เพิ่มเติม่", "syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index f7d10bc0c..f03229330 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "Simula sa pag -sync", "sync_status_syncronized": "Naka -synchronize", "sync_status_syncronizing": "Pag -synchronize", + "sync_status_timed_out": "Nag -time out", "sync_status_unsupported": "Hindi suportadong node", "syncing_wallet_alert_content": "Ang iyong balanse at listahan ng transaksyon ay maaaring hindi kumpleto hanggang sa sabihin nito na \"naka -synchronize\" sa tuktok. Mag -click/tap upang malaman ang higit pa.", "syncing_wallet_alert_title": "Ang iyong pitaka ay nag -sync", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index fcd63448f..bceec8f61 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "SENKRONİZE BAŞLATILIYOR", "sync_status_syncronized": "SENKRONİZE EDİLDİ", "sync_status_syncronizing": "SENKRONİZE EDİLİYOR", + "sync_status_timed_out": "ZAMAN AŞIMINA UĞRADI", "sync_status_unsupported": "Desteklenmeyen düğüm", "syncing_wallet_alert_content": "Bakiyeniz ve işlem listeniz, en üstte \"SENKRONİZE EDİLDİ\" yazana kadar tamamlanmamış olabilir. Daha fazla bilgi edinmek için tıklayın/dokunun.", "syncing_wallet_alert_title": "Cüzdanınız senkronize ediliyor", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 07088a2ba..e5ed5c049 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "ПОЧАТОК СИНХРОНІЗАЦІЇ", "sync_status_syncronized": "СИНХРОНІЗОВАНИЙ", "sync_status_syncronizing": "СИНХРОНІЗАЦІЯ", + "sync_status_timed_out": "ТАЙМ-АУТ", "sync_status_unsupported": "Непідтримуваний вузол", "syncing_wallet_alert_content": "Ваш баланс та список транзакцій може бути неповним, доки вгорі не буде написано «СИНХРОНІЗОВАНО». Натисніть/торкніться, щоб дізнатися більше.", "syncing_wallet_alert_title": "Ваш гаманець синхронізується", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index b624e272a..70b72dc94 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "مطابقت پذیری شروع کر رہا ہے۔", "sync_status_syncronized": "مطابقت پذیر", "sync_status_syncronizing": "مطابقت پذیری", + "sync_status_timed_out": "وقت ختم", "sync_status_unsupported": "غیر تعاون یافتہ نوڈ", "syncing_wallet_alert_content": "آپ کے بیلنس اور لین دین کی فہرست اس وقت تک مکمل نہیں ہو سکتی جب تک کہ یہ سب سے اوپر \"SYNCRONIZED\" نہ کہے۔ مزید جاننے کے لیے کلک/تھپتھپائیں۔", "syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 9612d561c..a8b4b7ab7 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "Ń BẸ̀RẸ̀ RẸ́", "sync_status_syncronized": "TI MÚDỌ́GBA", "sync_status_syncronizing": "Ń MÚDỌ́GBA", + "sync_status_timed_out": "Ti akoko jade", "sync_status_unsupported": "Ile-igbimọ ti ko ni atilẹyin", "syncing_wallet_alert_content": "Iwontunws.funfun rẹ ati atokọ idunadura le ma pari titi ti yoo fi sọ “SYNCHRONIZED” ni oke. Tẹ/tẹ ni kia kia lati ni imọ siwaju sii.", "syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 26cd880b9..10d184cb5 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "开始同步", "sync_status_syncronized": "已同步", "sync_status_syncronizing": "正在同步", + "sync_status_timed_out": "时间到", "sync_status_unsupported": "不支持的节点", "syncing_wallet_alert_content": "您的余额和交易列表可能不完整,直到顶部显示“已同步”。单击/点击以了解更多信息。", "syncing_wallet_alert_title": "您的钱包正在同步",