From 5294820eb6dab8ec8556e006c91c6e5f6a8f41de Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 8 Oct 2020 09:39:01 +0200 Subject: [PATCH 001/123] Bump version number for v1.4.0 --- build.gradle | 2 +- common/src/main/java/bisq/common/app/Version.java | 2 +- desktop/package/linux/Dockerfile | 2 +- desktop/package/linux/package.sh | 2 +- desktop/package/linux/release.sh | 2 +- desktop/package/macosx/Info.plist | 4 ++-- desktop/package/macosx/create_app.sh | 2 +- desktop/package/macosx/finalize.sh | 2 +- desktop/package/macosx/replace_version_number.sh | 4 ++-- desktop/package/windows/package.bat | 2 +- desktop/package/windows/release.bat | 2 +- relay/src/main/resources/version.txt | 2 +- seednode/src/main/java/bisq/seednode/SeedNodeMain.java | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index f70959744d3..fe986909e55 100644 --- a/build.gradle +++ b/build.gradle @@ -385,7 +385,7 @@ configure(project(':desktop')) { apply plugin: 'witness' apply from: '../gradle/witness/gradle-witness.gradle' - version = '1.3.9-SNAPSHOT' + version = '1.4.0' mainClassName = 'bisq.desktop.app.BisqAppMain' diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index 809e1e9bf0e..fd8cfb64634 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -30,7 +30,7 @@ public class Version { // VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update // Therefore all sub versions start again with 1 // We use semantic versioning with major, minor and patch - public static final String VERSION = "1.3.9"; + public static final String VERSION = "1.4.0"; /** * Holds a list of the versions of tagged resource files for optimizing the getData requests. diff --git a/desktop/package/linux/Dockerfile b/desktop/package/linux/Dockerfile index 5c8b4a7058a..1da1e5f5cc0 100644 --- a/desktop/package/linux/Dockerfile +++ b/desktop/package/linux/Dockerfile @@ -8,7 +8,7 @@ # pull base image FROM openjdk:8-jdk -ENV version 1.3.9-SNAPSHOT +ENV version 1.4.0 RUN apt-get update && apt-get install -y --no-install-recommends openjfx && rm -rf /var/lib/apt/lists/* && apt-get install -y vim fakeroot diff --git a/desktop/package/linux/package.sh b/desktop/package/linux/package.sh index 25c989b097f..553336f0cdf 100755 --- a/desktop/package/linux/package.sh +++ b/desktop/package/linux/package.sh @@ -6,7 +6,7 @@ # - Update version below # - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory -version=1.3.9-SNAPSHOT +version=1.4.0 version_base=$(echo $version | awk -F'[_-]' '{print $1}') if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then diff --git a/desktop/package/linux/release.sh b/desktop/package/linux/release.sh index ae93fad6739..a46d48d3e77 100755 --- a/desktop/package/linux/release.sh +++ b/desktop/package/linux/release.sh @@ -4,7 +4,7 @@ # Prior to running this script: # - Update version below -version=1.3.9-SNAPSHOT +version=1.4.0 base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../.. package_dir=$base_dir/desktop/package release_dir=$base_dir/desktop/release/$version diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index b0f6d5a678a..f701df84e23 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.3.9 + 1.4.0 CFBundleShortVersionString - 1.3.9 + 1.4.0 CFBundleExecutable Bisq diff --git a/desktop/package/macosx/create_app.sh b/desktop/package/macosx/create_app.sh index 25540a5d782..70781f1dc0b 100755 --- a/desktop/package/macosx/create_app.sh +++ b/desktop/package/macosx/create_app.sh @@ -6,7 +6,7 @@ mkdir -p deploy set -e -version="1.3.9-SNAPSHOT" +version="1.4.0" cd .. ./gradlew :desktop:build -x test shadowJar diff --git a/desktop/package/macosx/finalize.sh b/desktop/package/macosx/finalize.sh index 2c1916b17b5..fc61be73d5e 100755 --- a/desktop/package/macosx/finalize.sh +++ b/desktop/package/macosx/finalize.sh @@ -2,7 +2,7 @@ cd ../../ -version="1.3.9-SNAPSHOT" +version="1.4.0" target_dir="releases/$version" diff --git a/desktop/package/macosx/replace_version_number.sh b/desktop/package/macosx/replace_version_number.sh index ce7486313c3..12f1963dd5a 100755 --- a/desktop/package/macosx/replace_version_number.sh +++ b/desktop/package/macosx/replace_version_number.sh @@ -2,8 +2,8 @@ cd $(dirname $0)/../../../ -oldVersion=1.3.8 -newVersion=1.3.9 +oldVersion=1.3.9 +newVersion=1.4.0 find . -type f \( -name "finalize.sh" \ -o -name "create_app.sh" \ diff --git a/desktop/package/windows/package.bat b/desktop/package/windows/package.bat index 0ad8e3838ab..2ca93718c21 100644 --- a/desktop/package/windows/package.bat +++ b/desktop/package/windows/package.bat @@ -11,7 +11,7 @@ @echo off -set version=1.3.9-SNAPSHOT +set version=1.4.0 if not exist "%JAVA_HOME%\bin\javapackager.exe" ( if not exist "%ProgramFiles%\Java\jdk-10.0.2" ( echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK. diff --git a/desktop/package/windows/release.bat b/desktop/package/windows/release.bat index e49377c78cb..d6e8597ca5d 100644 --- a/desktop/package/windows/release.bat +++ b/desktop/package/windows/release.bat @@ -6,7 +6,7 @@ @echo off -set version=1.3.9-SNAPSHOT +set version=1.4.0 set release_dir=%~dp0..\..\..\releases\%version% set package_dir=%~dp0.. diff --git a/relay/src/main/resources/version.txt b/relay/src/main/resources/version.txt index 1610789efb4..88c5fb891dc 100644 --- a/relay/src/main/resources/version.txt +++ b/relay/src/main/resources/version.txt @@ -1 +1 @@ -1.3.9-SNAPSHOT +1.4.0 diff --git a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java index 5d0ca61b391..4d871ab462c 100644 --- a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java @@ -32,7 +32,7 @@ @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { - private static final String VERSION = "1.3.9"; + private static final String VERSION = "1.4.0"; private SeedNode seedNode; public SeedNodeMain() { From 2d06946c18cf7de746f811bc51b0942e7bd12b24 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 8 Oct 2020 09:41:42 +0200 Subject: [PATCH 002/123] Update translations for v1.4.0 --- .../i18n/displayStrings_de.properties | 187 ++++++++--- .../i18n/displayStrings_es.properties | 217 +++++++++---- .../i18n/displayStrings_fa.properties | 189 ++++++++--- .../i18n/displayStrings_fr.properties | 195 ++++++++--- .../i18n/displayStrings_ja.properties | 287 +++++++++++------ .../i18n/displayStrings_pt-br.properties | 187 ++++++++--- .../i18n/displayStrings_pt.properties | 187 ++++++++--- .../i18n/displayStrings_ru.properties | 187 ++++++++--- .../i18n/displayStrings_th.properties | 187 ++++++++--- .../i18n/displayStrings_vi.properties | 187 ++++++++--- .../i18n/displayStrings_zh-hans.properties | 299 +++++++++++------ .../i18n/displayStrings_zh-hant.properties | 303 +++++++++++------- 12 files changed, 1840 insertions(+), 772 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 6f0ceac2fca..b1e9c699231 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -35,7 +35,7 @@ shared.no=Nein shared.iUnderstand=Ich verstehe shared.na=N/A shared.shutDown=Herunterfahren -shared.reportBug=Bug bei Github-Issues melden +shared.reportBug=Report bug on GitHub shared.buyBitcoin=Bitcoin kaufen shared.sellBitcoin=Bitcoin verkaufen shared.buyCurrency={0} kaufen @@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (min - max) shared.removeOffer=Angebot entfernen shared.dontRemoveOffer=Angebot nicht entfernen shared.editOffer=Angebot bearbeiten -shared.openLargeQRWindow=Großes QR-Code-Fenster öffnen +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=Handelskonto -shared.faq=FAQ-Webseite besuchen +shared.faq=Visit FAQ page shared.yesCancel=Ja, abbrechen shared.nextStep=Nächster Schritt shared.selectTradingAccount=Handelskonto auswählen shared.fundFromSavingsWalletButton=Gelder aus Bisq-Wallet überweisen shared.fundFromExternalWalletButton=Ihre externe Wallet zum Finanzieren öffnen -shared.openDefaultWalletFailed=Das Öffnen einer Bitcoin-Wallet-Standardanwendung schlug fehl. Haben Sie möglicherweise keine installiert? +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=Abstand vom Marktpreis in % shared.belowInPercent=% unter dem Marktpreis shared.aboveInPercent=% über dem Marktpreis shared.enterPercentageValue=%-Wert eingeben shared.OR=ODER -shared.notEnoughFunds=Sie haben nicht genug Gelder in Ihrer Bisq-Wallet.\nSie benötigen {0}, haben aber nur {1} in Ihrer Bisq-Wallet.\n\nFinanzieren Sie diesen Handel bitte von einer externen Bitcoin-Wallet oder finanzieren Sie Ihre Bisq-Wallet unter \"Gelder/Gelder erhalten\". +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=Warte auf Gelder... shared.depositTransactionId=Kautionstransaktions-ID shared.TheBTCBuyer=Der BTC-Käufer @@ -116,22 +116,22 @@ shared.You=Sie shared.reasonForPayment=Verwendungszweck shared.sendingConfirmation=Sende Bestätigung... shared.sendingConfirmationAgain=Bitte senden Sie die Bestätigung erneut -shared.exportCSV=In CSV exportieren +shared.exportCSV=Export to CSV shared.exportJSON=Exportiere als JSON shared.noDateAvailable=Kein Datum verfügbar shared.noDetailsAvailable=Keine Details vorhanden shared.notUsedYet=Noch ungenutzt shared.date=Datum shared.sendFundsDetailsWithFee=Gesendet: {0}\nVon Adresse: {1}\nAn Empfangsadresse: {2}.\nBenötigte Mining-Gebühr ist: {3} ({4} Satoshis/Byte)\nTransaktionsgröße: {5} Kb\n\nDer Empfänger erhält: {6}\n\nSind Sie sicher, dass Sie diesen Betrag abheben wollen? -shared.sendFundsDetailsDust=Bisq hat festgestellt, dass diese Transaktion eine Wechselgeldausgabe erzeugen würde, die unter dem Dust-Mindestgrenzwert liegt (und nach den Bitcoin-Konsensregeln nicht zulässig ist). Stattdessen werden diese Dust ({0} Satoshi{1}) der Mining-Gebühr hinzugefügt.\n\n\n +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=In Zwischenablage kopieren shared.language=Sprache shared.country=Land shared.applyAndShutDown=Anwenden und herunterfahren shared.selectPaymentMethod=Zahlungsmethode wählen -shared.accountNameAlreadyUsed=Dieser Kontoname wird bereits in einem gespeicherten Konto verwendet.\nBitte nutzen Sie einen anderen Namen. +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=Möchten Sie das ausgewählte Konto wirklich löschen? -shared.cannotDeleteAccount=Sie können dieses Konto nicht löschen, da es in einem offenen Angebot oder Handel gebraucht wird. +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=Es wurden noch keine Konten eingerichtet shared.manageAccounts=Konten verwalten shared.addNewAccount=Neues Konto hinzufügen @@ -214,7 +214,8 @@ shared.mediator=Mediator shared.arbitrator=Vermittler shared.refundAgent=Vermittler shared.refundAgentForSupportStaff=Rückerstattungsbeauftragten -shared.delayedPayoutTxId=Rückerstattung von Sicherheiten Transaktions-ID +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=Sie haben im Moment zu viele unbestätigte Transaktionen. Bitte versuchen Sie es später noch einmal. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=Als Banksitz akzeptierte Länder (Abnehmer): offerbook.availableOffers=Verfügbare Angebote offerbook.filterByCurrency=Nach Währung filtern offerbook.filterByPaymentMethod=Nach Zahlungsmethode filtern -offerbook.timeSinceSigning=Zeit seit der Unterzeichnung +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=Dieses Konto wurde verifiziert und {0} offerbook.timeSinceSigning.info.arbitrator=von einem Vermittler unterzeichnet und kann Partner-Konten unterzeichnen offerbook.timeSinceSigning.info.peer=von einem Partner unterzeichnet, der darauf wartet, dass die Limits aufgehoben werden @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=vom Partner unterzeichnet und kann Partne offerbook.timeSinceSigning.info.banned=Konto wurde geblockt offerbook.timeSinceSigning.daysSinceSigning={0} Tage offerbook.timeSinceSigning.daysSinceSigning.long={0} seit der Unterzeichnung +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=Wenn Sie einen Trade mit einem Partner erfolgreich abschließen, der ein unterzeichnetes Zahlungskonto hat, wird Ihr Zahlungskonto unterzeichnet.\n{0} Tage später wird das anfängliche Limit von {1} aufgehoben und Ihr Konto kann die Zahlungskonten anderer Partner unterzeichnen. offerbook.timeSinceSigning.notSigned=Noch nicht unterzeichnet @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=Deaktivieren des Angebots fehlgeschlagen:\n{0} offerbook.activateOffer.failed=Veröffentlichung des Angebots fehlgeschlagen:\n{0} offerbook.withdrawFundsHint=Sie können die eingezahlten Gelder im {0}-Bildschirm abheben. -offerbook.warning.noTradingAccountForCurrency.headline=Kein Handelskonto für die ausgewählte Währung vorhanden -offerbook.warning.noTradingAccountForCurrency.msg=Sie haben kein Handelskonto für die ausgewählte Währung.\nMöchten Sie ein Angebot mit einem Ihrer vorhanden Handelskonten erstellen? -offerbook.warning.noMatchingAccount.headline=Kein passendes Handelskonto vorhanden. -offerbook.warning.noMatchingAccount.msg=Um dieses Angebot anzunehmen, müssen Sie ein Zahlungskonto für diese Zahlungsmethode erstellen.\n\nMöchten Sie dies jetzt tun? +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=Dieses Angebot kann aufgrund von Handelsbeschränkungen der Gegenpartei nicht angenommen werden @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=Die in diesem Handel verwendete Zahlungsme offerbook.warning.nodeBlocked=Die Onion-Adresse dieses Händlers wurde von den Bisq-Entwicklern blockiert.\nWahrscheinlich gibt es einen unbehobenen Bug, der Probleme beim Annehmen von Angeboten dieses Händlers verursacht. offerbook.warning.requireUpdateToNewVersion=Ihre Bisq Version ist nicht mehr zum Handeln kompatible.\nBitte updaten Sie auf die neuste Bisqversion von https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=Ihr Zahlungskonto wurde vor {0} erstellt. Ihr Trade-Limit basiert auf dem Kontoalter und ist für dieses Angebot nicht ausreichend.\n\nIhr Trade-Limit ist: {1}\nDer Trade-Mindestbetrag des Angebots ist: {2}.\n\nSie können dieses Angebot im Moment nicht annehmen. Sobald Ihr Konto älter als 2 Monate ist, wird diese Einschränkung aufgehoben. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=Sie verkaufen zum aktuellen Marktpreis (jede Minute aktualisiert). offerbook.info.buyAtMarketPrice=Sie kaufen zum aktuellen Marktpreis (jede Minute aktualisiert). @@ -541,7 +543,7 @@ portfolio.tab.history=Verlauf portfolio.tab.failed=Fehlgeschlagen portfolio.tab.editOpenOffer=Angebot bearbeiten -portfolio.pending.invalidDelayedPayoutTx=Bitte senden Sie NICHT die Altcoin- oder Fiat-Zahlung, sondern kontaktieren Sie die Bisq-Entwickler unter 'https://bisq.community' oder über den Keybase-Kanal.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=Auf Blockchain-Bestätigung warten portfolio.pending.step2_buyer.startPayment=Zahlung beginnen @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=Auf Zahlungseingang warten portfolio.pending.step3_seller.confirmPaymentReceived=Zahlungseingang bestätigen portfolio.pending.step5.completed=Abgeschlossen +portfolio.pending.step3_seller.autoConf.status.label=Selbstbestätigung Status +portfolio.pending.autoConf=Selbstbestätigt +portfolio.pending.autoConf.blocks=XMR Bestätigungen: {0} / Benötigt: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaktionsschlüssel erneut benutzt. Bitte eröffnen Sie einen Disput. +portfolio.pending.autoConf.state.confirmations=XMR Bestätigungen: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaktion ist noch nicht im Mem-Pool sichtbar +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=ID / Schlüssel für Transaktion ist ungültig +portfolio.pending.autoConf.state.filterDisabledFeature=Von Entwicklern deaktiviert. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Selbstbestätigungsfunktion ist deaktiviert. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Handelsbetrag überschreitet Limit für Selbstbestätigung +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer hat ungültige Daten angegeben. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Die Auszahlungstransaktion wurde bereits veröffentlicht. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Der Disput wurde eröffnet. Die Selbstbestätigung ist für diesen Trade deaktiviert. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaktionsnachweis-Anforderungen gestartet +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Erfolgsergebnisse: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Beweisführung bei allen Diensten gelungen +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=Die Kautionstransaktion wurde veröffentlicht.\n{0} muss auf wenigstens eine Blockchain-Bestätigung warten, bevor die Zahlung beginnt. portfolio.pending.step1.warn=Die Kautionstransaktion ist noch nicht bestätigt. Dies geschieht manchmal in seltenen Fällen, wenn die Finanzierungsgebühr aus der externen Wallet eines Traders zu niedrig war. portfolio.pending.step1.openForDispute=Die Kautionstransaktion ist noch nicht bestätigt. Sie können länger warten oder den Vermittler um Hilfe bitten. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=Bitte besuchen Sie Ihre Online-Banking-Websit # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Bitte kontaktieren Sie den BTC-Verkäufer, mit den bereitgestellten Daten und organisieren Sie ein Treffen um {0} zu zahlen.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Zahlung per {0} beginnen +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=Zu überweisender Betrag portfolio.pending.step2_buyer.sellersAddress={0}-Adresse des Verkäufers portfolio.pending.step2_buyer.buyerAccount=Ihr zu verwendendes Zahlungskonto @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Einige Banken könnte portfolio.pending.step2_buyer.confirmStart.headline=Bestätigen Sie, dass Sie die Zahlung begonnen haben portfolio.pending.step2_buyer.confirmStart.msg=Haben Sie die {0}-Zahlung an Ihren Handelspartner begonnen? portfolio.pending.step2_buyer.confirmStart.yes=Ja, ich habe die Zahlung begonnen - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=Auf Zahlung warten portfolio.pending.step2_seller.f2fInfo.headline=Kontaktinformation des Käufers portfolio.pending.step2_seller.waitPayment.msg=Die Kautionstransaktion hat mindestens eine Blockchain-Bestätigung.\nSie müssen warten bis der BTC-Käufer die {0}-Zahlung beginnt. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=Zu erhaltender Betrag portfolio.pending.step3_seller.yourAddress=Ihre {0}-Adresse portfolio.pending.step3_seller.buyersAddress={0}-Adresse des Käufers portfolio.pending.step3_seller.yourAccount=Ihr Handelskonto -portfolio.pending.step3_seller.buyersAccount=Handelskonto des Käufers +portfolio.pending.step3_seller.xmrTxHash=Transaktions-ID +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=Zahlungserhalt bestätigen portfolio.pending.step3_seller.buyerStartedPayment=Der BTC-Käufer hat die {0}-Zahlung begonnen.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Überprüfen Sie Ihre Altcoin-Wallet oder Ihren Block-Explorer auf Blockchain-Bestätigungen und bestätigen Sie die Zahlung, wenn ausreichend viele Blockchain-Bestätigungen angezeigt werden. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Bitte verwenden Sie diese Funktion nur i portfolio.pending.timeLockNotOver=Sie müssen ≈{0} ({1} weitere Blöcke) warten, bevor Sie einen Vermittlungskonflikt eröffnen können. portfolio.pending.error.depositTxNull=Die Einzahlungstransaktion ist null. Sie können einen Streitfall nicht ohne eine gültige Einzahlungstransaktion eröffnen. Bitte gehen Sie zu \"Einstellungen/Netzwerkinformationen\" und führen Sie eine SPV-Resynchronisierung durch.\n\nFür weitere Hilfe wenden Sie sich bitte an den Bisq-Support-Kanal des Bisq Keybase Teams. -portfolio.pending.mediationResult.error.depositTxNull=Die Einzahlungstransaktion ist Null. Der Trade wird in den Abschnitt für fehlgeschlagene Trades verschoben. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=Die verzögerte Auszahlungstransaktion ist null. Der Trade wird in den Bereich der fehlgeschlagenen Trades verschoben. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=Die Einzahlungstransaktion ist bestätigt. Sie können einen Vermittlungsfall nicht ohne eine bestätigte Einzahlungstransaktion eröffnen. Bitte warten Sie, bis sie bestätigt ist, oder gehen Sie zu \"Einstellungen/Netzwerkinformationen\" und führen Sie eine SPV-Resynchronisierung durch.\n\nFür weitere Hilfe wenden Sie sich bitte an den Bisq-Support-Kanal des Bisq Keybase Teams. portfolio.pending.notification=Benachrichtigung @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=Lösungsvorschlag ansehen portfolio.pending.mediationResult.popup.headline=Mediationsergebnis für Trade mit ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Ihr Trade-Partner hat den Vorschlag des Mediators akzeptiert für Trade {0} portfolio.pending.mediationResult.popup.info=Der Mediator hat die folgende Auszahlung vorgeschlagen:\nSie erhalten: {0}\nIhr Trading-Partner erhält: {1}\n\nSie können diese vorgeschlagene Auszahlung akzeptieren oder ablehnen.\n\nMit der Annahme unterzeichnen Sie die vorgeschlagene Auszahlungstransaktion. Wenn Ihr Trading-Partner auch akzeptiert und unterzeichnet, wird die Auszahlung abgeschlossen und der Trade geschlossen.\n\nWenn einer oder beide von Ihnen den Vorschlag ablehnen, müssen Sie bis {2} (Block {3}) warten, um einen Konflikt in der zweiten Runde mit einem Vermittler zu eröffnen, der den Fall erneut untersuchen und eine Auszahlung auf der Grundlage seiner Erkenntnisse vornehmen wird.\n\nDer Vermittler kann eine geringe Gebühr (Gebühr maximal: die Kaution des Traders) als Entschädigung für seine Arbeit erheben. Der Idealfall wäre, dass beide Trader dem Vorschlag des Mediators zustimmen, da das Schlichtungsverfahren für außergewöhnliche Umstände gedacht ist, z.B. wenn ein Trader sicher ist, dass der Mediator keinen fairen Auszahlungsvorschlag gemacht hat (oder wenn der andere Trader nicht reagiert).\n\nMehr Details zum neuen Vermittlungsmodell:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Ablehnen und Vermittler hinzuziehen portfolio.pending.mediationResult.popup.alreadyAccepted=Sie haben bereits akzeptiert +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=Abgeschlossen portfolio.closed.ticketClosed=Vermittelt portfolio.closed.mediationTicketClosed=Mediiert @@ -763,8 +817,8 @@ portfolio.closed.canceled=Abgebrochen portfolio.failed.Failed=Fehlgeschlagen portfolio.failed.unfail=Bevor Sie fortfahren, stellen Sie sicher, dass Sie ein Backup Ihres Datenverzeichnisses haben!\nWollen Sie diesen Trade wieder in offene Trades verschieben?\nDies ist eine Möglichkeit, Gelder freizugeben, die in einem gescheiterten Trade stecken geblieben sind. portfolio.failed.cantUnfail=Dieser Trade kann im Moment nicht wieder in offene Trades verschoben werden. \nVersuchen Sie es nach Abschluss des/der Trades erneut {0} -portfolio.failed.depositTxNull=Der Trade kann nicht abgeschlossen werden. Die Einzahlungstransaktion ist null -portfolio.failed.delayedPayoutTxNull=Der Trade kann nicht abgeschlossen werden. Die verzögerte Auszahlungstransaktion ist null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Legacy-Vermittlung support.tab.ArbitratorsSupportTickets={0} Tickets support.filter=Search disputes support.filter.prompt=Tragen sie Handel ID, Datum, Onion Adresse oder Kontodaten + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=Private Benachrichtigung senden -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=Private Benachrichtigung +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=Keine offenen Tickets vorhanden support.sendingMessage=Nachricht wird gesendet... support.receiverNotOnline=Empfänger ist nicht online. Nachricht wird in der Mailbox gespeichert. @@ -905,6 +970,9 @@ support.peerOpenedDispute=Ihr Trading-Partner hat einen Konflikt eröffnet.\n\n{ support.peerOpenedDisputeForMediation=Ihr Trading-Partner hat eine Mediation beantragt.\n\n{0}\n\nBisq-Version: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Node-Adresse des Mediators: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=Netzwerk-Info settings.tab.about=Über setting.preferences.general=Allgemeine Voreinstellungen -setting.preferences.explorer=Bitcoin-Block-Explorer -setting.preferences.explorer.bsq=BSQ Block Explorer +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Max. Abweichung vom Marktpreis setting.preferences.avoidStandbyMode=Standby Modus verhindern +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=Werte größer als {0}% sind nicht erlaubt. setting.preferences.txFee=Transaktionsgebühr zum Abheben (Satoshi/Byte) setting.preferences.useCustomValue=Spezifischen Wert nutzen @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Öffnen Sie das Notfallwerkze setting.about.shortcuts.showTorLogs=Umschalten des Log-Levels für Tor-Meldungen zwischen DEBUG und WARN -setting.about.shortcuts.showDisputeStatistics=Zusammenfassung aller Konflikte anzeigen -setting.about.shortcuts.showDisputeStatistics.value=Navigieren Sie zu Konflikte und drücken Sie: {0} - setting.about.shortcuts.manualPayoutTxWindow=Fenster öffnen für manuelle Auszahlung einer 2von2 Multisig Einzahlung tx setting.about.shortcuts.reRepublishAllGovernanceData=Neu veröffentlichen von DAO Governance-Daten (Vorschläge, Abstimmungen) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navigieren Sie zu Konto und dr setting.about.shortcuts.registerMediator=Mediator registrieren (nur Mediator/Vermittler) setting.about.shortcuts.registerMediator.value=Navigieren Sie zu Konto und drücken Sie: {0} -setting.about.shortcuts.reOpenDispute=Geschlossenen Konfliktfall erneut öffnen (nur Mediator/Vermittler) -setting.about.shortcuts.reOpenDispute.value=Wählen Sie den geschlossenen Konfliktfall und drücken Sie: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Öffnen Sie das Fenster für Kontoalter-Unterzeichnung (nur Legacy-Vermittler) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navigieren Sie zur Legacy-Vermittler-Ansicht und drücken Sie: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Warnung oder Update per Nachricht senden (p setting.about.shortcuts.sendFilter=Filter einstellen (privilegierte Aktivität) setting.about.shortcuts.sendPrivateNotification=Private Benachrichtigung an Peer senden (privilegierte Aktivität) -setting.about.shortcuts.sendPrivateNotification.value=Öffnen Sie die Peer-Info unter Avatar oder Konflikt und drücken Sie: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Bitte stellen Sie sicher, dass Sie die Anforder account.altcoin.popup.wallet.confirm=Ich verstehe und bestätige, dass ich weiß, welche Wallet ich benutzen muss. account.altcoin.popup.upx.msg=Der Handel mit UPX auf Bisq setzt voraus, dass Sie die folgenden Anforderungen verstehen und erfüllen:\n\nFür das Senden von UPX müssen Sie entweder das offizielle uPlexa GUI-Wallet oder das uPlexa CLI-Wallet mit aktiviertem store-tx-info Flag verwenden (Standard in neuen Versionen). Bitte stellen Sie sicher, dass Sie auf den tx key zugreifen können, da dies bei einem Konfliktfall erforderlich wäre.\nuplexa-wallet-cli (verwenden Sie den Befehl get_tx_key)\nuplexa-wallet-gui (gehen Sie zum History Tab und klicken Sie auf (P) für den Zahlungsnachweis)\n\nBei normalen Blockexplorern ist der Transfer nicht verifizierbar.\n\nSie müssen dem Vermittler im Konfliktfall die folgenden Daten zur Verfügung stellen:\n- Der tx Private Key\n- Der Transaktionshash\n- Die öffentliche Adresse des Empfängers\n\nWenn Sie die oben genannten Daten nicht angeben oder wenn Sie eine inkompatible Wallet verwendet haben, verlieren Sie den Konfliktfall. Der UPX-Sender ist dafür verantwortlich, im Konfliktfall dem Vermittler die Verifizierung des UPX-Transfers nachzuweisen.\n\nEs ist keine Zahlungs-ID erforderlich, sondern nur die normale öffentliche Adresse.\nWenn Sie sich über diesen Prozess nicht sicher sind, besuchen Sie den uPlexa discord channel (https://discord.gg/vhdNSrV) oder den uPlexa Telegram Chat (https://t.me/uplexaOfficial), um weitere Informationen zu erhalten. account.altcoin.popup.arq.msg=Der Handel mit ARQ auf Bisq setzt voraus, dass Sie die folgenden Anforderungen verstehen und erfüllen:\n\nFür den Versand von ARQ müssen Sie entweder das offizielle ArQmA GUI-Wallet oder das ArQmA CLI-Wallet mit aktiviertem store-tx-info Flag verwenden (Standard in neuen Versionen). Bitte stellen Sie sicher, dass Sie auf den tx Key zugreifen können, da dies im Falle eines Konfliktes erforderlich wäre.\narqma-wallet-cli (verwenden Sie den Befehl get_tx_key)\narqma-wallet-gui (gehen Sie zur History Tab und klicken Sie auf (P) für den Zahlungsnachweis)\n\nBei normalen Blockexplorern ist der Transfer nicht verifizierbar.\n\nSie müssen dem Mediator oder Vermittler im Konfliktfall die folgenden Daten zur Verfügung stellen:\n- Der tx Private Key\n- Der Transaktionshash\n- Die öffentliche Adresse des Empfängers\n\nWenn Sie die oben genannten Daten nicht angeben oder wenn Sie eine inkompatible Wallet verwendet haben, verlieren Sie den Konfliktfall. Der ARQ-Sender ist im Fall eines Konflikts dafür verantwortlich, die Verifizierung des ARQ-Transfers dem Mediator oder Vermittler nachzuweisen.\n\nEs ist keine Zahlungs-ID erforderlich, sondern nur die normale öffentliche Adresse.\nWenn Sie sich über diesen Prozess nicht sicher sind, besuchen Sie den ArQmA Discord Channel (https://discord.gg/s9BQpJT) oder das ArQmA Forum (https://labs.arqma.com), um weitere Informationen zu erhalten. -account.altcoin.popup.xmr.msg=Der Handel mit XMR auf Bisq setzt voraus, dass Sie die folgenden Anforderungen verstehen und erfüllen:\n\nNachweis von Zahlungen: Da es sich bei Monero um eine Private Coin handelt, sind einige Transaktionsdetails in der Blockchain nicht öffentlich zugänglich, und im Streitfall benötigt der Mediator oder Vermittler diese, um zu überprüfen, ob die Transaktion tatsächlich durchgeführt wurde. Auf Bisq ist der Absender der XMR-Transaktion für die Bereitstellung dieser Informationen an den Mediator oder Vermittler im Streitfall verantwortlich. Um dies zu tun, müssen Sie XMR mit einer Wallet versenden, die die erforderlichen Informationen enthält, um zu beweisen, dass die Zahlung erfolgt ist, einschließlich:\n\n- Transaktionsschlüssel (Tx Key, Tx Secret Key oder Tx Private Key)\n- Transaktions-ID (Tx ID oder Tx Hash)\n- Zieladresse (Empfängeradresse)\n\nDiese Informationen finden Sie in den offiziellen Monero GUI & CLI Wallets, MyMonero und Exodus (Desktop) sowie in Cake Wallet, MyMonero und Monerujo (Mobile) an den folgenden Orten:\n\n- Monero GUI: Im Transaktionen Tab\n- Monero CLI: Verwenden Sie den Befehl get_tx_key TXID. Das Flag store-tx-info muss aktiviert sein (in neuen Versionen standardmäßig aktiviert).\n- Andere Wallets: Gehen Sie zum Transaktionsverlauf und suchen Sie nach dem Transaktionsschlüssel (Tx Key oder Secret Key) und der Zieladresse in einer gesendeten Transaktion. Die Option Empfängeradresse speichern muss in den Einstellungen der Cake Wallet aktiviert sein.\n\nWenn Sie eine andere Wallet als die oben genannten verwenden, stellen Sie bitte sicher, dass Sie auf diese drei Informationen zugreifen können. Da der Transaktionsschlüssel und die Zieladresse in der Monero Wallet Software gespeichert sind und nicht in der Monero Blockchain wiederhergestellt werden können, sollten Sie Ihre Monero Wallet niemals löschen oder wiederherstellen, bevor ein Trade auf Bisq abgeschlossen ist. Wenn Sie die oben genannten Daten nicht angeben, verlieren Sie den Streitfall.\n\nÜberprüfen von Zahlungen: Mit diesen drei Informationen kann die Überprüfung, ob ein bestimmter Betrag in Monero an eine bestimmte Adresse geschickt wurde, auf folgende Weise durchgeführt werden:\n\n- Monero GUI: Wechseln Sie in den erweiterten Modus und wählen Sie Advanced > Prove/check > Check Transaction.\n- Monero CLI: verwenden Sie den Befehl check_tx_key TXID TXKEY ADDRESS\n- XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Explore Monero Website (https://www.exploremonero.com/receipt)\n\nWenn Sie sich über diesen Prozess weiterhin nicht sicher sind, besuchen Sie (https://www.getmonero.org/resources/user-guides/prove-payment.html), um weitere Informationen zu erhalten oder stellen Sie eine Frage im Monero Support Subreddit (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Der Handel mit MSR auf Bisq setzt voraus, dass Sie die folgenden Anforderungen verstehen und erfüllen:\n\nFür den Versand von MSR müssen Sie entweder das offizielle Masari GUI Wallet, das Masari CLI Wallet mit dem aktivierten store-tx-info Flag (standardmäßig aktiviert) oder das Masari Web Wallet (https://wallet.getmasari.org) verwenden. Bitte stellen Sie sicher, dass Sie auf den tx Key zugreifen können, da dies im Falle eines Konfliktes erforderlich wäre.\nmasari-wallet-cli (verwenden Sie den Befehl get_tx_key)\nmasari-wallet-gui (gehen Sie zur History Tab und klicken Sie auf (P) für den Zahlungsnachweis).\n\nMasari Web Wallet (gehen Sie zum Konto -> Transaktionshistorie und lassen Sie Details zu Ihrer gesendeten Transaktion anzeigen)\n\nDie Verifizierung kann im Wallet durchgeführt werden.\nmasari-wallet-cli : mit dem Befehl (check_tx_key).\nmasari-wallet-gui : auf der Seite Advanced > Prove/Check.\nDie Verifizierung kann im Block-Explorer durchgeführt werden. \nÖffnen Sie den Block-Explorer (https://explorer.getmasari.org), verwenden Sie die Suchleiste, um Ihren Transaktionshash zu finden.\nSobald die Transaktion gefunden wurde, scrollen Sie nach unten zum Bereich 'Prove Sending' und geben Sie bei Bedarf Details ein.\nSie müssen dem Mediator oder Vermittler im Konfliktfall die folgenden Daten zur Verfügung stellen:\n- Den tx Private Key\n- Den Transaktionshash\n- Die öffentliche Adresse des Empfängers\n\nWenn Sie die oben genannten Daten nicht angeben oder wenn Sie eine inkompatible Wallet verwendet haben, verlieren Sie den Konfliktfall. Der MSR-Sender ist im Fall eines Konflikts dafür verantwortlich, die Verifizierung des MSR-Transfers dem Mediator oder Vermittler nachzuweisen.\n\nEs ist keine Zahlungs-ID erforderlich, sondern nur die normale öffentliche Adresse.\nWenn Sie sich über diesen Prozess nicht sicher sind, fragen Sie um Hilfe auf der offiziellen Masari Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Der Handel mit BLUR auf Bisq setzt voraus, dass Sie die folgenden Anforderungen verstehen und erfüllen:\n\nUm BLUR zu senden, müssen Sie die Blur Network CLI oder GUI Wallet verwenden. \n\nWenn Sie die CLI-Wallet verwenden, wird nach dem Senden eines Transfers ein Transaktionshash (tx ID) angezeigt. Sie müssen diese Informationen speichern. Unmittelbar nach dem Senden des Transfers müssen Sie den Private Key der Transaktion mit dem Befehl 'get_tx_key' ermitteln. Wenn Sie diesen Schritt nicht ausführen, können Sie den Key möglicherweise später nicht mehr abrufen. \n\nWenn Sie das Blur Network GUI Wallet verwenden, können Sie problemlos den Private Key der Transaktion und die Transaktion-ID im "History" Tab finden. Suchen Sie sofort nach dem Absenden die Transaktion, die von Interesse ist. Klicken Sie auf das Symbol "?" in der unteren rechten Ecke des Feldes, das die Transaktion enthält. Sie müssen diese Informationen speichern. \n\nFalls ein Vermittlungsverfahren erforderlich ist, müssen Sie einem Mediator oder Vermittler Folgendes vorlegen: 1.) die Transaktions-ID, 2.) den Private Key der Transaktion und 3.) die Adresse des Empfängers. Der Mediator oder Vermittler überprüft dann den BLUR-Transfer mit dem Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nWenn Sie dem Mediator oder Vermittler nicht die erforderlichen Informationen zur Verfügung stellen, verlieren Sie den Konfliktfall. In allen Konfliktfällen trägt der BLUR-Sender 100% der Verantwortung für die Verifizierung von Transaktionen gegenüber einem Mediator oder Vermittler. \n\nWenn Sie diese Anforderungen nicht verstehen, handeln Sie nicht auf Bisq. Als Erstes suchen Sie Hilfe im Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Preis-Node Betreiber # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin-Node Betreiber # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Märkte-API Betreiber +dao.bond.bondedRoleType.MARKETS_OPERATOR=Betreiber der Handelsstatistik Webseite # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Betreiber des BSQ Explorers +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile Benachrichtigung Weiterleitung Betreiber # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=Zusammenfassende Anmerkungen disputeSummaryWindow.addSummaryNotes=Zusammenfassende Anmerkungen hinzufügen disputeSummaryWindow.close.button=Ticket schließen -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nNächste Schritte:\nTrade öffnen und Empfehlung des Mediators akzeptieren oder ablehnen -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nNächste Schritte:\nEs sind keine weiteren Schritte Ihrerseits erforderlich. Wenn der Vermittler sich zu Ihren Gunsten entschieden hat, sehen Sie unter Fonds/Transaktionen eine Transaktion "Rückerstattung aus dem Vermittlungsverfahren" + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=Sie müssen auch das Ticket des Handelspartners schließen! disputeSummaryWindow.close.txDetails.headline=Rückerstattungstransaktion veröffentlichen disputeSummaryWindow.close.txDetails.buyer=Käufer erhält {0} an Adresse: {1}\n disputeSummaryWindow.close.txDetails.seller=Verkäufer erhält {0} an Adresse: {1}\n disputeSummaryWindow.close.txDetails=Ausgaben: {0}\n{1}{2}Transaktionsgebühr: {3} ({4} Satoshis/Byte)\nTransaktionsgröße: {5} Kb\n\nSind Sie sicher, dass Sie diese Transaktion veröffentlichen möchten? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} Notfall-Wallets-Werkzeug emptyWalletWindow.info=Bitte nur in Notfällen nutzen, wenn Sie vom UI aus nicht auf Ihre Gelder zugreifen können.\n\nBeachten Sie bitte, dass alle offenen Angebote geschlossen werden, wenn Sie dieses Werkzeug verwenden.\n\nErstellen Sie ein Backup Ihres Dateiverzeichnisses, bevor Sie dieses Werkzeug verwenden. Dies können Sie unter \"Konto/Backup\" tun.\n\nBitte melden Sie uns das Problem und erstellen Sie einen Fehlerbericht auf GitHub oder im Bisq-Forum, damit wir feststellen können, was das Problem verursacht hat. emptyWalletWindow.balance=Ihr verfügbares Wallets-Guthaben @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=Gefilterte Preisrelais Knoten (Komma getr. Onion-Adr filterWindow.btcNode=Gefilterte Bitcoinknoten (Komma getr. Adresse + Port) filterWindow.preventPublicBtcNetwork=Nutzung des öffentlichen Bitcoin-Netzwerks verhindern filterWindow.disableDao=DAO deaktivieren +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Min. für DAO erforderliche Version filterWindow.disableTradeBelowVersion=Min. zum Handeln erforderliche Version filterWindow.add=Filter hinzufügen @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=Bestätigen: Angebot annehmen Bitcoin zu {0} offerDetailsWindow.creationDate=Erstellungsdatum offerDetailsWindow.makersOnion=Onion-Adresse des Erstellers -qRCodeWindow.headline=QR-Code -qRCodeWindow.msg=Bitte nutzen Sie diesen QR-Code zum Finanzieren Ihrer Bisq-Wallet von einer externen Wallet. +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=Zahlungsanfrage:\n{0} selectDepositTxWindow.headline=Kautionstransaktion für Konflikt auswählen @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=Private Benachrichtigung senden showWalletDataWindow.walletData=Wallet-Daten showWalletDataWindow.includePrivKeys=Private Schlüssel einbeziehen +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=Nutzervereinbarung @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=Die Einzahlungstransaktion des geschlo error.closedTradeWithNoDepositTx=Die Einzahlungstransaktion des geschlossenen Trades mit der Trade-ID {0} ist null.\n\nBitte starten Sie die Anwendung neu, um die Liste der geschlossenen Trades zu bereinigen. popup.warning.walletNotInitialized=Die Wallet ist noch nicht initialisiert +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=Sie verwenden vermutlich die falsche Bisq-Version für diesen Computer.\nDie Architektur Ihres Computers ist: {0}.\nDie installierten Bisq-Binärdateien sind: {1}.\nBitte fahren Sie Bisq herunter und installieren die korrekte Version ({2}). popup.warning.incompatibleDB=Wir haben nicht kompatible Datenbankdateien entdeckt!\n\nDie Datenbankdatei(en) sind mit unserer momentanen Codebasis nicht kompatibel:\n{0}\n\nWir haben ein Backup der beschädigten Datei(en) erstellt und die Standardwerte in eine neue Datenbankversion übertragen.\n\nDas Backup befindet sich in:\n{1}/db/backup_of_corrupted_data.\n\nBitte überprüfen Sie, ob Sie die aktuellste Bisq-Version installiert haben.\nSie können diese hier herunterladen:\nhttps://bisq.network/downloads\n\nBitte starten Sie die Anwendung neu. popup.warning.startupFailed.twoInstances=Bisq läuft bereits. Sie können nicht zwei Instanzen von Bisq laufen lassen. @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=Ihre BSQ-Wallet hat keine ausreichenden popup.warning.messageTooLong=Ihre Nachricht überschreitet die maximal erlaubte Größe. Sende Sie diese in mehreren Teilen oder laden Sie sie in einen Dienst wie https://pastebin.com hoch. popup.warning.lockedUpFunds=Sie haben gesperrtes Guthaben aus einem gescheiterten Trade.\nGesperrtes Guthaben: {0} \nEinzahlungs-Tx-Adresse: {1}\nTrade ID: {2}.\n\nBitte öffnen Sie ein Support-Ticket, indem Sie den Trade im Bildschirm "Offene Trades" auswählen und auf \"alt + o\" oder \"option + o\" drücken. -popup.warning.nodeBanned=Einer der {0} Knoten wurde gebannt. Bitte starten Sie die Anwendung neu, um sicher zu sein, nicht mit gebannten Knoten verbunden zu werden. +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=Preisrelais popup.warning.seed=Seed popup.warning.mandatoryUpdate.trading=Bitte aktualisieren Sie auf die neueste Bisq-Version. Es wurde ein obligatorisches Update veröffentlicht, das den Handel mit alten Versionen deaktiviert. Bitte besuchen Sie das Bisq-Forum für weitere Informationen. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=Um sicherzustellen, dass beide Händler dem Hande popup.info.cashDepositInfo=Stellen Sie sicher, dass eine Bank-Filiale in Ihrer Nähe befindet, um die Bargeld Kaution zu zahlen.\nDie Bankkennung (BIC/SWIFT) der Bank des Verkäufers ist: {0}. popup.info.cashDepositInfo.confirm=Ich bestätige, dass ich die Kaution zahlen kann popup.info.shutDownWithOpenOffers=Bisq wird heruntergefahren, aber Sie haben offene Angebote verfügbar.\n\nDiese Angebote sind nach dem Herunterfahren nicht mehr verfügbar und werden erneut im P2P-Netzwerk veröffentlicht wenn Sie das nächste Mal Bisq starten.\n\nLassen Sie Bisq weiter laufen und stellen Sie sicher, dass Ihr Computer online bleibt, um Ihre Angebote verfügbar zu halten (z.B.: verhindern Sie den Standby-Modus... der Standby-Modus des Monitors stellt kein Problem dar). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=Wichtige private Benachrichtigung! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: Ein dezentrales Bitcoin-Tauschbörsen-Netzwerk # GUI Util #################################################################### -guiUtil.miningFeeInfo=Bitte stellen Sie sicher, dass die Transaktionsgebühr Ihrer externen Wallet mindestens {0} Satoshi/Byte ist. Andernfalls kann die Handelstransaktion nicht bestätigt werden und der Handel würde in einem Konflikt enden. +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=Handelskonten in Verzeichnis gespeichert:\n{0} guiUtil.accountExport.noAccountSetup=Sie haben kein Handelskonto zum Exportieren eingerichtet. @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=Zahlungskonto vor {0} erstellt. peerInfoIcon.tooltip.unknownAge=Alter des Zahlungskontos unbekannt. tooltip.openPopupForDetails=Dialogfenster für Details öffnen +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=Externen Blockchain-Explorer für Adresse öffnen: {0} tooltip.openBlockchainForTx=Externen Blockchain-Explorer für Transaktion öffnen: {0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=Öffnen einer Bitcoin-Wallet-Standardanwendun peerInfoIcon.tooltip={0}\nMarkierung: {1} txIdTextField.copyIcon.tooltip=Transaktions-ID in Zwischenablage kopieren -txIdTextField.blockExplorerIcon.tooltip=Einen Blockchain-Explorer mit dieser Transaktions-ID öffnen +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=Einen Blockchain-Explorer mit dieser Tra navigation.account=\"Konto\" navigation.account.walletSeed=\"Konto/Wallet-Seed\" -navigation.funds.availableForWithdrawal=\"Gelder/Gelder senden\" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"Portfolio/Meine offenen Angebote\" navigation.portfolio.pending=\"Portfolio/Offene Trades\" navigation.portfolio.closedTrades=\"Portfolio/Verlauf\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=Ihre Wallets sind verschlüsselt.\n\nNach einer Wi seed.warn.walletDateEmpty=Da Sie kein Wallet-Datum angegeben haben, muss bisq die Blockchain ab 09.10.2013 (dem Datum der BIP39-Epoche) scannen.\n\nBIP39-Wallets wurden in bisq erstmals am 28.06.2017 (Release v0.5) eingeführt. Sie könnten also durch die Verwendung dieses Datums Zeit sparen.\n\nIdealerweise sollten Sie das Datum angeben, an dem Ihr Wallet Seed erstellt wurde.\n\n\nSind Sie sicher, dass Sie ohne Angabe eines Wallet-Datums fortfahren wollen? seed.restore.success=Wallets mit den neuen Seed-Wörtern erfolgreich wiederhergestellt.\n\nSie müssen die Anwendung herunterfahren und neu starten. seed.restore.error=Beim Wiederherstellen der Wallets mit den Seed-Wörtern ist ein Fehler aufgetreten.{0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=Konto payment.account.no=Kontonummer payment.account.name=Kontoname payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=Vollständiger Name des Kontoinhabers payment.account.fullName=Vollständiger Name (vor, zweit, nach) payment.account.state=Bundesland/Landkreis/Region diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 0ee316d1a03..7db7f14200f 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -96,7 +96,7 @@ shared.dontRemoveOffer=No eliminar oferta shared.editOffer=Editar oferta shared.openLargeQRWindow=Abrir código QR en ventana grande shared.tradingAccount=Cuenta de intercambio -shared.faq=Visite el sitio de FAQs +shared.faq=Visitar web preguntas frecuentes shared.yesCancel=Sí, cancelar shared.nextStep=Siguiente paso shared.selectTradingAccount=Selecionar cuenta de intercambio @@ -108,7 +108,7 @@ shared.belowInPercent=% por debajo del precio de mercado shared.aboveInPercent=% por encima del precio de mercado shared.enterPercentageValue=Introduzca valor % shared.OR=ó -shared.notEnoughFunds=No tiene suficientes fondos en su monedero Bisq.\nNecesita {0} pero tiene sólo {1} en su monedero Bisq.\n\nPor favor deposite desde un monedero Bitcoin externo o agregue fondos a su monedero Bisq en \"Fondos/Recibir fondos\". +shared.notEnoughFunds=No tiene suficientes fondos en su monedero bisq para esta transacción. Necesita {0} pero solo tiene {1} disponibles.\n\nPor favor deposite desde un monedero externo o agregue fondos a su monedero Bisq en Fondos > Recibir Fondos. shared.waitingForFunds=Esperando fondos... shared.depositTransactionId=ID de transacción del depósito shared.TheBTCBuyer=El comprador de BTC @@ -129,9 +129,9 @@ shared.language=Idioma shared.country=País shared.applyAndShutDown=Aplicar y cerrar shared.selectPaymentMethod=Seleccionar método de pago -shared.accountNameAlreadyUsed=Ese nombre de cuenta ya está en uso en una cuenta guardada.\nPor favor use otro nombre. +shared.accountNameAlreadyUsed=Ese nombre de cuenta ya está en uso para otra cuenta guardada.\nPor favor use otro nombre. shared.askConfirmDeleteAccount=¿Realmente quiere borrar la cuenta seleccionada? -shared.cannotDeleteAccount=No puede borrar esta cuenta porque está siendo usada en una oferta abierta o en un intercambio. +shared.cannotDeleteAccount=No puede borrar esta cuenta porque está siendo usada en una oferta abierta (o en un intercambio abierto). shared.noAccountsSetupYet=Aún no hay cuentas configuradas. shared.manageAccounts=Gestionar cuentas shared.addNewAccount=Añadir una nueva cuenta @@ -214,7 +214,8 @@ shared.mediator=Mediador shared.arbitrator=Árbitro shared.refundAgent=Árbitro shared.refundAgentForSupportStaff=Agente de devolución de fondos -shared.delayedPayoutTxId=ID de transacción de devolución colateral de fondos +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=Tiene demasiadas transacciones no confirmadas en este momento. Por favor, inténtelo de nuevo más tarde. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=Países de sede de banco aceptados (tomador) offerbook.availableOffers=Ofertas disponibles offerbook.filterByCurrency=Filtrar por moneda offerbook.filterByPaymentMethod=Filtrar por método de pago -offerbook.timeSinceSigning=Tiempo desde el firmado +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=Esta cuenta fue verificada y {0} offerbook.timeSinceSigning.info.arbitrator=firmada por un árbitro y puede firmar cuentas de pares offerbook.timeSinceSigning.info.peer=firmado por un par, esperando a que se eleven los límites @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=firmado por un par y puede firmar cuentas offerbook.timeSinceSigning.info.banned=La cuenta fue bloqueada offerbook.timeSinceSigning.daysSinceSigning={0} días offerbook.timeSinceSigning.daysSinceSigning.long={0} desde el firmado +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=Cuando complete con éxito un intercambio con un par que tenga una cuenta de pago firmada, su cuenta de pago es firmada.\n{0} días después, el límite inicial de {1} se eleva y su cuenta puede firmar tras cuentas de pago. offerbook.timeSinceSigning.notSigned=No firmada aún @@ -374,9 +376,9 @@ offerbook.activateOffer.failed=Fallo en la publicación de la oferta:\n{0} offerbook.withdrawFundsHint=Puede retirar los fondos pagados desde la pantalla {0}. offerbook.warning.noTradingAccountForCurrency.headline=No hay cuenta de intercambio para la moneda seleccionada -offerbook.warning.noTradingAccountForCurrency.msg=No tiene una cuenta de intercambio para la moneda seleccionada.\n¿Desea crear una oferta con una de sus cuentas existentes? -offerbook.warning.noMatchingAccount.headline=La cuenta de intercambio no concuerda. -offerbook.warning.noMatchingAccount.msg=Para aceptar esta oferta, deberá configurar una cuenta de pago utilizando este método de pago.\n\n¿Le gustaría hacerlo ahora? +offerbook.warning.noTradingAccountForCurrency.msg=No tiene una cuenta de pago para la moneda seleccionada.\n¿Desea crear una oferta con otra moneda en su lugar? +offerbook.warning.noMatchingAccount.headline=No La cuenta de pago no concuerda. +offerbook.warning.noMatchingAccount.msg=Esta oferta usa un método de pago que no tiene configurado.\n\n¿Quiere configurar un nuevo método de pago ahora? offerbook.warning.counterpartyTradeRestrictions=Esta oferta no puede tomarse debido a restricciones de intercambio de la contraparte @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=El método de pago utilizado en esta ofert offerbook.warning.nodeBlocked=La dirección onion de este comerciante ha sido bloqueada por los desarrolladores de Bisq.\nProbablemente existe un error de software desatendido que causa problemas al tomar ofertas de este comerciante. offerbook.warning.requireUpdateToNewVersion=Su versión de Bisq ya no es compatible para realizar intercambios.\nPor favor actualice a la última versión de Bisq en https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=Su cuenta de pago ha sido creada hace {0}. Su límite de intercambio está basado en la edad de cuenta y no es suficiente para esta oferta.\n\nSu límite de intercambio es: {1}\nLa cantidad mínima de intercambio para esta oferta es: {2}\n\nNo puede tomar esta oferta por ahora. Una vez que su cuenta sea mayor de 2 meses esta restricción se eliminará. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=Venderá a precio de mercado (actualizado cada minuto). offerbook.info.buyAtMarketPrice=Comprará a precio de mercado (actualizado cada minuto). @@ -541,7 +543,7 @@ portfolio.tab.history=Historial portfolio.tab.failed=Fallidas portfolio.tab.editOpenOffer=Editar oferta -portfolio.pending.invalidDelayedPayoutTx=Por favor NO envíe el pago del altcoin o dinero fiduciario, comuníquese con los desarrolladores de Bisq en 'https://bisq.community' o en el canal de Keybase.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=Esperar a la confirmación en la cadena de bloques portfolio.pending.step2_buyer.startPayment=Comenzar pago @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=Esperar hasta que el pago haya portfolio.pending.step3_seller.confirmPaymentReceived=Confirmar recepción de pago portfolio.pending.step5.completed=Completado +portfolio.pending.step3_seller.autoConf.status.label=Estado de autoconfirmación +portfolio.pending.autoConf=Auto-confirmado +portfolio.pending.autoConf.blocks=Confirmaciones XMR: {0} / Requerido: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Clave de transacción re-utilizada. Por favor, abra una disputa. +portfolio.pending.autoConf.state.confirmations=Confirmaciones XMR: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transacción no vista aún en la mempool +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=ID de transacción no válida / clave de transacción +portfolio.pending.autoConf.state.filterDisabledFeature=Deshabilitado por los desarrolladores + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=La función de autoconfirmación está deshabilitada. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=La cantidad de intercambio excede el límite. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=El par entregó datos inválidos. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=La transacción de pago ya fue publicada. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=La disputa se abrió. La autoconfirmación se ha desactivado para este intercambio. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=La solicitud de prueba de transacción comenzó +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Resultados de éxito: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Prueba con éxito en todos los servicios +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=Ocurrió un error en el servicio solicitado. No es posible la autoconfirmación. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=Un servicio volvió con algún fallo. No es posible la autoconfirmación. + portfolio.pending.step1.info=La transacción de depósito ha sido publicada.\n{0} tiene que esperar al menos una confirmación en la cadena de bloques antes de comenzar el pago. portfolio.pending.step1.warn=La transacción del depósito aún no se ha confirmado.\nEsto puede suceder en raras ocasiones cuando la tasa de depósito de un comerciante desde una cartera externa es demasiado baja. portfolio.pending.step1.openForDispute=La transacción de depósito aún no ha sido confirmada. Puede esperar más o contactar con el mediador para obtener asistencia. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=Por favor vaya a la página web de su banco y # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Por favor contacte al vendedor de BTC con el contacto proporcionado y acuerden un encuentro para pagar {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Comenzar pago utilizando {0} +portfolio.pending.step2_buyer.recipientsAccountData=Receptores {0} portfolio.pending.step2_buyer.amountToTransfer=Cantidad a transferir portfolio.pending.step2_buyer.sellersAddress=Dirección de vendedor {0} portfolio.pending.step2_buyer.buyerAccount=Su cuenta de pago para ser usada @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Algunos bancos pueden portfolio.pending.step2_buyer.confirmStart.headline=Confirme que ha comenzado el pago. portfolio.pending.step2_buyer.confirmStart.msg=¿Ha iniciado el pago de {0} a su par de intercambio? portfolio.pending.step2_buyer.confirmStart.yes=Sí, lo he iniciado. - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=No ha entregado una prueba de pago válida. +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=No ha introducido la transacción de ID y la clave de transacción.\n\nAl no proveer esta información el par no puede usar la autoconfirmación para liberar los BTC en cuanto los XMR se han recibido.\nAdemás de esto, Bisq requiere que el emisor de la transacción XMR sea capaz de entregar esta información al mediador o árbitro en caso de disputa.\nVea más detalles en la wiki de Bisq: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=El valor introducido no es un valor hexadecimal de 32 bytes +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignorar y continuar de todos modos portfolio.pending.step2_seller.waitPayment.headline=Esperar al pago. portfolio.pending.step2_seller.f2fInfo.headline=Información de contacto del comprador portfolio.pending.step2_seller.waitPayment.msg=La transacción del depósito tiene al menos una confirmación en la cadena de bloques.\nTiene que esperar hasta que el comprador de BTC comience el pago de {0}. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=Cantidad a recibir portfolio.pending.step3_seller.yourAddress=Su dirección {0} portfolio.pending.step3_seller.buyersAddress=Dirección {0} del comprador portfolio.pending.step3_seller.yourAccount=Su cuenta de intercambio -portfolio.pending.step3_seller.buyersAccount=Cuenta de intercambio del comprador +portfolio.pending.step3_seller.xmrTxHash=ID de la transacción +portfolio.pending.step3_seller.xmrTxKey=Clave de transacción +portfolio.pending.step3_seller.buyersAccount=Datos de cuenta del comprador portfolio.pending.step3_seller.confirmReceipt=Confirmar recibo de pago portfolio.pending.step3_seller.buyerStartedPayment=El comprador de BTC ha iniciado el pago de {0}.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Compruebe las confirmaciones en la cadena de bloques en su monedero de altcoin o explorador de bloques y confirme el pago cuando tenga suficientes confirmaciones. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Por favor use esta función solo en caso portfolio.pending.timeLockNotOver=Tiene hasta ≈{0} ({1} bloques más) antes de que pueda abrir una disputa de arbitraje. portfolio.pending.error.depositTxNull=La transacción de depósito es inválida. No puede abrir una disputa sin una transacción de depósito válida. Por favor vaya a \"Configuración/Información de red\" y haga una resincronización SPV.\n\nPara obtener ayuda contacte con el equipo de soporte en el canal Bisq de Keybase. -portfolio.pending.mediationResult.error.depositTxNull=El dpósito de transacción es inválido. El intercambio se elimina a la sección de intercambios fallidos. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=La transacción del pago es nula. El intercambio se movió a la sección de intercambios fallidos. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=El depósito de transacción no se ha confirmado. No puede abrir una disputa de arbitraje con una transacción de depósito no confirmada. Por favor espere a que se confirme o vaya a \"Configuración/Información de red\" y haga una resincronización SPV.\n\nPara más ayuda por favor contacte con el equipo de soporte en el canal Bisq de Keybase. portfolio.pending.notification=Notificación @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=Ver resolución propuesta portfolio.pending.mediationResult.popup.headline=Resultado de mediación para el intercambio con ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=El par de intercambio ha aceptado la sugerencia del mediador para el intercmabio {0} portfolio.pending.mediationResult.popup.info=El mediador ha sugerido el siguiente pago:\nUsted recibe: {0}\nEl par de intercambio recibe: {1}\n\nUsted puede aceptar o rechazar esta sugerencia de pago.\n\nAceptándola, usted firma el pago propuesto. Si su par de intercambio también acepta y firma, el pago se completará y el intercambio se cerrará.\n\nSi una o ambas partes rechaza la sugerencia, tendrá que esperar hasta {2} (bloque {3}) para abrir una segunda ronda de disputa con un árbitro que investigará el caswo de nuevo y realizará el pago de acuerdo a sus hallazgos.\n\nEl árbitro puede cobrar una tasa pequeña (tasa máxima: el depósito de seguridad del comerciante) como compensación por su trabajo. Que las dos partes estén de acuerdo es el buen camino, ya que requerir arbitraje se reserva para circunstancias excepcionales, como que un comerciante esté seguro de que el mediador hizo una sugerencia de pago injusta (o que la otra parte no responda).\n\nMás detalles acerca del nuevo modelo de arbitraje:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Rechazar y solicitar arbitraje portfolio.pending.mediationResult.popup.alreadyAccepted=Ya ha aceptado +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=Completado portfolio.closed.ticketClosed=Arbitrada portfolio.closed.mediationTicketClosed=Mediada @@ -763,8 +817,8 @@ portfolio.closed.canceled=Cancelado portfolio.failed.Failed=Fallado portfolio.failed.unfail=Antes de continuar, ¡asegúrese de tener un respaldo de su directorio de datos!\n¿Desea mover este intercambio de nuevo a intercambios abiertos?\nEsta es una forma de desbloquear los fondos retenidos en los intercambios fallidos. portfolio.failed.cantUnfail=Este intercambio no puede ser movido de nuevo a intercambios abiertos en este momento.\nIntente de nuevo después de completar el/los intercambios/s {0} -portfolio.failed.depositTxNull=El intercambio no se pudo completar. La transacción de depósito es nula. -portfolio.failed.delayedPayoutTxNull=El intercambio no se pudo compeltar. La transacción de pago es nula. +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -855,13 +909,24 @@ support.tab.mediation.support=Mediación support.tab.arbitration.support=Arbitraje support.tab.legacyArbitration.support=Legado de arbitraje support.tab.ArbitratorsSupportTickets=Tickets de {0} -support.filter=Search disputes +support.filter=Buscar disputas support.filter.prompt=Introduzca ID de transacción, fecha, dirección onion o datos de cuenta. -support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=Enviar notificación privada -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + +support.reOpenByTrader.prompt=¿Está seguro de que quiere reabrir la disputa? +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=Notificación privada +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=No hay tickets abiertos support.sendingMessage=Enviando mensaje... support.receiverNotOnline=El receptor no está conectado. El mensaje se ha guardado en su bandeja de entrada. @@ -903,8 +968,11 @@ support.youOpenedDisputeForMediation=Ha solicitado mediación\n\n{0}\n\nVersión support.peerOpenedTicket=Su par de intercambio ha solicitado soporte debido a problemas técnicos\n\n{0}\n\nVersión Bisq: {1} support.peerOpenedDispute=Su pareja de intercambio ha solicitado una disputa.\n\n{0}\n\nVersión Bisq: {1} support.peerOpenedDisputeForMediation=Su par de intercambio ha solicitado mediación.\n\n{0}\n\nVersión Bisq: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} +support.mediatorsDisputeSummary=Mensaje de sistema: Resumen de la disputa del mediador: {0} support.mediatorsAddress=Dirección del nodo del mediador: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=Información de red settings.tab.about=Acerca de setting.preferences.general=Preferencias generales -setting.preferences.explorer=Explorador de bloques Bitcoin -setting.preferences.explorer.bsq=Explorador de bloques BSQ +setting.preferences.explorer=Explorador Bitcoin +setting.preferences.explorer.bsq=Explorador Bisq setting.preferences.deviation=Desviación máxima del precio de mercado setting.preferences.avoidStandbyMode=Evitar modo en espera +setting.preferences.autoConfirmXMR=Autoconfirmación XMR +setting.preferences.autoConfirmEnabled=Habilitado +setting.preferences.autoConfirmRequiredConfirmations=Confirmaciones requeridas +setting.preferences.autoConfirmMaxTradeSize=Cantidad máxima de intecambio (BTC) +setting.preferences.autoConfirmServiceAddresses=Explorador de URLs Monero (usa Tor, excepto para localhost, direcciones LAN IP, y hostnames *.local) setting.preferences.deviationToLarge=No se permiten valores superiores a {0}% setting.preferences.txFee=Comisión de transacción de retiro (satoshis/byte) setting.preferences.useCustomValue=Usar valor personalizado @@ -981,8 +1054,8 @@ settings.net.p2PPeersLabel=Pares conectados settings.net.onionAddressColumn=Dirección onion settings.net.creationDateColumn=Establecido settings.net.connectionTypeColumn=Dentro/Fuera -settings.net.sentDataLabel=Sent data statistics -settings.net.receivedDataLabel=Received data statistics +settings.net.sentDataLabel=Estadísticas de datos enviados +settings.net.receivedDataLabel=Estadísticas de datos recibidos settings.net.roundTripTimeColumn=Tiempo de ida y vuelta settings.net.sentBytesColumn=Enviado settings.net.receivedBytesColumn=Recibido @@ -995,8 +1068,8 @@ settings.net.heightColumn=Altura settings.net.needRestart=Necesita reiniciar la aplicación para aplicar ese cambio.\n¿Quiere hacerlo ahora? settings.net.notKnownYet=Aún no conocido... -settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec -settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec +settings.net.sentData=Datos enviados: {0}, mensajes {1}, mensajes {2} mensajes por segundo +settings.net.receivedData=Datos recibidos: {0}, mensajes {1}, mensajes por segundo {2} settings.net.ips=[Dirección IP:puerto | host:puerto | dirección onion:puerto] (separado por coma). El puerto puede ser omitido si se utiliza el predeterminado (8333). settings.net.seedNode=Nodo semilla settings.net.directPeer=Par (directo) @@ -1018,8 +1091,8 @@ setting.about.support=Apoye a Bisq setting.about.def=Bisq no es una compañía - es un proyecto abierto a la comunidad. Si quiere participar o ayudar a Bisq por favor siga los enlaces de abajo. setting.about.contribute=Contribuir setting.about.providers=Proveedores de datos -setting.about.apisWithFee=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices, and Bisq Mempool Nodes for mining fee estimation. -setting.about.apis=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices. +setting.about.apisWithFee=Bisq usa Índices de Precios Bisq para los precios de mercado de fiat y altcoin, y los Nodos de Mempool de Bisq para la estimación de tasas de minado. +setting.about.apis=Bisq usa los Índices de Precios Bisq para los precios de mercado de fiat y altcoin. setting.about.pricesProvided=Precios de mercado proporcionados por: setting.about.feeEstimation.label=Estimación de comisión de minería proporcionada por: setting.about.versionDetails=Detalles de la versión @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Abrir herramienta de monedero setting.about.shortcuts.showTorLogs=Cambiar nivel de registro para mensajes Tor entre DEBUG y WARN -setting.about.shortcuts.showDisputeStatistics=Mostrar resumen de todas las disputas -setting.about.shortcuts.showDisputeStatistics.value=Navegar a vista de disputas y pulsar: {0} - setting.about.shortcuts.manualPayoutTxWindow=Abrir ventana para pago manual desde la transacción de depósito multifirma 2de2 setting.about.shortcuts.reRepublishAllGovernanceData=Republicar datos de gobernanza DAO (propuestas, votos) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navegar a cuenta y pulsar: {0} setting.about.shortcuts.registerMediator=Registrar mediador (solo mediador/árbitro) setting.about.shortcuts.registerMediator.value=Navegar a cuenta y pulsar: {0} -setting.about.shortcuts.reOpenDispute=Reabrir disputa ya cerrada (solo mediador/árbitro) -setting.about.shortcuts.reOpenDispute.value=Seleccionar disputa cerrada y pulsar: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Abrir ventana para firmado de edad de cuenta (solo árbitros legacy) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navegar a vista de árbitro legacy y pulsar: {0} @@ -1082,7 +1149,8 @@ setting.about.shortcuts.sendFilter=Establecer filtro (actividad privilegiada) setting.about.shortcuts.sendPrivateNotification=Enviar notificación privada a los pares (actividad privilegiada) setting.about.shortcuts.sendPrivateNotification.value=Abrir información de par en el avatar o disputa y pulsar: {0} - +setting.info.headline=Nueva función de autoconfirmación XMR +setting.info.msg=Al vender XMR por XMR puede usar la función de autoconfirmación para verificar que la cantidad correcta de XMR se envió al monedero con lo que Bisq pueda marcar el intercambio como completo, haciendo los intercambios más rápidos para todos.\n\nLa autoconfirmación comprueba que la transacción de XMR en al menos 2 nodos exploradores XMR usando la clave de transacción entregada por el emisor XMR. Por defecto, Bisq usa nodos exploradores ejecutados por contribuyentes Bisq, pero recomendamos usar sus propios nodos exploradores para un máximo de privacidad y seguridad.\n\nTambién puede configurar la cantidad máxima de BTC por intercambio para la autoconfirmación, así como el número de confirmaciones en Configuración.\n\nVea más detalles (incluído cómo configurar su propio nodo explorador) en la wiki Bisq: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1090,7 +1158,7 @@ setting.about.shortcuts.sendPrivateNotification.value=Abrir información de par account.tab.arbitratorRegistration=Registro de árbitro account.tab.mediatorRegistration=Registro de mediador account.tab.refundAgentRegistration=Registro de agente de devolución de fondos -account.tab.signing=Signing +account.tab.signing=Firmado account.tab.account=Cuenta account.info.headline=Bienvenido a su cuenta Bisq account.info.msg=Aquí puede añadir cuentas de intercambio para monedas nacionales y altcoins y crear una copia de su cartera y datos de cuenta.\n\nUna nueva cartera Bitcoin fue creada la primera vez que inició Bisq.\n\nRecomendamos encarecidamente que escriba sus palabras de la semilla de la cartera Bitcoin (mire pestaña arriba) y considere añadir una contraseña antes de enviar fondos. Los ingresos y retiros de Bitcoin se administran en la sección \"Fondos\".\n\nNota de privacidad y seguridad: Debido a que Bisq es un exchange descentralizado, todos sus datos se guardan en su ordenador. No hay servidores, así que no tenemos acceso a su información personal, sus saldos, o incluso su dirección IP. Datos como número de cuenta bancaria, direcciones altcoin y Bitcoin, etc son solo compartidos con su par de intercambio para completar intercambios iniciados (en caso de una disputa el mediador o árbitro verá los mismos datos que el par de intercambio). @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Por favor, asegúrese que sigue los requisitos account.altcoin.popup.wallet.confirm=Entiendo y confirmo que sé qué monedero tengo que utilizar. account.altcoin.popup.upx.msg=Para intercambiar UPX en bisq es preciso que usted entienda y cumpla los siguientes requerimientos:\n\nPara enviar upx es preciso que use el monedero oficial de uPlexa GUI o el de uPlexa CLI con la opción "store-tx-info" habilitada (predeterminada en las nuevas versiones). Por favor, asegúrese de poder acceder a las llaves tx, ya que podrían requerirse en caso de disputa.\nuplexa-wallet-cli (utilice el comando get_tx_key)\nuplexa-wallet-gui (vaya a la pestaña historial y haga clic en el botón (P) para prueba de pago).\nEn los exploradores de bloques normales la transferencia no es verificable.\nUsted necesita proporcionar al árbitro los siguientes datos en caso de disputa:\n- La llave privada tx\n- El hash de la transacción\n- La dirección publica de recepción\nSi no proporciona los datos arriba señalados, o si utilizó una cartera incompatible, perderá la disputa.\nLa parte que envía UPX es responsable de proporcionar las evidencias de la transferencia de UPX al árbitro en caso de disputa.\nNo se requiere ID de pago, solo la dirección pública normal.\nSi no está seguro sobre el proceso, visite el canal de conflictos de uPlexa https://discord.gg/vhdNSrV) o el Chat de Telegram de uPlexa (https://t.me/uplexaOfficial) para encontrar más información. account.altcoin.popup.arq.msg=El ARQ de intercambio de Bisq requiere que entienda y cumpla los siguientes requerimientos:\n\nPara el ARQ de envío, necesita utilizar la cartera oficial ArQmA GUI o una cartera ArQmA CLI con la opción store-tx-info habilitada (predeterminada en nuevas versiones). Por favor asegúrese de poder acceder a la llave tx ya que podría ser requerido en caso de disputa.\narqma-wallet-cli (utilice el comando get_tx_key)\narqma-wallet-gui (vaya a la pestaña historial y pulse el botón (P) para prueba de pago)\n\nEn los exploradores de bloques normales la transferencia no es verificable.\n\nNecesita proveer al mediador o al árbitro los siguientes datos en caso de disputa:\n- El tx de la llave privada\n- El hash de transacción\n- La dirección publica de recepción\n\nNo proveer los datos arriba señalados, o si utilizó una cartera incompatible, resultará en perder la disputa. La parte ARQ que envía es responsable de proveer verificación de la transferencia ARQ al mediador o al árbitro en caso de disputa.\n\nNo se requiere ID de pago, solo la dirección pública normal.\nSi no está seguro sobre el proceso visite el canal ArQmA en discord (https://discord.gg/s9BQpJT) o el foro de ArQmA (https://labs.arqma.com) para encontrar más información. -account.altcoin.popup.xmr.msg=Intercambiar XMR en Bisq requiere entender y cumplir los siguientes requisitos\n\nPruebas de pago:\nComo Monero es una monda privada, algunos detalles de la transacción no están disponibles públicamente en la cadena de bloques y, en caso de dsiputa, el mediador o el árbitro necesita comprobarlas para verificar si una transacción realmente se realizó. En Bisq, el emisor de la transacción XMR es el responsable de proveer esta información a el mediador o árbitro en caso de disputa. Para hacerlo, debe enviar XMR usando un monedero que proporcione la información necesaria para probar que el pago se haya realizado, lo que incluye:\n\n- La clave de transacción (Tx key, Tx Secret Key o Tx Private Key)\n- La ID de transacción (Tx ID o Tx Hash)\n- La dirección de destino (recipient's address)\n\nEsta información puede encontrarse en el monedero oficial Monero GUI y CLI, así como MyMonero o Exodus (escritorio) así como en Cake Wallet, MyMonero y Monerujo (móvil), en las siguientes ubicaciones:\n\n- GUI Monero: Vaya a la pestaña de Transacciones.\n- CLI Monero: Use el comando get_tx_key TXID. Debe habilitar la bandera store-tx-info (habilitada por defecto en las nuevas versiones).\n- Otros monederos: vaya a Historial de transacciones y busque la Clave de transacción (Tx key o Secret key) y la dirección de destino en la transacción enviada. Deberá activar "Guardar la dirección del receptor" en la configuración de Cake Wallet.\n\nSi está usando un monedero diferente de los anteriores, por favor asegúrese de que puede acceder a estos tres requerimientos de información. Como la clave de transacción y la dirección de destino se guardan en el software del monedero de Monero, y no se pueden recuperar desde la cadena de bloques de Monero, no debe borrar o restaurar su monedero de Monero antes de completar los intercambios de Bisq. Si no entrega los datos mencionados anteriormente, perderá el caso de disputa.\n\nComprobación de pagos: con estos tres datos de información, la verificación de que una cantidad específica de Monero se envió a una dirección específica puede completarse de la siguiente manera:\n\n- GUI Monero: cambiar el monedero a Modo avanzado > Prueba/comprobar > Comprobar transacción\n- CLI Monero: Use el comando check_tx_key TXID TXKEY ADDRESS\n-XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Web de Explore Monero (https://www.exploremonero.com/receipt)\n\nSu aún no está seguro del proceso, visite (https://www.getmonero.org/resources/user-guides/prove-payment.html) para encontrar más información o pregunte en el subreddit de soporte de Monero (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Intercambiar XMR en Bisq requiere entender y cumplir los siguientes requisitos\n\nPruebas de pago:\n\n\nSi está vendiendo XMR, debe ser capaz de entregar la siguiente información en caso de disputa:\n\n- La clave de transacción (Tx key, Tx Secret Key o Tx Private Key)\n- La ID de transacción (Tx ID o Tx Hash)\n- La dirección de destino (recipient's address)\n\nVea la wiki para detalles sobre dónde encontrar esta información en los principales monederos XMR:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\n\nNo entregar estos datos de transacción resultará en pérdida de la disputa.\n\nTenga en cuenta que Bisq ahora ofrece confirmación automática de transacciones XMR para realizar intercambios más rápido, pero necesita habilitarlo en Configuración. \n\nVea la wiki para más información sobre la función de autoconfirmación.\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Para intercambiar MSR en Bisq es necesario que usted entienda y cumpla los siguientes requisitos:\n\nPara enviar MSR es necesario que use el monedero oficial de Masari monedero GUI o Masari monedero CLI con la opción "store-tx-info" habilitada (predeterminada en las nuevas versiones) o el monedero oficial Masari Web (https://wallet.getmasari.org). Por favor, asegúrese de poder acceder a las llaves tx, ya que podrían requerirse en caso de alguna disputa. \nmasari-wallet-cli (utilice el comando get_tx_key)\nmasari-wallet-gui (vaya a la pestaña historial y haga clic en el botón (P) para prueba de pago). \n\nMonedero Masari Web (vaya a Cuenta -> historial transacción y vea los detalles de su transacción enviada.)\n\nPuede lograr verificación de la transacción dentro del monedero:\nmasari-wallet-cli : usando el comando (check_tx_key).\nmasari-wallet-gui: vaya a la pestaña Avanzado > Comprobar/Revisar.\nVerificación de transacción también se puede lograr desde el explorador de bloques\nAbrir explorador de bloques (https://explorer.getmasari.org), use la barra de búsqueda para buscar el hash de transacción. \nCuando encuentre su transacción, navegue hacia el final donde dice "Comprobar Envío" y rellene los detalles necesarios. \nUsted necesita compartir esta información con el mediador o árbitro en caso de disputa:\n- La clave privada tx\n- El hash de transacción\n- La dirección pública del que recibe\n\nSi no proporciona los datos arriba señalados, o si utilizó una cartera incompatible, perderá la disputa. El que envía MSR es responsable de entregar la verificación de transferencia al mediador o árbitro en caso de disputa.\n\nNo se requiere el ID de pago, sólo la dirección pública normal.\nSi no está seguro sobre el proceso, pida ayuda visitando el canal oficial de Masari en Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Intercambiar BLUR en Bisq requiere que entienda y cumpla los siguientes requisitos:\n\nPara enviar BLUR debe usar Blur Network CLI o la cartera GUI. \n\nSi utiliza la cartera CLI, un hash de transacción (tx ID) se mostrará después de hacer una transferencia. Deberá guardar esta información. Inmediatamente después de enviar la transferencia, deberá utilizar el comando 'get_tx_key' para obtener la clave privada de transacción. Si no realiza este paso, es posible que no pueda obtener la llave mas tarde. \n\nSi utiliza la cartera Blur Network GUI, la clave privada de transacción y el ID de transacción puede ser encontrada convenientemente en la pestaña "Historia". Inmediatamente después del envío, localice la transacción de interés. Pulse en el símbolo "?" en la esquina inferior derecha de la caja que contiene la transacción. Debe guardar esta información. \n\nEn caso de que sea necesaria una disputa, deberá presentar lo siguiente al mediador o al árbitro: 1.) el ID de transacción, 2.) la clave privada de transacción, y 3.) la dirección de destino. El mediador o árbitro entonces verificará la transferencia BLUR utilizando el Visor de Transacción Blur (https://blur.cash/#tx-viewer).\n\nSi no se proporciona la información requerida al mediador o al árbitro se perderá el caso de disputa. En todos los casos de disputa, el remitente BLUR asume el 100% de la responsabilidad en la verificación de las transacciones a un mediador o a un árbitro.. \n\nSi no comprende estos requisitos, no realice transacciones en Bisq. Primero, busque ayuda en Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Operador nodo de precio # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Operador de nodo Bitcoin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador de la API de mercados +dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador de mercados # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Operador explorador BSQ +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Operador de explorador # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operador transmisión de notificaciones móvil # suppress inspection "UnusedProperty" @@ -1699,7 +1767,7 @@ dao.proposal.myVote.summary=Votos: {0}; Peso de voto: {1} (ganado: {2} + cantida dao.proposal.myVote.invalid=Votación inválida dao.proposal.voteResult.success=Aceptado -dao.proposal.voteResult.failed=Rechadazo +dao.proposal.voteResult.failed=Rechazado dao.proposal.voteResult.summary=Resultado: {0}; Umbral: {1} (requerido > {2}); Quorum: {3} (requerido > {4}) dao.proposal.display.paramComboBox.label=Seleccionar parámetro a cambiar @@ -1972,28 +2040,37 @@ disputeSummaryWindow.reason.OTHER=Otro # suppress inspection "UnusedProperty" disputeSummaryWindow.reason.BANK_PROBLEMS=Banco # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.OPTION_TRADE=Option trade +disputeSummaryWindow.reason.OPTION_TRADE=Intercambio de opciones # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=Seller not responding +disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=Vendedor no responde # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=Wrong sender account +disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=Cuenta de emisor errónea # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.PEER_WAS_LATE=Peer was late +disputeSummaryWindow.reason.PEER_WAS_LATE=El par actuó tarde # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled +disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=El intercambio ya había acabado disputeSummaryWindow.summaryNotes=Nota de resumen disputeSummaryWindow.addSummaryNotes=Añadir notas de sumario disputeSummaryWindow.close.button=Cerrar ticket -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nSiguientes pasos:\nAbrir intercambio y aceptar o rechazar la sugerencia del mediador -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nSiguientes pasos:\nNo se requiere ninguna acción adicional. Si el árbitro decide en su favor, verá una transacción de "Devolución de fondos de arbitraje" en Fondos/Transacciones + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=Necesitar cerrar también el ticket del par de intercambio! disputeSummaryWindow.close.txDetails.headline=Publicar transacción de devolución de fondos disputeSummaryWindow.close.txDetails.buyer=El comprador recibe {0} en la dirección: {1}\n disputeSummaryWindow.close.txDetails.seller=El vendedor recibe {0} en la dirección: {1}\n disputeSummaryWindow.close.txDetails=Gastando: {0}\n{1}{2}Tasa de transacción: {3} ({4} satoshis/byte)\nTamaño de transacción: {5} Kb\n\n¿Está seguro de que quiere publicar esta transacción?\n +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline=Herramienta de monedero {0} de emergencia emptyWalletWindow.info=Por favor usar sólo en caso de emergencia si no puede acceder a sus fondos desde la Interfaz de Usuario (UI).\n\nPor favor, tenga en cuenta que todas las ofertas abiertas se cerrarán automáticamente al usar esta herramienta.\n\nAntes de usar esta herramienta, por favor realice una copia de seguridad del directorio de datos. Puede hacerlo en \"Cuenta/Copia de Seguridad\".\n\nPor favor repórtenos su problema y envíe un reporte de fallos en Github en el foro de Bisq para que podamos investigar qué causa el problema. emptyWalletWindow.balance=Su balance disponible en cartera @@ -2013,7 +2090,7 @@ filterWindow.onions=Direcciones onion filtradas (separadas por coma) filterWindow.accounts=Cuentas de intercambio filtradas:\nFormato: lista de [ID método de pago | campo de datos | valor] separada por coma. filterWindow.bannedCurrencies=Códigos de moneda filtrados (separados por coma) filterWindow.bannedPaymentMethods=ID's de métodos de pago filtrados (separados por coma) -filterWindow.bannedAccountWitnessSignerPubKeys=Filtered account witness signer pub keys (comma sep. hex of pub keys) +filterWindow.bannedAccountWitnessSignerPubKeys=Filtro de cuenta de las claves públicas de testigo de firmado (claves públicas en hexadecimal, separado por coma) filterWindow.bannedPrivilegedDevPubKeys=Filtered privileged dev pub keys (comma sep. hex of pub keys) filterWindow.arbitrators=Árbitros filtrados (direcciones onion separadas por coma) filterWindow.mediators=Mediadores filtrados (direcciones onion separadas por coma) @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=nodos de retransmisión de precio filtrados (direcci filterWindow.btcNode=Nodos Bitcoin filtrados (direcciones + puerto separadas por coma) filterWindow.preventPublicBtcNetwork=Prevenir uso de la red Bitcoin pública filterWindow.disableDao=Deshabilitar DAO +filterWindow.disableAutoConf=Deshabilitar autoconfirmación filterWindow.disableDaoBelowVersion=Versión mínima requerida para DAO filterWindow.disableTradeBelowVersion=Versión mínima requerida para intercambios. filterWindow.add=Añadir filtro @@ -2046,7 +2124,7 @@ offerDetailsWindow.creationDate=Fecha de creación offerDetailsWindow.makersOnion=Dirección onion del creador qRCodeWindow.headline=Código QR -qRCodeWindow.msg=Por favor, utiliza este código QR para fondear tu billetera Bisq desde una billetera externa. +qRCodeWindow.msg=Por favor, utilice este código QR para fondear su billetera Bisq desde su billetera externa. qRCodeWindow.request=Solicitud de pago:\n{0} selectDepositTxWindow.headline=Seleccione transacción de depósito para la disputa @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=Enviar notificación privada showWalletDataWindow.walletData=Datos del monedero showWalletDataWindow.includePrivKeys=Incluir claves privadas: +setXMRTxKeyWindow.headline=Prueba de envío de XMR +setXMRTxKeyWindow.note=Añadiendo la información de transacción a continuación, se habilita la autoconfirmación para intercambios más rápidos. Ver más: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=ID de transacción (opcional) +setXMRTxKeyWindow.txKey=Clave de transacción (opcional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=Acuerdo de usuario @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=La transacción de depósito de el int error.closedTradeWithNoDepositTx=El depósito de transacción de el intercambio cerrado con ID de intercambio {0} es inválido.\nPor favor reinicie la aplicación para limpiar la lista de intercambios cerrados. popup.warning.walletNotInitialized=La cartera aún no sea ha iniciado +popup.warning.osxKeyLoggerWarning=Debido a medidas de seguridad más estrictas en macOS 10.14 y siguientes, al iniciar una aplicación Java (Bisq usa Java) causa un popup de alarma en macOS ('Bisq would like to receive keystrokes from any application').\n\nPara evitar esto por favor abra su 'Configuración macOS' y vaya a 'Seguridad y privacidad' -> 'Privacidad¡ -> 'Monitorización de inputs' y elimine 'Bisq' de la lista a la derecha.\n\nBisq actualizara a una nueva versión de Java para evitar que este problema tan pronto como se resuelvan las limitaciones técnicas (el paquete de Java para la versión requerida de Java aún no se ha emitido). popup.warning.wrongVersion=Probablemente tenga una versión de Bisq incorrecta para este ordenador.\nLa arquitectura de su ordenador es: {0}.\nLos binarios de Bisq instalados son: {1}.\nPor favor cierre y reinstale la versión correcta ({2}). popup.warning.incompatibleDB=¡Hemos detectado archivos de base de datos incompatibles!\n\nEstos archivos de base de datos no son compatibles con nuestro actual código base:\n{0}\n\nHemos hecho una copia de seguridad de los archivos corruptos y aplicado los valores por defecto a la nueva versión de base de datos.\n\nLa copia de seguridad se localiza en:\n{1}/db/backup_of_corrupted_data.\n\nPor favor, compruebe si tiene la última versión de Bisq instalada.\nPuede descargarla en:\nhttps://bisq.network/downloads\n\nPor favor, reinicie la aplicación. popup.warning.startupFailed.twoInstances=Ya está ejecutando Bisq. No puede ejecutar dos instancias de Bisq. @@ -2169,12 +2253,12 @@ popup.warning.insufficientBtcFundsForBsqTx=No tiene suficientes fondos BTC para popup.warning.bsqChangeBelowDustException=Esta transacción crea un output BSQ de cambio que está por debajo del límite dust (5.46 BSQ) y sería rechazado por la red Bitcoin.\n\nTiene que enviar una cantidad mayor para evitar el output de cambio (Ej. agregando la cantidad de dust a su cantidad de envío) o añadir más fondos BSQ a su cartera para evitar generar un output de dust.\n\nEl output dust es {0}. popup.warning.btcChangeBelowDustException=Esta transacción crea un output de cambio que está por debajo del límite de dust (546 Satoshi) y sería rechazada por la red Bitcoin.\n\nDebe agregar la cantidad de dust a su cantidad de envío para evitar generar un output de dust.\n\nEl output de dust es {0}. -popup.warning.insufficientBsqFundsForBtcFeePayment=You''ll need more BSQ to do this transaction—the last 5.46 BSQ in your wallet cannot be used to pay trade fees because of dust limits in the Bitcoin protocol.\n\nYou can either buy more BSQ or pay trade fees with BTC.\n\nMissing funds: {0} +popup.warning.insufficientBsqFundsForBtcFeePayment=Necesitará más BSQ para hacer esta transacción -los últimos 5.46BSQ en su monedero no pueden usarse para pagar tasas de intercambio debido a límites dust en el protocolo Bitcoin.\n\nPuede comprar más BSQ o pagar las tasas de intercambio con BTC.\n\nFondos necesarios: {0} popup.warning.noBsqFundsForBtcFeePayment=Su monedero BSQ no tiene suficientes fondos para pagar la comisión de intercambio en BSQ. popup.warning.messageTooLong=Su mensaje excede el tamaño máximo permitido. Por favor, envíelo por partes o súbalo a un servicio como https://pastebin.com popup.warning.lockedUpFunds=Ha bloqueado fondos de un intercambio fallido.\nBalance bloqueado: {0}\nDirección de depósito TX: {1}\nID de intercambio: {2}.\n\nPor favor, abra un ticket de soporte seleccionando el intercambio en la pantalla de intercambios pendientes y haciendo clic en \"alt + o\" o \"option + o\"." -popup.warning.nodeBanned=Uno de los nodos {0} ha sido baneado. Por favor reinicie la aplicación para asegurarse de que no está conectada al nodo baneado. +popup.warning.nodeBanned=Uno de los nodos {0} ha sido baneado. popup.warning.priceRelay=retransmisión de precio popup.warning.seed=semilla popup.warning.mandatoryUpdate.trading=Por favor, actualice a la última versión de Bisq. Se lanzó una actualización obligatoria que inhabilita intercambios con versiones anteriores. Por favor, lea el Foro de Bisq para más información\n @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=Para asegurarse de que ambos comerciantes siguen popup.info.cashDepositInfo=Por favor asegúrese de que tiene una oficina bancaria donde pueda hacer el depósito de efectivo.\nEl ID del banco (BIC/SWIFT) de del vendedor es: {0} popup.info.cashDepositInfo.confirm=Confirmo que puedo hacer el depósito popup.info.shutDownWithOpenOffers=Bisq se está cerrando, pero hay ofertas abiertas.\n\nEstas ofertas no estarán disponibles en la red P2P mientras Bisq esté cerrado, pero serán re-publicadas a la red P2P la próxima vez que inicie Bisq.\n\nPara mantener sus ofertas en línea, mantenga Bisq ejecutándose y asegúrese de que la computadora permanece en línea también (Ej. asegúrese de que no se pone en modo standby... el monitor en espera no es un problema). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=Notificación privada importante! @@ -2273,7 +2358,7 @@ notification.trade.accepted=Su oferta ha sido aceptada por un {0} BTC notification.trade.confirmed=Su intercambio tiene al menos una confirmación en la cadena de bloques.\nPuede comenzar el pago ahora. notification.trade.paymentStarted=El comprador de BTC ha comenzado el pago. notification.trade.selectTrade=Seleccionar intercambio -notification.trade.peerOpenedDispute=Su pareja de intercambio ha abierto un/a {0}. +notification.trade.peerOpenedDispute=Su pareja de intercambio ha abierto un {0}. notification.trade.disputeClosed={0} se ha cerrado. notification.walletUpdate.headline=Actualizar monedero de intercambio. notification.walletUpdate.msg=Su monedero de intercambio tiene fondos suficientes.\nCantidad: {0} @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: Una red de intercambio de bitcoin descentralizada # GUI Util #################################################################### -guiUtil.miningFeeInfo=Por favor asegúrese de que la comisión de minado usada en su monedero externo es de al menos {0} sat/byte.\nDe otro modo, las transacciones de intercambio no podrán confirmarse y el intercambio acabaría en disputa. +guiUtil.miningFeeInfo=Por favor asegúrese de que la comisión de minado usada en su monedero externo es de al menos {0} sat/byte. De lo contrario, las transacciones de intercambio podrían no confirmarse y el intercambio acabaría en disputa. guiUtil.accountExport.savedToPath=Las cuentas de intercambio se han guardado en el directorio:\n{0} guiUtil.accountExport.noAccountSetup=No tiene cuentas de intercambio configuradas para exportar. @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=Cuenta de pago creada hace {0} peerInfoIcon.tooltip.unknownAge=Edad de cuenta de pago no conocida. tooltip.openPopupForDetails=Abrir popup para detalles +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=Abrir explorador de cadena de bloques externo para la dirección: {0} tooltip.openBlockchainForTx=Abrir explorador de cadena de bloques externo para la la transacción: {0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=Fallo al abrir la cartera Bitcoin predetermin peerInfoIcon.tooltip={0}\nEtiqueta: {1} txIdTextField.copyIcon.tooltip=Copiar ID de transacción al monedero -txIdTextField.blockExplorerIcon.tooltip=Abrir un explorador de bloques con esta ID de transacción +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=Abrir un explorador de bloques con esta navigation.account=\"Cuenta\" navigation.account.walletSeed=\"Cuenta/Semilla de cartera\" -navigation.funds.availableForWithdrawal=\"Fondos/Enviar fondos\" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"Portafolio/Mis ofertas abiertas\" navigation.portfolio.pending=\"Portafolio/Intercambios abiertos\" navigation.portfolio.closedTrades=\"Portafolio/Historial\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=Sus carteras están cifradas.\n\nDespués de resta seed.warn.walletDateEmpty=Como no ha especificado una fecha específica para el monedero, bisq tendrá que escanear la cadena de bloques desde el 2013.10.09 (la fecha de BIP39).\n\nLos monederos BIP39 se introdujeron en bisq en 2017.06.28 (publicación v.0.5). Puede ahorrar tiempo utilizando esa fecha.\n\nIdealmente, debería especificar la fecha en que su semilla fue creada.\n\n\nEstá seguro de que quiere continuar sin especificar una fecha para el monedero? seed.restore.success=Las carteras se restauraron con éxito con las nuevas palabras semilla.\n\nDebe cerrar y reiniciar la aplicación seed.restore.error=Un error ocurrió el restaurar los monederos con las palabras semilla. {0} +seed.restore.openOffers.warn=Tiene ofertas abiertas que serán eliminadas si restaura desde las palabras semilla.\n¿Está seguro de que quiere continuar? #################################################################### @@ -2466,7 +2554,8 @@ seed.restore.error=Un error ocurrió el restaurar los monederos con las palabras payment.account=Cuenta payment.account.no=Número de cuenta payment.account.name=Nombre de cuenta -payment.account.userName=User name +payment.account.userName=Nombre de usuario +payment.account.phoneNr=Número de teléfono payment.account.owner=Nombre completo del propietario de la cuenta payment.account.fullName=Nombre completo payment.account.state=Estado/Provincia/Región @@ -2546,9 +2635,9 @@ payment.limits.info.withSigning=Para limitar el riesgo de devolución de cargo, payment.cashDeposit.info=Por favor confirme que su banco permite enviar depósitos de efectivo a cuentas de otras personas. Por ejemplo, Bank of America y Wells Fargo ya no permiten estos depósitos. -payment.revolut.info=Revolut requires the 'User name' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not has set the ''User name''.\nPlease enter your Revolut ''User name'' to update your account data.\nThis will not affect your account age signing status. -payment.revolut.addUserNameInfo.headLine=Update Revolut account +payment.revolut.info=Revolut requiere el 'nombre de usuario' como ID de cuenta y no el número de teléfono o el e-mail que se requería anteriormente. +payment.account.revolut.addUserNameInfo={0}\nSu cuenta Revolut actual ({1}) no ha establecido el "nombre de usuario".\nPor favor introduzca su "nombre de usuario" en Revolut para actualizar sus datos de cuenta.\nEsto no afectará al estado de su edad de cuenta. +payment.revolut.addUserNameInfo.headLine=Actualizar cuenta Revolut payment.usPostalMoneyOrder.info=Los intercambios usando US Postal Money Orders (USPMO) en Bisq requiere que entienda lo siguiente:\n\n- Los compradores de BTC deben escribir la dirección del vendedor en los campos de "Payer" y "Payee" y tomar una foto en alta resolución de la USPMO y del sobre con la prueba de seguimiento antes de enviar.\n- Los compradores de BTC deben enviar la USPMO con confirmación de entrega.\n\nEn caso de que sea necesaria la mediación, se requerirá al comprador que entregue las fotos al mediador o agente de devolución de fondos, junto con el número de serie de la USPMO, número de oficina postal, y la cantidad de USD, para que puedan verificar los detalles en la web de US Post Office.\n\nNo entregar la información requerida al Mediador o Árbitro resultará en pérdida del caso de disputa. \n\nEn todos los casos de disputa, el emisor de la USPMO tiene el 100% de responsabilidad en aportar la evidencia al Mediador o Árbitro.\n\nSi no entiende estos requerimientos, no comercie usando USPMO en Bisq. diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index eab54e76b1f..cf998b4d16f 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -35,7 +35,7 @@ shared.no=خیر shared.iUnderstand=فهمیدم shared.na=بدون پاسخ shared.shutDown=خاموش -shared.reportBug=گزاش باگ در GitHub  +shared.reportBug=Report bug on GitHub shared.buyBitcoin=خرید بیتکوین shared.sellBitcoin=بیتکوین بفروشید shared.buyCurrency=خرید {0} @@ -94,21 +94,21 @@ shared.BTCMinMax=بیتکوین (حداقل - حداکثر) shared.removeOffer=حذف پیشنهاد shared.dontRemoveOffer=پیشنهاد را حذف نکنید shared.editOffer=ویرایش پیشنهاد -shared.openLargeQRWindow=کد QR در پنجره بزرگ باز کنید +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=حساب معاملات -shared.faq=از صفحه پرسش و پاسخ‌های متداول بازدید نمایید +shared.faq=Visit FAQ page shared.yesCancel=بله، لغو شود shared.nextStep=گام بعدی shared.selectTradingAccount=حساب معاملات را انتخاب کنید shared.fundFromSavingsWalletButton=انتقال وجه از کیف Bisq shared.fundFromExternalWalletButton=برای تهیه پول، کیف پول بیرونی خود را باز کنید -shared.openDefaultWalletFailed=پیدا کردن کیف پول پیش‌فرض بیتکوین ناموفق بوده است. شاید چنین برنامه‌ای را نصب نداشته باشید؟ +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=فاصله در ٪ از قیمت بازار shared.belowInPercent= ٪ زیر قیمت بازار shared.aboveInPercent= ٪ بالای قیمت بازار shared.enterPercentageValue=ارزش ٪ را وارد کنید shared.OR=یا -shared.notEnoughFunds=وجه کافی در کیف پول Bisq خود ندارید. \nبه {0} نیاز دارید، اما تنها دارای {1} در کیف پول خود هستید. \n\nلطفاً وجه آن معامله را از یک کیف پول بیت‌کوین خارجی یا از کیف پول Bisq در \"وجوه/دریافت وجوه\"، تأمین نمایید. +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=در انتظار دریافت وجه... shared.depositTransactionId=شناسه تراکنش وجه دریافتی shared.TheBTCBuyer=خریدار بیتکوین @@ -116,22 +116,22 @@ shared.You=شما shared.reasonForPayment=دلیل پرداخت shared.sendingConfirmation=در حال ارسال تاییدیه... shared.sendingConfirmationAgain=لطفاً تاییدیه را دوباره ارسال نمایید -shared.exportCSV=به csv خروجی بگیرید +shared.exportCSV=Export to CSV shared.exportJSON=به JSON خروجی بگیر shared.noDateAvailable=تاریخ موجود نیست shared.noDetailsAvailable=جزئیاتی در دسترس نیست shared.notUsedYet=هنوز مورد استفاده قرار نگرفته shared.date=تاریخ shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? -shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=کپی در کلیپ‌بورد shared.language=زبان shared.country=کشور shared.applyAndShutDown=اعمال و خاموش کردن shared.selectPaymentMethod=انتخاب روش پرداخت -shared.accountNameAlreadyUsed=چنین نامی درحال حاضر در حسابی دیگر ثبت شده است.\nلطفا از نام دیگری استفاده نمایید. +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=از حذف حساب انتخاب شده مطمئن هستید؟ -shared.cannotDeleteAccount=امکان حذف این حساب وجود ندارد. این حساب در پیشنهاد باز یا معامله‌ای در حال استفاده است. +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=هنوز هیچ حساب کاربری تنظیم نشده است shared.manageAccounts=مدیریت حساب‌ها shared.addNewAccount=افزودن حساب جدید @@ -214,7 +214,8 @@ shared.mediator=واسط shared.arbitrator=داور shared.refundAgent=داور shared.refundAgentForSupportStaff=Refund agent -shared.delayedPayoutTxId=Refund collateral transaction ID +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=بانک‌های کشورهای پذیرف offerbook.availableOffers=پیشنهادهای موجود offerbook.filterByCurrency=فیلتر بر اساس ارز offerbook.filterByPaymentMethod=فیلتر بر اساس روش پرداخت -offerbook.timeSinceSigning=Time since signing +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts offerbook.timeSinceSigning.info.peer=signed by a peer, waiting for limits to be lifted @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=signed by peer and can sign peer accounts offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} روز offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=غیرفعالسازی پیشنهاد نامو offerbook.activateOffer.failed=انتشار پیشنهاد ناموفق بود:\n{0} offerbook.withdrawFundsHint=می‌توانید مبلغی را که از صفحه {0} پرداخت کرده اید، بردارید. -offerbook.warning.noTradingAccountForCurrency.headline=حساب معاملاتی برای ارز انتخاب شده موجود نیست -offerbook.warning.noTradingAccountForCurrency.msg=حساب معاملاتی برای ارز انتخاب شده ندارید.\nآیا می خواهید پیشنهادی را با یکی از حساب‌های معاملاتی خود، ایجاد کنید؟ -offerbook.warning.noMatchingAccount.headline=حساب معاملاتی، مطابقت ندارد. -offerbook.warning.noMatchingAccount.msg=برای پذیرفتن این پیشنهاد، باید یک حساب پرداخت برای این روش پرداخت بسازید.\n\nآیا مایلید این کار را الان انجام دهید؟ +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due to counterparty trade restrictions @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=روش پرداخت مورد استفاد offerbook.warning.nodeBlocked=آدرس onion آن معامله گر، توسط توسعه دهندگان Bisq مسدود شد.\nاحتمالاً هنگام گرفتن پیشنهاد از جانب آن معامله گر، یک اشکال ناامن موجب پدید آمدن مسائلی شده است. offerbook.warning.requireUpdateToNewVersion=Your version of Bisq is not compatible for trading anymore.\nPlease update to the latest Bisq version at https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=Your payment account has been created {0} ago. Your trade limit is based on the account age and is not sufficient for that offer.\n\nYour trade limit is: {1}\nThe min. trade amount of the offer is: {2}.\n\nYou cannot take that offer at the moment. Once your account is older than 2 months this restriction gets removed. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=با قیمت روز بازار خواهید فروخت (به روز رسانی در هر دقیقه). offerbook.info.buyAtMarketPrice=با قیمت روز بازار خرید خواهید کرد (به روز رسانی در هر دقیقه). @@ -541,7 +543,7 @@ portfolio.tab.history=تاریخچه portfolio.tab.failed=ناموفق portfolio.tab.editOpenOffer=ویرایش پیشنهاد -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=برای تأییدیه بلاک چین منتظر باشید portfolio.pending.step2_buyer.startPayment=آغاز پرداخت @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=صبر کنید تا پرداخ portfolio.pending.step3_seller.confirmPaymentReceived=تأیید رسید پرداخت portfolio.pending.step5.completed=تکمیل شده +portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status +portfolio.pending.autoConf=Auto-confirmed +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key +portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Auto-confirm feature is disabled. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=تراکنش سپرده منتشر شده است.\nباید برای حداقل یک تأییدیه بلاک چین قبل از آغاز پرداخت، {0} صبر کنید. portfolio.pending.step1.warn=The deposit transaction is still not confirmed. This sometimes happens in rare cases when the funding fee of one trader from an external wallet was too low. portfolio.pending.step1.openForDispute=The deposit transaction is still not confirmed. You can wait longer or contact the mediator for assistance. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=لطفاً به صفحه‌ی وبسایت ب # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=لطفا با استفاده از راه‌های ارتباطی ارائه شده توسط فروشنده با وی تماس بگیرید و قرار ملاقاتی را برای پرداخت {0} تنظیم کنید.\n portfolio.pending.step2_buyer.startPaymentUsing=آغاز پرداخت با استفاده از {0} +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=مبلغ انتقال portfolio.pending.step2_buyer.sellersAddress=آدرس {0} فروشنده portfolio.pending.step2_buyer.buyerAccount=حساب پرداخت مورد استفاده @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might veri portfolio.pending.step2_buyer.confirmStart.headline=تأیید کنید که پرداخت را آغاز کرده‌اید portfolio.pending.step2_buyer.confirmStart.msg=آیا شما پرداخت {0} را به شریک معاملاتی خود آغاز کردید؟ portfolio.pending.step2_buyer.confirmStart.yes=بلی، پرداخت را آغاز کرده‌ام - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=برای پرداخت منتظر باشید portfolio.pending.step2_seller.f2fInfo.headline=اطلاعات تماس خریدار portfolio.pending.step2_seller.waitPayment.msg=تراکنش سپرده، حداقل یک تأییدیه بلاکچین دارد.شما\nباید تا آغاز پرداخت {0} از جانب خریدار بیتکوین، صبر نمایید. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=مبلغ قابل دریافت portfolio.pending.step3_seller.yourAddress=آدرس {0} شما portfolio.pending.step3_seller.buyersAddress=آدرس {0} خریدار portfolio.pending.step3_seller.yourAccount=حساب معاملاتی شما -portfolio.pending.step3_seller.buyersAccount=حساب معاملاتی خریدار +portfolio.pending.step3_seller.xmrTxHash=شناسه تراکنش +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=تأیید رسید پرداخت portfolio.pending.step3_seller.buyerStartedPayment=خریدار بیتکوین پرداخت {0} را آغاز کرده است.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=تأییدیه‌های بلاکچین را در کیف پول آلتکوین خود یا بلاکچین اکسپلورر بررسی کنید و هنگامی که تأییدیه های بلاکچین کافی دارید، پرداخت را تأیید کنید. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Please use this function only in emergen portfolio.pending.timeLockNotOver=You have to wait until ≈{0} ({1} more blocks) before you can open an arbitration dispute. portfolio.pending.error.depositTxNull=The deposit transaction is null. You cannot open a dispute without a valid deposit transaction. Please go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. -portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. The trade gets moved to the failed trades section. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=The deposit transaction is not confirmed. You can not open an arbitration dispute with an unconfirmed deposit transaction. Please wait until it is confirmed or go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. portfolio.pending.notification=اطلاع رسانی @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=تکمیل شده portfolio.closed.ticketClosed=Arbitrated portfolio.closed.mediationTicketClosed=Mediated @@ -763,8 +817,8 @@ portfolio.closed.canceled=لغو شده است portfolio.failed.Failed=ناموفق portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets support.filter=Search disputes support.filter.prompt=Enter trade ID, date, onion address or account data + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=ارسال اطلاع رسانی خصوصی -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=اعلان خصوصی +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=هیچ تیکتی به صورت باز وجود ندارد support.sendingMessage=در حال ارسال پیام ... support.receiverNotOnline=Receiver is not online. Message is saved to their mailbox. @@ -905,6 +970,9 @@ support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nB support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nBisq version: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Mediator''s node address: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=اطلاعات شبکه settings.tab.about=درباره setting.preferences.general=اولویت‌های عمومی -setting.preferences.explorer=کاوشگر بلاک بیت‌کوین -setting.preferences.explorer.bsq=BSQ block explorer +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=حداکثر تفاوت از قیمت روز بازار setting.preferences.avoidStandbyMode=حالت «آماده باش» را نادیده بگیر +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=مقادیر بزرگتر از {0}% مجاز نیست. setting.preferences.txFee=کارمزد تراکنش برداشت (ساتوشی/بایت) setting.preferences.useCustomValue=استفاده از ارزش سفارشی @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool fo setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN -setting.about.shortcuts.showDisputeStatistics=Show summary of all disputes -setting.about.shortcuts.showDisputeStatistics.value=Navigate to disputes view and press: {0} - setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navigate to account and press: setting.about.shortcuts.registerMediator=Register mediator (mediator/arbitrator only) setting.about.shortcuts.registerMediator.value=Navigate to account and press: {0} -setting.about.shortcuts.reOpenDispute=Re-open already closed dispute (mediator/arbitrator only) -setting.about.shortcuts.reOpenDispute.value=Select closed dispute and press: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Open window for account age signing (legacy arbitrators only) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navigate to legacy arbitrator view and press: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Send alert or update message (privileged ac setting.about.shortcuts.sendFilter=Set Filter (privileged activity) setting.about.shortcuts.sendPrivateNotification=Send private notification to peer (privileged activity) -setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar or dispute and press: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements account.altcoin.popup.wallet.confirm=من می فهمم و تأیید می کنم که می دانم از کدام کیف پول باید استفاده کنم. account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. account.altcoin.popup.arq.msg=Trading ARQ on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending ARQ, you need to use either the official ArQmA GUI wallet or ArQmA CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\narqma-wallet-cli (use the command get_tx_key)\narqma-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The ARQ sender is responsible for providing verification of the ARQ transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit ArQmA discord channel (https://discord.gg/s9BQpJT) or the ArQmA forum (https://labs.arqma.com) to find more information. -account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand and fulfill the following requirements:\n\nProve payments: since Monero is a private coin, some transaction details aren't publicly available in the blockchain, and, in case of a dispute, the mediator or arbitrator needs them to check if the transaction was really made. In Bisq, the sender of the XMR transaction is the one responsible for providing this information to the mediator or arbitrator in case of a dispute. In order to do that, you must send XMR using a wallet that provides the information required to prove the payment was made, which includes:\n\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nThis information can be found in the official Monero GUI & CLI wallets, MyMonero, and Exodus (desktop) as well as in Cake Wallet, MyMonero, and Monerujo (mobile), in the following locations:\n\n- Monero GUI: go to Transactions tab\n- Monero CLI: use the command get_tx_key TXID. The flag store-tx-info must be enabled (enabled by default in new versions)\n- Other wallets: go to Transactions history and search for Transaction key (Tx key or Secret key) and the destination address in a sent transaction. Save recipient address option must be enabled in Cake Wallet settings.\n\nIf you are using a wallet different from the mentioned above, please be sure you can access those three pieces of information.Since the transaction key and the destination address are stored in the Monero wallet software, and they cannot be recovered in the Monero blockchain, you should never delete or restore your Monero wallet before a Bisq trade is completed. Failure to provide the above data will result in losing the dispute case.\n\nCheck payments: with those three pieces of information, the verification that a quantity of Monero was sent to a specific address can be accomplished the following way:\n\n- Monero GUI: change wallet to Advanced mode and go to Advanced > Prove/check > Check Transaction\n- Monero CLI: use the command check_tx_key TXID TXKEY ADDRESS\n- XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Explore Monero website (https://www.exploremonero.com/receipt)\n\nIf you are still not sure about this process, visit (https://www.getmonero.org/resources/user-guides/prove-payment.html) to find more information or ask a question on the Monero support subreddit (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Trading MSR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending MSR, you need to use either the official Masari GUI wallet, Masari CLI wallet with the store-tx-info flag enabled (enabled by default) or the Masari web wallet (https://wallet.getmasari.org). Please be sure you can access the tx key as that would be required in case of a dispute.\nmasari-wallet-cli (use the command get_tx_key)\nmasari-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nMasari Web Wallet (goto Account -> transaction history and view details on your sent transaction)\n\nVerification can be accomplished in-wallet.\nmasari-wallet-cli : using command (check_tx_key).\nmasari-wallet-gui : on the Advanced > Prove/Check page.\nVerification can be accomplished in the block explorer \nOpen block explorer (https://explorer.getmasari.org), use the search bar to find your transaction hash.\nOnce transaction is found, scroll to bottom to the 'Prove Sending' area and fill in details as needed.\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The MSR sender is responsible for providing verification of the MSR transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process, ask for help on the Official Masari Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=گرداننده گره قیم # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets API operator +dao.bond.bondedRoleType.MARKETS_OPERATOR=گرداننده بازارها # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=گرداننده کاوشگر BSQ +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=اپراتور بازپخش اعلان های موبایل # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=نکات خلاصه disputeSummaryWindow.addSummaryNotes=افزودن نکات خلاصه disputeSummaryWindow.close.button=بستن تیکت -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nNext steps:\nOpen trade and accept or reject suggestion from mediator -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=شما باید همچنین تیکت همتایان معامله را هم ببندید! disputeSummaryWindow.close.txDetails.headline=Publish refund transaction disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to publish this transaction? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline=ابزار اضطراری کیف پول {0} emptyWalletWindow.info=لطفاً تنها در مورد اضطراری از آن استفاده کنید اگر نمی توانید به وجه خود از UI دسترسی داشته باشید.\n\nلطفاً توجه داشته باشید که تمام معاملات باز به طور خودکار در هنگام استفاده از این ابزار، بسته خواهد شد.\n\nقبل از به کار گیری این ابزار، از راهنمای داده ی خود پشتیبان بگیرید. می توانید این کار را در \"حساب/پشتیبان\" انجام دهید.\n\nلطفاً مشکل خود را به ما گزارش کنید و گزارش مشکل را در GitHub یا تالار گفتگوی Bisq بایگانی کنید تا ما بتوانیم منشأ مشکل را بررسی نماییم. emptyWalletWindow.balance=موجودی در دسترس کیف‌پول شما @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=گره های رله قیمت فیلترشده (آد filterWindow.btcNode=گره‌های بیت‌کوین فیلترشده (آدرس + پورت جدا شده با ویرگول) filterWindow.preventPublicBtcNetwork=جلوگیری از استفاده ازشبکه عمومی بیت‌کوین filterWindow.disableDao=Disable DAO +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Min. version required for DAO filterWindow.disableTradeBelowVersion=Min. version required for trading filterWindow.add=افزودن فیلتر @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=تأیید: پیشنهاد را به {0} بپذ offerDetailsWindow.creationDate=تاریخ ایجاد offerDetailsWindow.makersOnion=آدرس Onion سفارش گذار -qRCodeWindow.headline=QR-Code -qRCodeWindow.msg=لطفاً از آن QR-Code برای تأمین وجه کیف پول Bisq از کیف پول خارجی خود استفاده کنید. +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=درخواست پرداخت:\n{0} selectDepositTxWindow.headline=تراکنش سپرده را برای مناقشه انتخاب کنید @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=ارسال اطلاع رسانی خصوصی showWalletDataWindow.walletData=داده های کیف پول showWalletDataWindow.includePrivKeys=شامل کلیدهای خصوصی +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=موافقتنامه کاربر @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=The deposit transaction of the closed error.closedTradeWithNoDepositTx=The deposit transaction of the closed trade with the trade ID {0} is null.\n\nPlease restart the application to clean up the closed trades list. popup.warning.walletNotInitialized=کیف پول هنوز راه اندازی اولیه نشده است +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=شما احتمالاً نسخه اشتباه Bisq را برای این رایانه دارید.\nمعماری کامپیوتر شما این است: {0}.\nباینری Bisq که شما نصب کرده اید،عبارت است از: {1}.\nلطفاً نسخه فعلی را خاموش کرده و مجدداً نصب نمایید ({2}). popup.warning.incompatibleDB=ما فایل های پایه داده ناسازگار را کشف کردیم! \n\nاین فایل های(s) پایگاه داده با پایه کد فعلی ما سازگار نیست:\n {0}\n\n ما از فایل (های) خراب شده یک پشتیبان گرفتیم و مقادیر پیش فرض را به یک پایگاه داده نسخه ی جدید اعمال کردیم. \n\nپشتیبان واقع است در: {1}/db/backup_of_corrupted_data.\n\n لطفا بررسی کنید که آیا آخرین نسخه Bisq نصب شده است یا خیر. \nشما می توانید آن را در: https://bisq.network/downloads دانلود کنید\n\n لطفا برنامه را دوباره راه اندازی کنید. popup.warning.startupFailed.twoInstances=Bisq در حال اجرا است. شما نمیتوانید دو نمونه از Bisq را اجرا کنید. @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=کیف‌پول BSQ شما BSQ کافی popup.warning.messageTooLong=پیام شما بیش از حداکثر اندازه مجاز است. لطفا آن را در چند بخش ارسال کنید یا آن را در یک سرویس مانند https://pastebin.com آپلود کنید. popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." -popup.warning.nodeBanned=یکی از گره های {0} مسدود شده است. لطفا برنامه خود را مجددا راه اندازی کنید تا مطمئن شوید که به گره ی مسدود وصل نیستید. +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=رله قیمت popup.warning.seed=دانه popup.warning.mandatoryUpdate.trading=Please update to the latest Bisq version. A mandatory update was released which disables trading for old versions. Please check out the Bisq Forum for more information. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=برای اطمینان از اینکه هر دو popup.info.cashDepositInfo=لطفا مطمئن شوید که شما یک شعبه بانک در منطقه خود دارید تا بتوانید سپرده نقدی را بپردازید. شناسه بانکی (BIC/SWIFT) بانک فروشنده: {0}. popup.info.cashDepositInfo.confirm=تأیید می کنم که می توانم سپرده را ایجاد کنم popup.info.shutDownWithOpenOffers=Bisq در حال خاموش شدن است ولی پیشنهاداتی وجود دارند که باز هستند.\n\nزمانی که Bisq بسته باشد این پیشنهادات در شبکه P2P در دسترس نخواهند بود، ولی هر وقت دوباره Bisq را باز کنید این پیشنهادات دوباره در شبکه P2P منتشر خواهند شد.\n\n برای اینکه پیشنهادات شما برخط بمانند، بگذارید Bisq در حال اجرابماند و همچنین مطمئن شوید که این کامپیوتر به اینترنت متصل است. (به عنوان مثال مطمئن شوید که به حالت آماده باش نمی‌رود.. البته حالت آماده باش برای نمایشگر ایرادی ندارد). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=اعلان خصوصی مهم! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=لطفا مطمئن شوید که هزینه استخراج مورد استفاده در کیف پول خارجی شما حداقل {0} ساتوشی/بایت است. در غیر این صورت تراکنش های معامله نمی تواند تأیید شود و معامله در معرض مناقشه قرار می گیرد. +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=حساب های معاملاتی در مسیر ذیل ذخیره شد:\n{0} guiUtil.accountExport.noAccountSetup=شما حساب های معاملاتی برای صادرات ندارید. @@ -2337,7 +2422,8 @@ peerInfoIcon.tooltip.age=حساب معاملاتی {0} قبل ایجاد شده peerInfoIcon.tooltip.unknownAge=عمر حساب پرداخت ناشناخته است. tooltip.openPopupForDetails=باز کردن پنجره برای جزئیات -tooltip.openBlockchainForAddress= مرورگرهای بلاک چین خارجی را برای آدرس باز کنید: {0} +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information +tooltip.openBlockchainForAddress=مرورگرهای بلاک چین خارجی را برای آدرس باز کنید: {0} tooltip.openBlockchainForTx=باز کردن مرورگر بلاک چین خارجی برای تراکنش: {0} confidence.unknown=وضعیت معامله ناشناخته @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=باز کردن یک برنامه کیف پو peerInfoIcon.tooltip={0}\nتگ: {1} txIdTextField.copyIcon.tooltip=رونوشت شناسه تراکنش در حافظه ی موقتی -txIdTextField.blockExplorerIcon.tooltip=باز کردن یک مرورگر بلاک چین با آن شناسه تراکنش +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=باز کردن یک مرورگر بلا navigation.account=\"حساب\" navigation.account.walletSeed=\"حساب/رمز پشتیبان کیف پول\" -navigation.funds.availableForWithdrawal=\"وجوه/ارسال وجوه\" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"سبد سهام /پیشنهادهای باز من\" navigation.portfolio.pending=\"سبد سهام /معاملات باز\" navigation.portfolio.closedTrades=\"سبد سهام /تاریخچه\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=کیف های پول شما رمزگذاری شد seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=کیف های پول با کلمات کلمات رمز خصوصی جدید بازیابی شده است. \n\nشما باید برنامه را خاموش و مجددا راه اندازی کنید. seed.restore.error=هنگام بازگرداندن کیف پول با کلمات رمز خصوصی، خطایی روی داد. {0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=حساب payment.account.no=شماره حساب payment.account.name=نام حساب payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=نام کامل مالک حساب payment.account.fullName=نام کامل (اول، وسط، آخر) payment.account.state=ایالت/استان/ناحیه diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 6629a86411c..4cc45d3736a 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -35,7 +35,7 @@ shared.no=Non shared.iUnderstand=Je comprends shared.na=N/A shared.shutDown=Éteindre -shared.reportBug=Signaler le bug sur GitHub issues +shared.reportBug=Report bug on GitHub shared.buyBitcoin=Achat Bitcoin shared.sellBitcoin=Vendre des Bitcoins shared.buyCurrency=Achat {0} @@ -63,7 +63,7 @@ shared.priceInCurForCur=Prix en {0} pour 1 {1} shared.fixedPriceInCurForCur=Prix fixé en {0} pour 1 {1} shared.amount=Montant shared.txFee=Frais de transaction -shared.tradeFee=Trade Fee +shared.tradeFee=Frais de transaction shared.buyerSecurityDeposit=Dépôt de l'acheteur shared.sellerSecurityDeposit=Dépôt du vendeur shared.amountWithCur=Montant en {0} @@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (min - max) shared.removeOffer=Retirer l'ordre shared.dontRemoveOffer=Ne pas retirer l'ordre shared.editOffer=Éditer l'ordre -shared.openLargeQRWindow=Ouvrir une fenêtre avec un grand QR-Code +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=Compte de trading -shared.faq=Visiter la page FAQ +shared.faq=Visit FAQ page shared.yesCancel=Oui, annuler shared.nextStep=Étape suivante shared.selectTradingAccount=Sélectionner le compte de trading shared.fundFromSavingsWalletButton=Transférer des fonds depuis le portefeuille Bisq shared.fundFromExternalWalletButton=Ouvrez votre portefeuille externe pour provisionner -shared.openDefaultWalletFailed=L'ouverture d'un portefeuille Bitcoin par défaut a échoué. Peut-être n'en avez-vous aucun d'installé? +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=Écart en % par rapport au du prix du marché shared.belowInPercent=% sous le prix du marché shared.aboveInPercent=% au-dessus du prix du marché shared.enterPercentageValue=Entrez la valeur en % shared.OR=OU -shared.notEnoughFunds=Vous ne disposez pas de fonds suffisants dans votre portefeuille Bisq.\nVous avez besoin de {0} mais vous n''avez que {1} dans votre portefeuille Bisq.\n\nVeuillez créditer la transaction à partir d''un portefeuille Bitcoin externe ou créditer votre portefeuille Bisq ici \"Fonds/Recevoir des fonds\". +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=En attente des fonds... shared.depositTransactionId=ID de la transaction de dépôt shared.TheBTCBuyer=L'acheteur de BTC @@ -116,22 +116,22 @@ shared.You=Vous shared.reasonForPayment=Motif du paiement shared.sendingConfirmation=Envoi de la confirmation... shared.sendingConfirmationAgain=Veuillez envoyer de nouveau la confirmation -shared.exportCSV=Exporter au format csv +shared.exportCSV=Export to CSV shared.exportJSON=Exporter vers JSON shared.noDateAvailable=Pas de date disponible shared.noDetailsAvailable=Pas de détails disponibles shared.notUsedYet=Pas encore utilisé shared.date=Date -shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? -shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n +shared.sendFundsDetailsWithFee=Envoi: {0}\nDepuis l'adresse: {1}\nÀ l'adresse de réception: {2}\nLes frais de transaction requis sont : {3} ({4} satoshis/byte)\nMontant de la transaction: {5} Kb\n\nLe destinataire recevra: {6}\n\nÊtes-vous certain de vouloir retirer ce montant? +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=Copier dans le presse-papiers shared.language=Langue shared.country=Pays shared.applyAndShutDown=Appliquer et éteindre shared.selectPaymentMethod=Sélectionner un mode de paiement -shared.accountNameAlreadyUsed=Ce nom de compte est déjà utilisé avec un compte enregistré.\nMerci de choisir un autre nom. +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=Voulez-vous vraiment supprimer le compte sélectionné? -shared.cannotDeleteAccount=Vous ne pouvez pas supprimer ce compte car il est utilisé pour un ordre en cours ou un échange. +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=Il n'y a pas encore de comptes établis. shared.manageAccounts=Gérer les comptes shared.addNewAccount=Ajouter un nouveau compte @@ -214,8 +214,9 @@ shared.mediator=Médiateur shared.arbitrator=Arbitre shared.refundAgent=Arbitre shared.refundAgentForSupportStaff=Agent de remboursement -shared.delayedPayoutTxId=ID de la transaction de remboursement du dépôt de garantie -shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to +shared.unconfirmedTransactionsLimitReached=Vous avez trop de transactions non confirmées pour le moment. Veuillez réessayer plus tard. #################################################################### @@ -336,15 +337,16 @@ offerbook.offerersAcceptedBankSeats=Pays acceptés où se situe le siège de la offerbook.availableOffers=Ordres disponibles offerbook.filterByCurrency=Filtrer par devise offerbook.filterByPaymentMethod=Filtrer par mode de paiement -offerbook.timeSinceSigning=Temps depuis la signature +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=Ce compte a été vérifié et {0} offerbook.timeSinceSigning.info.arbitrator=signé par un arbitre et pouvant signer des comptes pairs offerbook.timeSinceSigning.info.peer=signé par un pair, en attente de la levée des limites offerbook.timeSinceSigning.info.peerLimitLifted=signé par un pair et les limites ont été levées offerbook.timeSinceSigning.info.signer=signé par un pair et pouvant signer des comptes de pairs (limites levées) -offerbook.timeSinceSigning.info.banned=account was banned +offerbook.timeSinceSigning.info.banned=Ce compte a été banni offerbook.timeSinceSigning.daysSinceSigning={0} jours offerbook.timeSinceSigning.daysSinceSigning.long={0} depuis la signature +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=Lorsque vous effectuez avec succès une transaction avec un pair disposant d''un compte de paiement signé, votre compte de paiement est signé.\n{0} Jours plus tard, la limite initiale de {1} est levée et votre compte peut signer les comptes de paiement d''un autre pair. offerbook.timeSinceSigning.notSigned=Pas encore signé @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=La désactivation de l''ordre a échoué:\n{0} offerbook.activateOffer.failed=La publication de l''ordre a échoué:\n{0} offerbook.withdrawFundsHint=Vous pouvez retirer les fonds investis depuis l''écran {0}. -offerbook.warning.noTradingAccountForCurrency.headline=Aucun compte de trading sélectionné pour cette devise -offerbook.warning.noTradingAccountForCurrency.msg=Vous n'avez pas de compte de trading pour la devise sélectionnée.\nSoutaitez-vous créer un ordre avec l'un de vos comptes de trading existant? -offerbook.warning.noMatchingAccount.headline=Pas de compte de trading correspondant. -offerbook.warning.noMatchingAccount.msg=Pour accepter cet ordre, vous devrez établir un compte de paiement qui utilise ce mode de paiement.\n\nSouhaitez-vous le faire maintenant ? +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=Cette offre ne peut être acceptée en raison de restrictions d'échange imposées par les contreparties @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=Le mode de paiement utilisé pour cet ordr offerbook.warning.nodeBlocked=L'adresse onion de ce trader a été bloquée par les développeurs de Bisq.\nIl s'agit peut être d'un bug qui cause des problèmes lors de l'acceptation de cet ordre. offerbook.warning.requireUpdateToNewVersion=Votre version de Bisq n'est plus compatible pour le trading.\nVeuillez mettre à jour votre version de Bisq sur https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=Votre compte de paiement a été créé il y a {0} . Votre limite de trading est basée sur l''âge du compte et n'est pas suffisante pour cette offre.\n\nVotre limite de trading est: {1}\nLe montant minimum de l'offre est de: {2}.\n\nVous ne pouvez pas accepter cette offre pour le moment. Une fois que votre compte aura plus de 2 mois, cette restriction sera levée. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=Vous vendrez au prix du marché (mis à jour chaque minute). offerbook.info.buyAtMarketPrice=Vous achèterez au prix du marché (mis à jour chaque minute). @@ -541,7 +543,7 @@ portfolio.tab.history=Historique portfolio.tab.failed=Échec portfolio.tab.editOpenOffer=Éditer l'ordre -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=Attendre la confirmation de la blockchain portfolio.pending.step2_buyer.startPayment=Initier le paiement @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=Patientez jusqu'à la réceptio portfolio.pending.step3_seller.confirmPaymentReceived=Confirmation de paiement reçu portfolio.pending.step5.completed=Terminé +portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status +portfolio.pending.autoConf=Auto-confirmed +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key +portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Auto-confirm feature is disabled. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=La transaction de dépôt à été publiée.\n{0} devez attendre au moins une confirmation de la blockchain avant d''initier le paiement. portfolio.pending.step1.warn=La transaction de dépôt n'est toujours pas confirmée. Cela se produit parfois dans de rares occasions lorsque les frais de financement d'un trader en provenance d'un portefeuille externe sont trop bas. portfolio.pending.step1.openForDispute=La transaction de dépôt n'est toujours pas confirmée. Vous pouvez attendre plus longtemps ou contacter le médiateur pour obtenir de l'aide. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=Merci de vous rendre sur le site de votre ban # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Veuillez s''il vous plaît contacter le vendeur de BTC via le contact fourni, et planifiez un rendez-vous pour effectuer le paiement {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Initier le paiement en utilisant {0} +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=Montant à transférer portfolio.pending.step2_buyer.sellersAddress=Adresse {0} du vendeur portfolio.pending.step2_buyer.buyerAccount=Votre compte de paiement à utiliser @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might veri portfolio.pending.step2_buyer.confirmStart.headline=Confirmez que vous avez initié le paiement portfolio.pending.step2_buyer.confirmStart.msg=Avez-vous initié le {0} paiement auprès de votre partenaire de trading? portfolio.pending.step2_buyer.confirmStart.yes=Oui, j'ai initié le paiement - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=En attende du paiement portfolio.pending.step2_seller.f2fInfo.headline=Coordonnées de l'acheteur portfolio.pending.step2_seller.waitPayment.msg=La transaction de dépôt a été vérifiée au moins une fois sur la blockchain\nVous devez attendre que l''acheteur de BTC lance le {0} payment. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=Montant à recevoir portfolio.pending.step3_seller.yourAddress=Votre adresse {0} portfolio.pending.step3_seller.buyersAddress=Adresse {0} des acheteurs portfolio.pending.step3_seller.yourAccount=Votre compte de trading -portfolio.pending.step3_seller.buyersAccount=Compte de trading des acheteurs +portfolio.pending.step3_seller.xmrTxHash=ID de la transaction +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=Confirmer la réception du paiement portfolio.pending.step3_seller.buyerStartedPayment=L''acheteur BTC a commencé le {0} paiement.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Vérifiez la présence de confirmations par la blockchain dans votre portefeuille altcoin ou sur un explorateur de blocs et confirmez le paiement lorsque vous aurez suffisamment de confirmations sur la blockchain. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=S'il vous plaît n'utilisez seulement ce portfolio.pending.timeLockNotOver=Vous devez patienter jusqu''au ≈{0} ({1} blocs de plus) avant de pouvoir ouvrir ouvrir un arbitrage pour le litige. portfolio.pending.error.depositTxNull=La transaction de dépôt est nulle. Vous ne pouvez pas ouvrir un litige sans une transaction de dépôt valide. Allez dans \"Paramètres/Info sur le réseau\" et faites une resynchronisation SPV.\n\nPour obtenir de l'aide, le canal support de l'équipe Bisq est disponible sur Keybase. -portfolio.pending.mediationResult.error.depositTxNull=La transaction de dépôt est nulle. L'échange est déplacé vers la section des échanges en échec. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=La transaction de dépôt n'est pas confirmée. Vous ne pouvez pas ouvrir un arbitrage pour le litige avec une transaction de dépôt non confirmée. Veuillez patienter jusqu'à ce qu'elle soit confirmée ou allez à \"Paramètres/Info sur le réseau réseau\" et faites une resynchronisation SPV.\n\nPour obtenir de l'aide, le canal support de l'équipe Bisq est disponible sur Keybase. portfolio.pending.notification=Notification @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=Voir la résolution proposée portfolio.pending.mediationResult.popup.headline=Résultat de la médiation pour la transaction avec l''ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Votre pair de trading a accepté la suggestion du médiateur pour la transaction {0} portfolio.pending.mediationResult.popup.info=Le médiateur a suggéré le paiement suivant:\nVous recevez: {0}\nVotre pair de trading recevra: {1}\n\nVous pouvez accepter ou rejeter cette proposition de paiement.\n\nEn acceptant, vous signez la proposition de transaction de paiement. Si votre pair de trading accepte et signe également, le paiement sera terminé et la transaction sera soldée.\n\nSi l''un ou les deux d''entre vous rejettent la proposition, vous devrez attendre jusqu''au {2} (block {3}) pour ouvrir un litige en deuxième instance avec un arbitre qui enquêtera de nouveau sur la situation et effectuera un paiement en fonction de ses conclusions.\n\nL''arbitre peut imputer des frais minimes (frais maximal : le dépôt de garantie du trader) au titre de rémunération pour son travail. Les deux traders acceptant la suggestion du médiateur créant ainsi une résolution heureuse—la demande d''arbitrage est prévue pour des circonstances exceptionnelles, par exemple si un trader est convaincu que le médiateur n''a pas proposé une solution de paiement équitable (ou si l''autre pair ne répond pas).\n\nPlus de détails sur le nouveau modèle d''arbitrage:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Refuser et demander un arbitrage portfolio.pending.mediationResult.popup.alreadyAccepted=Vous avez déjà accepté +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=Terminé portfolio.closed.ticketClosed=Arbitré portfolio.closed.mediationTicketClosed=Ayant fait l'objet d'une médiation @@ -763,8 +817,8 @@ portfolio.closed.canceled=Annulé portfolio.failed.Failed=Échec portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Conclusion d'arbitrage support.tab.ArbitratorsSupportTickets=Tickets de {0} support.filter=Search disputes support.filter.prompt=Saisissez l'ID du trade, la date, l'adresse "onion" ou les données du compte. + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=Envoyer une notification privée -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=Notification privée +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=Il n'y a pas de tickets ouverts support.sendingMessage=Envoi du message... support.receiverNotOnline=Le destinataire n'est pas en ligne. Le message est enregistré dans leur boîte mail. @@ -905,6 +970,9 @@ support.peerOpenedDispute=Votre pair de trading a fait une demande de litige.\n\ support.peerOpenedDisputeForMediation=Votre pair de trading a demandé une médiation.\n\n{0}\n\nVersion de Bisq: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Adresse du nœud du médiateur: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=Info sur le réseau settings.tab.about=À propos setting.preferences.general=Préférences générales -setting.preferences.explorer=Explorateur de block Bitcoin -setting.preferences.explorer.bsq=Explorateur de blocs BSQ +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Ecart maximal par rapport au prix du marché setting.preferences.avoidStandbyMode=Éviter le mode veille +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=Les valeurs supérieures à {0}% ne sont pas autorisées. setting.preferences.txFee=Frais de transaction du retrait (satoshis/byte) setting.preferences.useCustomValue=Utiliser une valeur personnalisée @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Ouvrir l'outil de portefeuill setting.about.shortcuts.showTorLogs=Basculer le niveau de log pour les messages Tor entre DEBUG et WARN -setting.about.shortcuts.showDisputeStatistics=Afficher le résumé de tous les litiges -setting.about.shortcuts.showDisputeStatistics.value=Naviguez vers la vue des litiges et appuyez sur: {0} - setting.about.shortcuts.manualPayoutTxWindow=Ouvrir la fenêtre pour le paiement manuel à partir du tx de dépôt Multisig 2of2 setting.about.shortcuts.reRepublishAllGovernanceData=Publier à nouveau les données sur la gouvernance de la DAO (propositions, votes) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Naviguez jusqu'au compte et app setting.about.shortcuts.registerMediator=Inscrire le médiateur (médiateur/arbitre seulement) setting.about.shortcuts.registerMediator.value=Naviguez jusqu'au compte et appuyez sur: {0} -setting.about.shortcuts.reOpenDispute=Réouverture d'un litige déjà clos (médiateur/arbitre seulement) -setting.about.shortcuts.reOpenDispute.value=Sélectionnez le litige fermé et appuyez sur: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Ouvrir la fenêtre pour la signature de l'âge du compte (anciens arbitres seulement) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Naviguer vers l''ancienne vue de l''arbitre et appuyer sur: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Envoyer un message d'alerte ou de mise à j setting.about.shortcuts.sendFilter=Définir le filtre (activité privilégiée) setting.about.shortcuts.sendPrivateNotification=Envoyer une notification privée à un pair (activité privilégiée) -setting.about.shortcuts.sendPrivateNotification.value=Ouvrez l''information du pair via l''avatar ou le litige et pressez: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Veuillez vous assurer que vous respectez les ex account.altcoin.popup.wallet.confirm=Je comprends et confirme que je sais quel portefeuille je dois utiliser. account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. account.altcoin.popup.arq.msg=Le trading d'ARQ sur Bisq exige que vous compreniez et remplissiez les exigences suivantes:\n\nPour envoyer des ARQ, vous devez utiliser soit le portefeuille officiel ArQmA GUI soit le portefeuille ArQmA CLI avec le flag store-tx-info activé (par défaut dans les nouvelles versions). Veuillez vous assurer que vous pouvez accéder à la tx key car cela pourrait être nécessaire en cas de litige.\narqma-wallet-cli (utiliser la commande get_tx_key)\narqma-wallet-gui (allez dans l'onglet historique et cliquez sur le bouton (P) pour accéder à la preuve de paiement).\n\nAvec un l'explorateur de bloc normal, le transfert n'est pas vérifiable.\n\nVous devez fournir au médiateur ou à l'arbitre les données suivantes en cas de litige:\n- Le tx de la clé privée\n- Le hash de la transaction\n- L'adresse publique du destinataire\n\nSi vous manquez de communiquer les données ci-dessus ou si vous utilisez un portefeuille incompatible, vous perdrez le litige. L'expéditeur des ARQ est responsable de la transmission au médiateur ou à l'arbitre de la vérification du transfert ces informations relatives au litige.\n\nIl n'est pas nécessaire de fournir l'ID du paiement, seulement l'adresse publique normale.\nSi vous n'êtes pas sûr de ce processus, visitez le canal discord ArQmA (https://discord.gg/s9BQpJT) ou le forum ArQmA (https://labs.arqma.com) pour obtenir plus d'informations. -account.altcoin.popup.xmr.msg=Le trading de XMR sur Bisq exige que vous compreniez et remplissiez les conditions suivantes:\n\nProuver les paiements : comme Monero est une cryptomonnaie privée, certains détails de la transaction ne sont pas disponibles publiquement dans la blockchain. En cas de litige, le médiateur ou l'arbitre ont besoin de vérifier si la transaction a vraiment été effectuée. Dans le cas de Bisq, l'expéditeur de la transaction XMR est celui qui doit fournir ces renseignements au médiateur ou à l'arbitre en cas de littige. Pour ce faire, vous devez envoyer des XMR en utilisant un porte-monnaie qui fournit les renseignements nécessaires pour prouver que le paiement a été effectué, ce qui comprend:\n\n- la clé de transaction (Tx Key, Tx Secret Key ou Tx Private Key)\n- l'ID de la transaction (Tx ID ou Tx Hash)\n- l'adresse de destination (recipient's address)\n\nCes informations se trouvent dans les portefeuilles officiels de Monero GUI & CLI, MyMonero, et Exodus (bureau) ainsi que dans Cake Wallet, MyMonero, et Monerujo (mobile), dans les endroits suivants:\n\n- Monero GUI : allez à l'onglet Transactions\n- Monero CLI : utilisez la commande get_tx_key TXID. Le flag store-tx-info doit être activé (activé par défaut dans les nouvelles versions)\n- Autres portefeuilles : allez dans l'historique des transactions et recherchez la clé de transaction (Tx key ou Secret key) et l'adresse de destination dans une transaction envoyée. L'option "Enregistrer l'adresse du destinataire" doit être activée dans les paramètres de Cake Wallet.\n\nSi vous utilisez un portefeuille différent de celui mentionné ci-dessus, assurez-vous de pouvoir accéder à ces trois informations. Puisque la clé de transaction et l'adresse de destination sont stockées dans le logiciel de portefeuille Monero et qu'elles ne peuvent pas être récupérées dans la blockchain Monero, vous ne devez jamais supprimer ou restaurer votre portefeuille Monero avant qu'une transaction Bisq ne soit effectuée. Ne pas fournir les données ci-dessus entraînera la perte du litige.\n\nPaiements par chèque : avec ces trois informations, la vérification qu'une quantité de Monero a été envoyée à une adresse spécifique peut être effectuée de la manière suivante:\n\n- Monero GUI : changez le portefeuille en mode Avancé et allez à Avancé > Prouver/vérifier > Vérifier la transaction\n- Monero CLI : utiliser la commande check_tx_key TXID TXKEY ADDRESS\n- L'outil XMR checktx (https://xmr.llcoins.net/checktx.html)\n- Explorez le site web de Monero (https://www.exploremonero.com/receipt)\n\nSi vous ne comprenez toujours pas ce processus, visitez (https://www.getmonero.org/resources/user-guides/prove-payment.html) pour trouver plus d'informations ou poser une question sur le sous-reddit de support Monero (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Trading MSR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending MSR, you need to use either the official Masari GUI wallet, Masari CLI wallet with the store-tx-info flag enabled (enabled by default) or the Masari web wallet (https://wallet.getmasari.org). Please be sure you can access the tx key as that would be required in case of a dispute.\nmasari-wallet-cli (use the command get_tx_key)\nmasari-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nMasari Web Wallet (goto Account -> transaction history and view details on your sent transaction)\n\nVerification can be accomplished in-wallet.\nmasari-wallet-cli : using command (check_tx_key).\nmasari-wallet-gui : on the Advanced > Prove/Check page.\nVerification can be accomplished in the block explorer \nOpen block explorer (https://explorer.getmasari.org), use the search bar to find your transaction hash.\nOnce transaction is found, scroll to bottom to the 'Prove Sending' area and fill in details as needed.\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The MSR sender is responsible for providing verification of the MSR transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process, ask for help on the Official Masari Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Price node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets API operator +dao.bond.bondedRoleType.MARKETS_OPERATOR=Opérateur de marchés # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Opérateur de l'explorateur BSQ +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Opérateur relais pour les notifications mobile # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=Notes de synthèse disputeSummaryWindow.addSummaryNotes=Ajouter des notes de synthèse disputeSummaryWindow.close.button=Fermer le ticket -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nProchaines étapes:\nOuvrir la transaction et accepter ou rejeter la suggestion du médiateur -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nProchaines étapes:\nAucune autre action n'est requise de votre part. Si l'arbitre a rendu une décision en votre faveur, vous verrez une opération de " Remboursement à la suite de l'arbitrage " dans Fonds/Transactions + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=Vous devez également clore le ticket des pairs de trading ! disputeSummaryWindow.close.txDetails.headline=Publier la transaction de remboursement disputeSummaryWindow.close.txDetails.buyer=L''acheteur reçoit {0} à l''adresse: {1}\n disputeSummaryWindow.close.txDetails.seller=Le vendeur reçoit {0} à l''adresse: {1}\n disputeSummaryWindow.close.txDetails=Dépenser: {0}\n{1}{2}Frais de transaction: {3} ({4} satoshis/octet)\nTaille de la transaction: {5} Kb\n\nÊtes-vous sûr de vouloir publier cette transaction ? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} Outil de secours du portefeuille emptyWalletWindow.info=Veuillez utiliser ceci qu'en cas d'urgence si vous ne pouvez pas accéder à vos fonds à partir de l'interface utilisateur.\n\nVeuillez remarquer que touts les ordres en attente seront automatiquement fermés lors de l'utilisation de cet outil.\n\nAvant d'utiliser cet outil, veuillez sauvegarder votre répertoire de données. Vous pouvez le faire sur \"Compte/sauvegarde\".\n\nVeuillez nous signaler votre problème et déposer un rapport de bug sur GitHub ou sur le forum Bisq afin que nous puissions enquêter sur la source du problème. emptyWalletWindow.balance=Votre solde disponible sur le portefeuille @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=Nœuds relais avec prix filtrés (adresses onion sé filterWindow.btcNode=Nœuds Bitcoin filtrés (adresses séparées par une virgule + port) filterWindow.preventPublicBtcNetwork=Empêcher l'utilisation du réseau public Bitcoin filterWindow.disableDao=Désactiver la DAO +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Version minimale requise pour la DAO filterWindow.disableTradeBelowVersion=Version min. nécessaire pour pouvoir échanger filterWindow.add=Ajouter le filtre @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=Confirmer: Acceptez l''ordre de {0} Bitcoin offerDetailsWindow.creationDate=Date de création offerDetailsWindow.makersOnion=Adresse onion du maker -qRCodeWindow.headline=QR-Code -qRCodeWindow.msg=Veuillez utiliser ce QR-Code pour approvisionner votre portefeuille Bisq à partir de votre portefeuille externe. +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=Demande de paiement:\n{0} selectDepositTxWindow.headline=Sélectionner la transaction de dépôt en cas de litige @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=Envoyer une notification privée showWalletDataWindow.walletData=Données du portefeuille showWalletDataWindow.includePrivKeys=Inclure les clés privées +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=Conditions d'utilisation @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=La transaction de dépôt de l''échan error.closedTradeWithNoDepositTx=La transaction de dépôt de l'échange fermé avec l''ID d'échange {0} est nulle.\n\nVeuillez redémarrer l''application pour nettoyer la liste des transactions fermées. popup.warning.walletNotInitialized=Le portefeuille n'est pas encore initialisé +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=Vous avez probablement une mauvaise version de Bisq sur cet ordinateur.\nL''architecture de votre ordinateur est: {0}.\nLa binary Bisq que vous avez installé est: {1}.\nVeuillez éteindre et réinstaller une bonne version ({2}). popup.warning.incompatibleDB=Nous avons détecté des fichiers de base de données incompatibles!\n\nCes fichier(s) de base de données ne sont pas compatibles avec notre code de base actuel:\n{0}\n\nNous avons fait une sauvegarde de fichier(s) corrompus et appliqué les valeurs par défaut sur une nouvelle version de la base de données.\n\nLa sauvegarde se trouve à l''adresse:\n{1}/db/backup_of_corrupted_data.\n\nVeuillez vérifier si vous avez la dernière version de Bisq installée.\nVous pouvez le télécharger à l''adresse:\nhttps://bisq.network/downloads\n\nVeuillez redémarrer l''application. popup.warning.startupFailed.twoInstances=Bisq est déjà lancé. Vous ne pouvez pas lancer deux instances de bisq. @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=Votre portefeuille BSQ ne dispose pas d popup.warning.messageTooLong=Votre message dépasse la taille maximale autorisée. Veuillez l'envoyer en plusieurs parties ou le télécharger depuis un service comme https://pastebin.com. popup.warning.lockedUpFunds=Vous avez des fonds bloqués d''une transaction qui a échoué.\nSolde bloqué: {0}\nAdresse de la tx de dépôt: {1}\nID de l''échange: {2}.\n\nVeuillez ouvrir un ticket de support en sélectionnant la transaction dans l'écran des transactions ouvertes et en appuyant sur \"alt + o\" ou \"option + o\". -popup.warning.nodeBanned=Un des nœuds {0} a été banni. Veuillez redémarrer votre application pour vous assurer de ne pas être connecté au nœud banni. +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=Relais de prix popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Veuillez faire une mise à jour vers la dernière version de Bisq. Une mise à jour obligatoire a été publiée, laquelle désactive le trading sur les anciennes versions. Veuillez consulter le Forum Bisq pour obtenir plus d'informations. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=Afin de s'assurer que les deux traders suivent le popup.info.cashDepositInfo=Veuillez vous assurer d''avoir une succursale de l''établissement bancaire dans votre région afin de pouvoir effectuer le dépôt en espèces.\nL''identifiant bancaire (BIC/SWIFT) de la banque du vendeur est: {0}. popup.info.cashDepositInfo.confirm=Je confirme que je peux effectuer le dépôt. popup.info.shutDownWithOpenOffers=Bisq est en cours de fermeture, mais des ordres sont en attente.\n\nCes ordres ne seront pas disponibles sur le réseau P2P si Bisq est éteint, mais ils seront republiés sur le réseau P2P la prochaine fois que vous lancerez Bisq.\n\nPour garder vos ordres en ligne, laissez Bisq en marche et assurez-vous que cet ordinateur reste aussi en ligne (pour cela, assurez-vous qu'il ne passe pas en mode veille...la veille du moniteur ne pose aucun problème). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=Notification privée importante! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: Une plateforme d''échange décentralisée sur le rése # GUI Util #################################################################### -guiUtil.miningFeeInfo=Veuillez vous assurer que les frais de minage utilisés sur votre portefeuille externe sont d''au moins {0} satoshis/byte. Dans le cas contraire, les transactions d''échange ne peuvent pas être confirmées et l''échange aboutirait à un litige. +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=Les comptes de trading sont sauvegardés vers l''arborescence:\n{0} guiUtil.accountExport.noAccountSetup=Vous n'avez pas de comptes de trading configurés pour exportation. @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=Compte de paiement créé il y a {0}. peerInfoIcon.tooltip.unknownAge=Ancienneté du compte de paiement inconnue. tooltip.openPopupForDetails=Ouvrir le popup pour obtenir des détails +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=Ouvrir un explorateur de blockchain externe pour l''adresse: {0} tooltip.openBlockchainForTx=Ouvrir un explorateur de blockchain externe pour la transaction: {0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=L'ouverture d'un portefeuille Bitcoin par dé peerInfoIcon.tooltip={0}\nTag: {1} txIdTextField.copyIcon.tooltip=Copier l'ID de la transaction dans le presse-papiers -txIdTextField.blockExplorerIcon.tooltip=Ouvrir l'explorateur de blockchain avec cet ID de transaction +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=Ouvrir l'explorateur de blockchain avec navigation.account=\"Compte\" navigation.account.walletSeed=\"Compte/Seed du portefeuille\" -navigation.funds.availableForWithdrawal=\"Fond/Envoyer des fonds\" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"Portfolio/Mes ordres en cours\" navigation.portfolio.pending=\"Portfolio/Échanges en cours\" navigation.portfolio.closedTrades=\"Portfolio/Historique\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=Vos portefeuilles sont cryptés.\n\nAprès la rest seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=Portefeuilles restaurés avec succès grâce aux nouveaux mots de la seed.\n\nVous devez arrêter et redémarrer l'application. seed.restore.error=Une erreur est survenue lors de la restauration des portefeuilles avec les mots composant la seed.{0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=Compte payment.account.no=N° de compte payment.account.name=Nom du compte payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=Nom et prénoms du propriétaire du compte payment.account.fullName=Nom complet (prénom, deuxième prénom, nom de famille) payment.account.state=État/Département/Région diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 0574ac0dad1..81173b5701c 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -35,7 +35,7 @@ shared.no=いいえ shared.iUnderstand=了解 shared.na=N/A shared.shutDown=終了 -shared.reportBug=Githubにバグを報告 +shared.reportBug=Githubでバグを報告 shared.buyBitcoin=ビットコインを買う shared.sellBitcoin=ビットコインを売る shared.buyCurrency={0}を買う @@ -62,10 +62,10 @@ shared.priceWithCur={0}の価格 shared.priceInCurForCur=1{1}当りの{0}の価格 shared.fixedPriceInCurForCur=1 {1}あたりの{0}で価格を固定 shared.amount=金額 -shared.txFee=Transaction Fee -shared.tradeFee=Trade Fee -shared.buyerSecurityDeposit=Buyer Deposit -shared.sellerSecurityDeposit=Seller Deposit +shared.txFee=トランザクション手数料 +shared.tradeFee=トレード手数料 +shared.buyerSecurityDeposit=買い手敷金 +shared.sellerSecurityDeposit=売り手敷金 shared.amountWithCur={0}の金額 shared.volumeWithCur={0}取引高 shared.currency=通貨 @@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (下限 - 上限) shared.removeOffer=オファー取消 shared.dontRemoveOffer=オファー取り消さない shared.editOffer=オファーを編集 -shared.openLargeQRWindow=QRコードを大きく表示 +shared.openLargeQRWindow=大きいQRコードウィンドウを開く shared.tradingAccount=取引アカウント -shared.faq=FAQページを参照 +shared.faq=よくある質問ページを訪れる shared.yesCancel=はい、取り消します shared.nextStep=次へ shared.selectTradingAccount=取引アカウントを選択 shared.fundFromSavingsWalletButton=Bisqウォレットから資金を移動する shared.fundFromExternalWalletButton=外部のwalletを開く -shared.openDefaultWalletFailed=既定のビットコインウォレットが開けませんでした。何らかのビットコインウォレットはインストールされていますか? +shared.openDefaultWalletFailed=ビットコインウォレットのアプリを開けませんでした。インストールされているか確認して下さい。 shared.distanceInPercent=市場価格から % の乖離 shared.belowInPercent=市場価格から%以下 shared.aboveInPercent=市場価格から%以上 shared.enterPercentageValue=%を入力 shared.OR=または -shared.notEnoughFunds=あなたのBisqウォレットに十分な資金がありません。\n{0}が必要ですが、Bisqウォレットには{1}しかありません。\n\n外部のビットコインウォレットから入金するか、または「資金/資金の受取」でBisqウォレットに入金してください。 +shared.notEnoughFunds=このトランザクションには、Bisqウォレットに資金が足りません。\n{0}が必要ですが、Bisqウォレットには{1}しかありません。\n\n外部のビットコインウォレットから入金するか、または「資金 > 資金の受取」でBisqウォレットに入金してください。 shared.waitingForFunds=資金を待っています shared.depositTransactionId=入金トランザクションID shared.TheBTCBuyer=BTC買い手 @@ -122,8 +122,8 @@ shared.noDateAvailable=日付がありません shared.noDetailsAvailable=詳細不明 shared.notUsedYet=未使用 shared.date=日付 -shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? -shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n +shared.sendFundsDetailsWithFee=送金中: {0}\n送金元アドレス: {1}\n入金先アドレス: {2}\n必要なマイニング手数料: {3} ({4} Satoshis/byte)\nトランザクションサイズ: {5} Kb\n\n入金先の受け取る金額: {6}\n\n本当にこの金額を出金しますか? +shared.sendFundsDetailsDust=Bisqがこのトランザクションはダストの最小閾値以下のおつりアウトプットを生じることを検出しました(それにしたがって、ビットコインのコンセンサス・ルールによって許されない)。代わりに、その ({0} satoshi{1}) のダストはマイニング手数料に追加されます。\n\n\n shared.copyToClipboard=クリップボードにコピー shared.language=言語 shared.country=国 @@ -131,7 +131,7 @@ shared.applyAndShutDown=適用して終了 shared.selectPaymentMethod=支払い方法を選ぶ shared.accountNameAlreadyUsed=そのアカウント名は既に使用されています。\n別の名前を使用してください。 shared.askConfirmDeleteAccount=選択したアカウントを本当に削除しますか? -shared.cannotDeleteAccount=このアカウントは取引中かオファーを入れているため消去することはできません。 +shared.cannotDeleteAccount=このアカウントはトレード中(それともオファーを入れている中)ため消去することはできません。 shared.noAccountsSetupYet=アカウントが設定されていません shared.manageAccounts=アカウント管理 shared.addNewAccount=アカウントを追加 @@ -153,7 +153,7 @@ shared.save=保存 shared.onionAddress=Onionアドレス shared.supportTicket=サポートチケット shared.dispute=係争 -shared.mediationCase=mediation case +shared.mediationCase=調停事件 shared.seller=売り手 shared.buyer=買い手 shared.allEuroCountries=ユーロ全諸国 @@ -190,7 +190,7 @@ shared.makerTxFee=メイカー: {0} shared.takerTxFee=テイカー: {0} shared.securityDepositBox.description=BTC {0} のセキュリティデポジット shared.iConfirm=確認します -shared.tradingFeeInBsqInfo=equivalent to {0} used as trading fee +shared.tradingFeeInBsqInfo=トレード手数料として使用される{0}と同じ shared.openURL={0} をオープン shared.fiat=法定通貨 shared.crypto=暗号通貨 @@ -208,14 +208,15 @@ shared.votes=投票 shared.learnMore=もっと詳しく知る shared.dismiss=却下する shared.selectedArbitrator=選択された調停人 -shared.selectedMediator=Selected mediator +shared.selectedMediator=選択された調停人 shared.selectedRefundAgent=選択された調停人 shared.mediator=調停者 shared.arbitrator=調停人 shared.refundAgent=調停人 shared.refundAgentForSupportStaff=Refund agent -shared.delayedPayoutTxId=Refund collateral transaction ID -shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to +shared.unconfirmedTransactionsLimitReached=現在、非確認されたトランザクションが多すぎます。しばらく待ってからもう一度試して下さい。 #################################################################### @@ -256,8 +257,8 @@ mainView.footer.bsqInfo.synchronizing=/ DAOと同期中 mainView.footer.btcInfo.synchronizingWith=同期中 mainView.footer.btcInfo.synchronizedWith=同期されています: mainView.footer.btcInfo.connectingTo=接続中: -mainView.footer.btcInfo.connectionFailed=Connection failed to -mainView.footer.p2pInfo=Bisq network peers: {0} +mainView.footer.btcInfo.connectionFailed=接続失敗 +mainView.footer.p2pInfo=Bisqネットワークピア: {0} mainView.footer.daoFullNode=DAOのフルノード mainView.bootstrapState.connectionToTorNetwork=(1/4) Torネットワークに接続中... @@ -267,15 +268,15 @@ mainView.bootstrapState.initialDataReceived=(4/4) 初期データを受信しま mainView.bootstrapWarning.noSeedNodesAvailable=シードノードが見つかりません mainView.bootstrapWarning.noNodesAvailable=シードノードとピアが見つかりません -mainView.bootstrapWarning.bootstrappingToP2PFailed=Bootstrapping to Bisq network failed +mainView.bootstrapWarning.bootstrappingToP2PFailed=Bisqネットワークとの同期に失敗しました mainView.p2pNetworkWarnMsg.noNodesAvailable=データを要求するためのシードノードと永続ピアが見つかりません。\nインターネット接続を確認するか、アプリケーションを再起動してみてください。 -mainView.p2pNetworkWarnMsg.connectionToP2PFailed=Connecting to the Bisq network failed (reported error: {0}).\nPlease check your internet connection or try to restart the application. +mainView.p2pNetworkWarnMsg.connectionToP2PFailed=Bisqネットワークへの接続に失敗しました(報告されたエラー: {0})。\nインターネット接続を確認するか、アプリケーションを再起動してみてください。 mainView.walletServiceErrorMsg.timeout=タイムアウトのためビットコインネットワークへの接続に失敗しました mainView.walletServiceErrorMsg.connectionError=次のエラーのためビットコインネットワークへの接続に失敗しました: {0} -mainView.walletServiceErrorMsg.rejectedTxException=A transaction was rejected from the network.\n\n{0} +mainView.walletServiceErrorMsg.rejectedTxException=トランザクションはネットワークに拒否されました。\n\n{0} mainView.networkWarning.allConnectionsLost=全ての{0}のネットワークピアへの接続が切断されました。\nインターネット接続が切断されたか、コンピュータがスタンバイモードになった可能性があります。 mainView.networkWarning.localhostBitcoinLost=ローカルホストビットコインノードへの接続が切断されました。\nBisqアプリケーションを再起動して他のビットコインノードに接続するか、ローカルホストのビットコインノードを再起動してください。 @@ -336,26 +337,27 @@ offerbook.offerersAcceptedBankSeats=利用可能な銀行の国名(テイカ offerbook.availableOffers=利用可能なオファー offerbook.filterByCurrency=通貨でフィルター offerbook.filterByPaymentMethod=支払い方法でフィルター -offerbook.timeSinceSigning=Time since signing -offerbook.timeSinceSigning.info=This account was verified and {0} -offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts -offerbook.timeSinceSigning.info.peer=signed by a peer, waiting for limits to be lifted -offerbook.timeSinceSigning.info.peerLimitLifted=signed by a peer and limits were lifted -offerbook.timeSinceSigning.info.signer=signed by peer and can sign peer accounts (limits lifted) -offerbook.timeSinceSigning.info.banned=account was banned +offerbook.timeSinceSigning=Signed since +offerbook.timeSinceSigning.info=このアカウントは認証されまして、{0} +offerbook.timeSinceSigning.info.arbitrator=調停人に署名されました。ピアアカウントも署名できます +offerbook.timeSinceSigning.info.peer=ピアが署名しました。制限の解除を待ちます +offerbook.timeSinceSigning.info.peerLimitLifted=ピアが署名しました。制限は解除されました +offerbook.timeSinceSigning.info.signer=ピアが署名しました。ピアアカウントも署名できます(制限は解除されました) +offerbook.timeSinceSigning.info.banned=このアカウントは禁止されました offerbook.timeSinceSigning.daysSinceSigning={0}日 -offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing +offerbook.timeSinceSigning.daysSinceSigning.long=署名する後から {0} +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. -offerbook.timeSinceSigning.notSigned=Not signed yet +offerbook.timeSinceSigning.notSigned=まだ署名されていません offerbook.timeSinceSigning.notSigned.noNeed=N/A -shared.notSigned=This account hasn't been signed yet -shared.notSigned.noNeed=This account type doesn't use signing +shared.notSigned=このアカウントはまだ署名されていません +shared.notSigned.noNeed=この種類のアカウントは署名を使用しません offerbook.nrOffers=オファー数: {0} offerbook.volume={0} (下限 - 上限) -offerbook.deposit=Deposit BTC (%) -offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.deposit=BTCの敷金(%) +offerbook.deposit.help=トレードを保証するため、両方の取引者が支払う敷金。トレードが完了されたら、返還されます。 offerbook.createOfferToBuy={0} を購入するオファーを作成 offerbook.createOfferToSell={0} を売却するオファーを作成 @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=オファー無効化に失敗:\n{0} offerbook.activateOffer.failed=オファー公開に失敗:\n{0} offerbook.withdrawFundsHint=あなたが支払った資金を{0}画面から出金できます。 -offerbook.warning.noTradingAccountForCurrency.headline=指定の通貨では取引アカウントがありません -offerbook.warning.noTradingAccountForCurrency.msg=選択した通貨のトレードアカウントがありません。\n既存のトレードアカウントの1つを使ってオファーを作成しますか? -offerbook.warning.noMatchingAccount.headline=一致するトレードアカウントがありません -offerbook.warning.noMatchingAccount.msg=このオファーを利用するには、この支払い方法を使用する支払いアカウントを設定する必要があります。\n\n今すぐ設定しますか? +offerbook.warning.noTradingAccountForCurrency.headline=指定の通貨では支払いアカウントがありません +offerbook.warning.noTradingAccountForCurrency.msg=選択した通貨の支払いアカウントがありません。\n\n他の通貨でオファーを作成しますか? +offerbook.warning.noMatchingAccount.headline=一致する支払いアカウントがありません +offerbook.warning.noMatchingAccount.msg=このオファーは、まだ設定されない支払い方法を利用します。\n\n今すぐ新しい支払いアカウントを設定しますか? offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due to counterparty trade restrictions @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=そのオファーで使用されている offerbook.warning.nodeBlocked=そのonionアドレスはBisq開発者によってブロックされました。\nおそらくその取引者からのオファーを受けるときに問題が引きおこさる未処理のバグがあります。 offerbook.warning.requireUpdateToNewVersion=あなたのBisqのバージョンではもうトレードの互換性がありません。\nhttps://bisq.network/downloads で最新のBisqバージョンに更新してください。 offerbook.warning.tradeLimitNotMatching=Your payment account has been created {0} ago. Your trade limit is based on the account age and is not sufficient for that offer.\n\nYour trade limit is: {1}\nThe min. trade amount of the offer is: {2}.\n\nYou cannot take that offer at the moment. Once your account is older than 2 months this restriction gets removed. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=市場価格で売却されるでしょう(毎分更新されます)。 offerbook.info.buyAtMarketPrice=市場価格で購入されるでしょう(毎分更新されます)。 @@ -463,10 +465,10 @@ createOffer.tac=このオファーを公開することで、この画面で定 createOffer.currencyForFee=取引手数料 createOffer.setDeposit=買い手のセキュリティデポジット (%) createOffer.setDepositAsBuyer=購入時のセキュリティデポジット (%) -createOffer.setDepositForBothTraders=Set both traders' security deposit (%) +createOffer.setDepositForBothTraders=両方の取引者の保証金を設定する(%) createOffer.securityDepositInfo=あなたの買い手のセキュリティデポジットは{0}です createOffer.securityDepositInfoAsBuyer=あなたの購入時のセキュリティデポジットは{0}です -createOffer.minSecurityDepositUsed=Min. buyer security deposit is used +createOffer.minSecurityDepositUsed=最小値の買い手の保証金は使用されます #################################################################### @@ -541,7 +543,7 @@ portfolio.tab.history=履歴 portfolio.tab.failed=失敗 portfolio.tab.editOpenOffer=オファーを編集 -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=ブロックチェーンの承認をお待ち下さい portfolio.pending.step2_buyer.startPayment=支払い開始 @@ -550,9 +552,39 @@ portfolio.pending.step3_buyer.waitPaymentArrived=支払いが到着するまで portfolio.pending.step3_seller.confirmPaymentReceived=支払いを受領したことを確認して下さい portfolio.pending.step5.completed=完了 +portfolio.pending.step3_seller.autoConf.status.label=ステータスを自動確認する +portfolio.pending.autoConf=自動確認されました +portfolio.pending.autoConf.blocks=XMR承認: {0} / 必要: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR承認: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=トランザクションはまだメモリプールに見られません +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=有効なトランザクションID/トランザクション・キーはありません +portfolio.pending.autoConf.state.filterDisabledFeature=開発者により無効されました + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=自動確認機能は無効されました。{0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=ピアは無効データを提供しました。{0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=デポジットトランザクションが発行されました。\n{0}は、支払いを開始する前に少なくとも1つのブロックチェーンの承認を待つ必要があります。 portfolio.pending.step1.warn=The deposit transaction is still not confirmed. This sometimes happens in rare cases when the funding fee of one trader from an external wallet was too low. -portfolio.pending.step1.openForDispute=The deposit transaction is still not confirmed. You can wait longer or contact the mediator for assistance. +portfolio.pending.step1.openForDispute=デポジットトランザクションがまだ承認されていません。もう少し待つか、助けを求めて調停人に連絡できます。 # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2.confReached=あなたの取引は少なくとも1つのブロックチェーン承認に達しました。\n(必要に応じてさらに承認を待つことができます - 6回の承認が非常に安全であると考えられます。)\n\n @@ -584,12 +616,13 @@ portfolio.pending.step2_buyer.bank=オンラインバンキングのWebページ # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=提供された連絡先でBTCの売り手に連絡し、{0}を支払うためのミーティングを準備してください。\n\n portfolio.pending.step2_buyer.startPaymentUsing={0}を使用して支払いを開始 +portfolio.pending.step2_buyer.recipientsAccountData=受領者 {0} portfolio.pending.step2_buyer.amountToTransfer=振替金額 portfolio.pending.step2_buyer.sellersAddress=売り手の{0}アドレス portfolio.pending.step2_buyer.buyerAccount=使用されるあなたの支払いアカウント portfolio.pending.step2_buyer.paymentStarted=支払いが開始されました -portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1}. -portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.Please contact the mediator for assistance. +portfolio.pending.step2_buyer.warn={0}の支払いはまだ完了していません!\nトレードは{1}までに完了しなければなりません。 +portfolio.pending.step2_buyer.openForDispute=支払いを完了していません!\nトレードの最大期間が経過しました。助けを求めるには調停人に連絡してください。 portfolio.pending.step2_buyer.paperReceipt.headline=領収書をBTCの売り手へ送付しましたか? portfolio.pending.step2_buyer.paperReceipt.msg=覚えておいてください:\n領収書に「返金無し(NO REFUNDS)」と記載してください。\nそれからそれを2部に分け、写真を撮り、そしてBTCの売り手のEメールアドレスへそれを送ってください。 portfolio.pending.step2_buyer.moneyGramMTCNInfo.headline=認証番号と領収書を送信 @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might veri portfolio.pending.step2_buyer.confirmStart.headline=支払いが開始したことを確認 portfolio.pending.step2_buyer.confirmStart.msg=トレーディングパートナーへの{0}支払いを開始しましたか? portfolio.pending.step2_buyer.confirmStart.yes=はい、支払いを開始しました - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=支払証明を提出していません +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=入力が32バイトの16進値ではありません。 +portfolio.pending.step2_buyer.confirmStart.warningButton=無視して続ける portfolio.pending.step2_seller.waitPayment.headline=支払いをお待ちください portfolio.pending.step2_seller.f2fInfo.headline=買い手の連絡先 portfolio.pending.step2_seller.waitPayment.msg=デポジットトランザクションには、少なくとも1つのブロックチェーン承認があります。\nBTCの買い手が{0}の支払いを開始するまで待つ必要があります。 @@ -611,7 +647,7 @@ portfolio.pending.step2_seller.openForDispute=The BTC buyer has not started thei portfolio.pending.step2_seller.refresh=Refresh Trade State portfolio.pending.step2_seller.refreshInfo=Sometimes P2P network messages acknowledging payment are not delivered, causing trades to get stuck. Hit the button below to make your peer resend the last message. tradeChat.chatWindowTitle=Chat window for trade with ID ''{0}'' -tradeChat.openChat=Open chat window +tradeChat.openChat=チャットウィンドウを開く tradeChat.rules=You can communicate with your trade peer to resolve potential problems with this trade.\nIt is not mandatory to reply in the chat.\nIf a trader violates any of the rules below, open a dispute and report it to the mediator or arbitrator.\n\nChat rules:\n\t● Do not send any links (risk of malware). You can send the transaction ID and the name of a block explorer.\n\t● Do not send your seed words, private keys, passwords or other sensitive information!\n\t● Do not encourage trading outside of Bisq (no security).\n\t● Do not engage in any form of social engineering scam attempts.\n\t● If a peer is not responding and prefers to not communicate via chat, respect their decision.\n\t● Keep conversation scope limited to the trade. This chat is not a messenger replacement or troll-box.\n\t● Keep conversation friendly and respectful. # suppress inspection "UnusedProperty" @@ -632,8 +668,8 @@ portfolio.pending.step3_buyer.wait.info={0}の支払いを受け取るためのB portfolio.pending.step3_buyer.wait.msgStateInfo.label=支払いはメッセージステータスを開始 portfolio.pending.step3_buyer.warn.part1a={0} ブロックチェーン上で portfolio.pending.step3_buyer.warn.part1b=支払いプロバイダ(銀行など)で -portfolio.pending.step3_buyer.warn.part2=The BTC seller still has not confirmed your payment. Please check {0} if the payment sending was successful. -portfolio.pending.step3_buyer.openForDispute=The BTC seller has not confirmed your payment! The max. period for the trade has elapsed. You can wait longer and give the trading peer more time or request assistance from the mediator. +portfolio.pending.step3_buyer.warn.part2=BTCの売り手はまだあなたの支払いを確認していません!支払いの送信が成功したかどうか{0}を確認してください。 +portfolio.pending.step3_buyer.openForDispute=BTCの売り手があなたの支払いを確認していません!トレードの最大期間が経過しました。もっと長く待って取引相手にもっと時間を与えるか、調停人から援助を求めることができます。 # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.part=あなたのトレード相手は、彼らが{0}の支払いを開始したことを確認しました。\n\n portfolio.pending.step3_seller.altcoin.explorer=あなたの好きな{0}ブロックチェーンエクスプローラで @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=受取額 portfolio.pending.step3_seller.yourAddress=あなたの{0} アドレス portfolio.pending.step3_seller.buyersAddress=買い手の{0} アドレス portfolio.pending.step3_seller.yourAccount=あなたのトレードアカウント -portfolio.pending.step3_seller.buyersAccount=買い手のトレードアカウント +portfolio.pending.step3_seller.xmrTxHash=トランザクションID +portfolio.pending.step3_seller.xmrTxKey=トランザクション・キー +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=支払い受領を確認 portfolio.pending.step3_seller.buyerStartedPayment=BTCの買い手が{0}の支払いを開始しました。\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=あなたのアルトコインウォレットやブロックエクスプローラーでブロックチェーンの確認を確認し、十分なブロックチェーンの承認があるときに支払いを確認してください。 @@ -669,7 +707,7 @@ portfolio.pending.step3_seller.onPaymentReceived.part1=あなたの取引相手 portfolio.pending.step3_seller.onPaymentReceived.fiat=このトランザクションのトレードID(「支払理由」のテキスト)は \"{0} \"\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, don''t confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n -portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the BTC buyer and the security deposit will be refunded.\n\n +portfolio.pending.step3_seller.onPaymentReceived.note=領収書の確認が済むとすぐに、ロックされたトレード金額がBTCの買い手に解放され、保証金が返金されます。\n\n portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=支払いを受け取ったことを確認 portfolio.pending.step3_seller.onPaymentReceived.confirm.yes=はい、支払いを受け取りました portfolio.pending.step3_seller.onPaymentReceived.signer=IMPORTANT: By confirming receipt of payment, you are also verifying the account of the counterparty and signing it accordingly. Since the account of the counterparty hasn't been signed yet, you should delay confirmation of the payment as long as possible to reduce the risk of a chargeback. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Please use this function only in emergen portfolio.pending.timeLockNotOver=You have to wait until ≈{0} ({1} more blocks) before you can open an arbitration dispute. portfolio.pending.error.depositTxNull=The deposit transaction is null. You cannot open a dispute without a valid deposit transaction. Please go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. -portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. The trade gets moved to the failed trades section. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=The deposit transaction is not confirmed. You can not open an arbitration dispute with an unconfirmed deposit transaction. Please wait until it is confirmed or go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. portfolio.pending.notification=通知 @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=完了 portfolio.closed.ticketClosed=Arbitrated portfolio.closed.mediationTicketClosed=Mediated @@ -763,8 +817,8 @@ portfolio.closed.canceled=キャンセルされています portfolio.failed.Failed=失敗 portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets support.filter=Search disputes support.filter.prompt=トレードID、日付、onionアドレスまたはアカウントデータを入力してください + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=プライベート通知を送信 -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=プライベート通知 +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=オープンなチケットはありません support.sendingMessage=メッセージを送信中 support.receiverNotOnline=Receiver is not online. Message is saved to their mailbox. @@ -905,6 +970,9 @@ support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nB support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nBisq version: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Mediator''s node address: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=ネットワーク情報 settings.tab.about=About setting.preferences.general=一般設定 -setting.preferences.explorer=ビットコインのブロックエクスプローラ -setting.preferences.explorer.bsq=BSQ block explorer +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=市場価格からの最大偏差 setting.preferences.avoidStandbyMode=スタンバイモードを避ける +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge={0}%以上の値は許可されていません。 setting.preferences.txFee=出金取引手数料 (satoshis/byte) setting.preferences.useCustomValue=任意の値を使う @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool fo setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN -setting.about.shortcuts.showDisputeStatistics=Show summary of all disputes -setting.about.shortcuts.showDisputeStatistics.value=Navigate to disputes view and press: {0} - setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navigate to account and press: setting.about.shortcuts.registerMediator=Register mediator (mediator/arbitrator only) setting.about.shortcuts.registerMediator.value=Navigate to account and press: {0} -setting.about.shortcuts.reOpenDispute=Re-open already closed dispute (mediator/arbitrator only) -setting.about.shortcuts.reOpenDispute.value=Select closed dispute and press: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Open window for account age signing (legacy arbitrators only) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navigate to legacy arbitrator view and press: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Send alert or update message (privileged ac setting.about.shortcuts.sendFilter=Set Filter (privileged activity) setting.about.shortcuts.sendPrivateNotification=Send private notification to peer (privileged activity) -setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar or dispute and press: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements account.altcoin.popup.wallet.confirm=どのウォレットを使うべきか理解しており、承認する account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. account.altcoin.popup.arq.msg=Trading ARQ on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending ARQ, you need to use either the official ArQmA GUI wallet or ArQmA CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\narqma-wallet-cli (use the command get_tx_key)\narqma-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The ARQ sender is responsible for providing verification of the ARQ transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit ArQmA discord channel (https://discord.gg/s9BQpJT) or the ArQmA forum (https://labs.arqma.com) to find more information. -account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand and fulfill the following requirements:\n\nProve payments: since Monero is a private coin, some transaction details aren't publicly available in the blockchain, and, in case of a dispute, the mediator or arbitrator needs them to check if the transaction was really made. In Bisq, the sender of the XMR transaction is the one responsible for providing this information to the mediator or arbitrator in case of a dispute. In order to do that, you must send XMR using a wallet that provides the information required to prove the payment was made, which includes:\n\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nThis information can be found in the official Monero GUI & CLI wallets, MyMonero, and Exodus (desktop) as well as in Cake Wallet, MyMonero, and Monerujo (mobile), in the following locations:\n\n- Monero GUI: go to Transactions tab\n- Monero CLI: use the command get_tx_key TXID. The flag store-tx-info must be enabled (enabled by default in new versions)\n- Other wallets: go to Transactions history and search for Transaction key (Tx key or Secret key) and the destination address in a sent transaction. Save recipient address option must be enabled in Cake Wallet settings.\n\nIf you are using a wallet different from the mentioned above, please be sure you can access those three pieces of information.Since the transaction key and the destination address are stored in the Monero wallet software, and they cannot be recovered in the Monero blockchain, you should never delete or restore your Monero wallet before a Bisq trade is completed. Failure to provide the above data will result in losing the dispute case.\n\nCheck payments: with those three pieces of information, the verification that a quantity of Monero was sent to a specific address can be accomplished the following way:\n\n- Monero GUI: change wallet to Advanced mode and go to Advanced > Prove/check > Check Transaction\n- Monero CLI: use the command check_tx_key TXID TXKEY ADDRESS\n- XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Explore Monero website (https://www.exploremonero.com/receipt)\n\nIf you are still not sure about this process, visit (https://www.getmonero.org/resources/user-guides/prove-payment.html) to find more information or ask a question on the Monero support subreddit (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Trading MSR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending MSR, you need to use either the official Masari GUI wallet, Masari CLI wallet with the store-tx-info flag enabled (enabled by default) or the Masari web wallet (https://wallet.getmasari.org). Please be sure you can access the tx key as that would be required in case of a dispute.\nmasari-wallet-cli (use the command get_tx_key)\nmasari-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nMasari Web Wallet (goto Account -> transaction history and view details on your sent transaction)\n\nVerification can be accomplished in-wallet.\nmasari-wallet-cli : using command (check_tx_key).\nmasari-wallet-gui : on the Advanced > Prove/Check page.\nVerification can be accomplished in the block explorer \nOpen block explorer (https://explorer.getmasari.org), use the search bar to find your transaction hash.\nOnce transaction is found, scroll to bottom to the 'Prove Sending' area and fill in details as needed.\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The MSR sender is responsible for providing verification of the MSR transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process, ask for help on the Official Masari Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Price node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets API operator +dao.bond.bondedRoleType.MARKETS_OPERATOR=市場運営者 # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQエクスプローラー運営者 +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=モバイル通知中継の運営者 # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=概要ノート disputeSummaryWindow.addSummaryNotes=概要ノートを追加 disputeSummaryWindow.close.button=チケットを閉じる -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nNext steps:\nOpen trade and accept or reject suggestion from mediator -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=取引相手のチケットも閉じる必要があります! disputeSummaryWindow.close.txDetails.headline=Publish refund transaction disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to publish this transaction? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} 緊急ウォレットツール emptyWalletWindow.info=UIから資金にアクセスできない緊急時にのみ使用してください。\n\nこのツールを使用すると、開いているオファーはすべて自動的に閉じられることに注意してください。\n\nこのツールを使用する前に、データディレクトリをバックアップしてください。これは「アカウント/バックアップ」で行えます。\n\n問題の原因を調査できるように、問題を報告し、GitHubまたはBisqフォーラムにバグレポートを提出してください。 emptyWalletWindow.balance=あなたの利用可能なウォレット残高 @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=フィルター済価格中継ノード(コンマ filterWindow.btcNode=フィルター済ビットコインノード(コンマ区切り アドレス+ポート) filterWindow.preventPublicBtcNetwork=パブリックビットコインネットワークの使用を防止 filterWindow.disableDao=DAOを無効化 +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=DAOに必要な最低バージョン filterWindow.disableTradeBelowVersion=トレードに必要な最低バージョン filterWindow.add=フィルターを追加 @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=承認: ビットコインを{0}オファーを offerDetailsWindow.creationDate=作成日 offerDetailsWindow.makersOnion=メイカーのonionアドレス -qRCodeWindow.headline=QRコード -qRCodeWindow.msg=外部のウォレットからあなたのBisqウォレットを送金するためにそのQRコードを使ってください。 +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=支払いリクエスト:\n{0} selectDepositTxWindow.headline=係争の為のデポジットトランザクションを選択 @@ -2073,12 +2151,17 @@ sendPrivateNotificationWindow.send=プライベート通知を送信 showWalletDataWindow.walletData=ウォレットデータ showWalletDataWindow.includePrivKeys=プライベートキーを含む +setXMRTxKeyWindow.headline=XMR送金を証明 +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=トランザクションID(任意) +setXMRTxKeyWindow.txKey=トランザクション・キー(任意) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=ユーザー規約 tacWindow.agree=同意します tacWindow.disagree=同意せずにに終了 -tacWindow.arbitrationSystem=Dispute resolution +tacWindow.arbitrationSystem=紛争解決 tradeDetailsWindow.headline=トレード tradeDetailsWindow.disputedPayoutTxId=係争中の支払い取引ID: @@ -2087,7 +2170,7 @@ tradeDetailsWindow.txFee=マイニング手数料 tradeDetailsWindow.tradingPeersOnion=トレード相手のonionアドレス tradeDetailsWindow.tradingPeersPubKeyHash=Trading peers pubkey hash tradeDetailsWindow.tradeState=トレード状態 -tradeDetailsWindow.agentAddresses=Arbitrator/Mediator +tradeDetailsWindow.agentAddresses=調停人 walletPasswordWindow.headline=アンロックするためにパスワードを入力してください @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=The deposit transaction of the closed error.closedTradeWithNoDepositTx=The deposit transaction of the closed trade with the trade ID {0} is null.\n\nPlease restart the application to clean up the closed trades list. popup.warning.walletNotInitialized=ウォレットはまだ初期化されていません +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=このコンピューターのBisqバージョンが間違っている可能性があります。\nコンピューターのアーキテクチャ: {0}\nインストールしたBisqバイナリ: {1}\nシャットダウンして、次の正しいバージョンを再インストールしてください({2})。 popup.warning.incompatibleDB=互換性のないデータベースファイルが検出されました!\n\nこれらのデータベースファイルは、現在のコードベースと互換性がありません:\n{0}\n\n破損したファイルのバックアップを作成し、デフォルト値を新しいデータベースバージョンに適用しました。\n\nバックアップは次の場所にあります。\n{1}/db/backup_of_corrupted_data.\n\nBisqの最新バージョンがインストールされているかどうかを確認してください。\n以下からダウンロードできます。\nhttps://bisq.network/downloads\n\nアプリケーションを再起動してください。 popup.warning.startupFailed.twoInstances=Bisqは既に起動中です。Bisqを2つ起動することはできません。 @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=BSQウォレットにBSQのトレード popup.warning.messageTooLong=メッセージが許容サイズ上限を超えています。いくつかに分けて送信するか、 https://pastebin.com のようなサービスにアップロードしてください。 popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." -popup.warning.nodeBanned={0}ノードの1つが禁止されました。禁止されたノードに接続されないように、アプリケーションを再起動してください。 +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=価格中継 popup.warning.seed=シード popup.warning.mandatoryUpdate.trading=最新のBisqバージョンに更新してください。古いバージョンのトレードを無効にする必須の更新プログラムがリリースされました。詳細については、Bisqフォーラムをご覧ください。 @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=両方の取引者がトレードプロトコル popup.info.cashDepositInfo=あなたの地域の銀行支店が現金デポジットが作成できることを確認してください。\n売り手の銀行ID(BIC / SWIFT)は{0}です。 popup.info.cashDepositInfo.confirm=デポジットを作成できるか確認します popup.info.shutDownWithOpenOffers=Bisqはシャットダウン中ですが、オファーはあります。\n\nこれらのオファーは、Bisqがシャットダウンされている間はP2Pネットワークでは利用できませんが、次回Bisqを起動したときにP2Pネットワークに再公開されます。\n\nオファーをオンラインに保つには、Bisqを実行したままにして、このコンピュータもオンラインにしたままにします(つまり、スタンバイモードにならないようにしてください。モニタースタンバイは問題ありません)。 +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=重要なプライベート通知! @@ -2223,22 +2308,22 @@ popup.news.launch.accountSigning.description=Lift 0.01 BTC fiat trading limits b popup.news.launch.ntp.headline=NEW TRADE PROTOCOL popup.news.launch.ntp.description=New 2-level dispute resolution system makes Bisq more secure, scalable, and censorship-resistant. -popup.accountSigning.selectAccounts.headline=Select payment accounts +popup.accountSigning.selectAccounts.headline=支払いアカウントを選択 popup.accountSigning.selectAccounts.description=Based on the payment method and point of time all payment accounts that are connected to a dispute where a payout to the buyer occurred will be selected for you to sign. popup.accountSigning.selectAccounts.signAll=Sign all payment methods popup.accountSigning.selectAccounts.datePicker=Select point of time until which accounts will be signed -popup.accountSigning.confirmSelectedAccounts.headline=Confirm selected payment accounts -popup.accountSigning.confirmSelectedAccounts.description=Based on your input, {0} payment accounts will be selected. -popup.accountSigning.confirmSelectedAccounts.button=Confirm payment accounts -popup.accountSigning.signAccounts.headline=Confirm signing of payment accounts -popup.accountSigning.signAccounts.description=Based on your selection, {0} payment accounts will be signed. -popup.accountSigning.signAccounts.button=Sign payment accounts +popup.accountSigning.confirmSelectedAccounts.headline=選択された支払いアカウントを確認 +popup.accountSigning.confirmSelectedAccounts.description=入力に基づいて、{0}口の支払いアカウントは選択されます +popup.accountSigning.confirmSelectedAccounts.button=支払いアカウントを確認 +popup.accountSigning.signAccounts.headline=支払いアカウントの署名を確認 +popup.accountSigning.signAccounts.description=選択に基づいて、{0}口の支払いアカウントは署名されます +popup.accountSigning.signAccounts.button=支払いアカウントに署名 popup.accountSigning.signAccounts.ECKey=Enter private arbitrator key popup.accountSigning.signAccounts.ECKey.error=Bad arbitrator ECKey -popup.accountSigning.success.headline=Congratulations -popup.accountSigning.success.description=All {0} payment accounts were successfully signed! +popup.accountSigning.success.headline=おめでとう +popup.accountSigning.success.description=全{0}口の支払いアカウントは成功裏に署名されました! popup.accountSigning.generalInformation=You'll find the signing state of all your accounts in the account section.\n\nFor further information, please visit https://docs.bisq.network/payment-methods#account-signing. popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer''s account after a successful trade.\n\n{0} popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you''ll be able to sign other accounts in {0} days from now.\n\n{1} @@ -2252,15 +2337,15 @@ popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash popup.accountSigning.confirmSingleAccount.button=Sign account age witness popup.accountSigning.successSingleAccount.description=Witness {0} was signed -popup.accountSigning.successSingleAccount.success.headline=Success +popup.accountSigning.successSingleAccount.success.headline=成功 popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0} popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed -popup.accountSigning.unsignedPubKeys.result.headline=Signing completed +popup.accountSigning.unsignedPubKeys.result.headline=署名が完了しました popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys -popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.accountSigning.unsignedPubKeys.result.failed=署名が失敗しました #################################################################### # Notifications @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=外部ウォレットで使用されるマイニング手数料が少なくとも{0} satoshis/byte あることを確認してください。 そうでなければ、このトレードトランザクションは承認されず、トレードは係争に終わります。 +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=取引アカウントを下記パスに保存しました:\n{0} guiUtil.accountExport.noAccountSetup=取引アカウントのエクスポート設定がされていません @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=支払いアカウントが{0}前に作成されまし peerInfoIcon.tooltip.unknownAge=支払いアカウントの年齢は不明です。 tooltip.openPopupForDetails=詳細についてのポップアップを開く +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=外部ブロックチェーンエクスプローラーで次のアドレスを開く: {0} tooltip.openBlockchainForTx=外部ブロックチェーンエクスプローラーで次のトランザクションを開く: {0} @@ -2350,7 +2436,7 @@ peerInfo.nrOfTrades=完了した取引数 peerInfo.notTradedYet=あなたは今までそのユーザーと取引していません。 peerInfo.setTag=そのピアにタグを設定する peerInfo.age.noRisk=支払いアカウントの年齢 -peerInfo.age.chargeBackRisk=Time since signing +peerInfo.age.chargeBackRisk=署名する後経過時間 peerInfo.unknownAge=年齢不明 addressTextField.openWallet=既定のビットコインウォレットを開く @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=既定のビットコインウォレットが peerInfoIcon.tooltip={0}\nタグ: {1} txIdTextField.copyIcon.tooltip=トランザクションIDをクリップボードにコピー -txIdTextField.blockExplorerIcon.tooltip=そのトランザクションIDをブロックチェーンエクスプローラで開く +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=そのトランザクションIDをブ navigation.account=「アカウント」 navigation.account.walletSeed=「アカウント/ウォレットシード」 -navigation.funds.availableForWithdrawal=「資金/送金する」 +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=「ポートフォリオ/私の公開オファー navigation.portfolio.pending=「ポートフォリオ/オープントレード」 navigation.portfolio.closedTrades=「ポートフォリオ/履歴」 @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=あなたの財布は暗号化されています seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=ウォレットは、新しいシードワードで正常に復元されました。\n\nアプリケーションをシャットダウンして再起動する必要があります。 seed.restore.error=シードワードを使用したウォレットの復元中にエラーが発生しました。{0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2466,7 +2554,8 @@ seed.restore.error=シードワードを使用したウォレットの復元中 payment.account=アカウント payment.account.no=アカウント番号 payment.account.name=アカウント名 -payment.account.userName=User name +payment.account.userName=ユーザ名 +payment.account.phoneNr=電話番号 payment.account.owner=アカウント所有者の氏名 payment.account.fullName=氏名(名、ミドルネーム、姓) payment.account.state=州/県/区 @@ -2582,7 +2671,7 @@ CASH_DEPOSIT=現金入金 MONEY_GRAM=MoneyGram WESTERN_UNION=Western Union F2F=対面(直接) -JAPAN_BANK=Japan Bank Furikomi +JAPAN_BANK=日本全銀振込 # suppress inspection "UnusedProperty" NATIONAL_BANK_SHORT=国立銀行 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 8bc92fc11a8..ee68fe79de8 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -35,7 +35,7 @@ shared.no=Não shared.iUnderstand=Eu compreendo shared.na=N/D shared.shutDown=Desligar -shared.reportBug=Reportar bug em Github Issues +shared.reportBug=Report bug on GitHub shared.buyBitcoin=Comprar bitcoin shared.sellBitcoin=Vender bitcoin shared.buyCurrency=Comprar {0} @@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (min - max) shared.removeOffer=Remover oferta shared.dontRemoveOffer=Não remover a oferta shared.editOffer=Editar oferta -shared.openLargeQRWindow=Abrir QR-Code em janela grande +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=Conta de negociação -shared.faq=Visite a página de ajuda +shared.faq=Visit FAQ page shared.yesCancel=Sim, cancelar shared.nextStep=Próximo passo shared.selectTradingAccount=Selecionar conta de negociação shared.fundFromSavingsWalletButton=Transferir fundos da carteira Bisq shared.fundFromExternalWalletButton=Abrir sua carteira externa para prover fundos -shared.openDefaultWalletFailed=Falha na abertura da carteira Bitcoin padrão. Você possui uma instalada? +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=Distância em % do preço de mercado shared.belowInPercent=% abaixo do preço de mercado shared.aboveInPercent=% acima do preço de mercado shared.enterPercentageValue=Insira a % shared.OR=OU -shared.notEnoughFunds=Você não possui saldo suficiente em sua carteira Bisq.\nSão necessários {0}, porém você possui apenas {1} em sua carteira Bisq.\n\nPor favor, realize o pagamento usando uma carteira externa ou transfira mais bitcoins para a sua carteira Bisq em \"Fundos/Receber fundos\". +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=Aguardando pagamento... shared.depositTransactionId=ID da Transação de depósito shared.TheBTCBuyer=O comprador de BTC @@ -116,22 +116,22 @@ shared.You=Você shared.reasonForPayment=Motivo do pagamento shared.sendingConfirmation=Enviando confirmação... shared.sendingConfirmationAgain=Por favor, envie a confirmação novamente -shared.exportCSV=Exportar para csv +shared.exportCSV=Export to CSV shared.exportJSON=Exportar para JSON shared.noDateAvailable=Sem data disponível shared.noDetailsAvailable=Sem detalhes disponíveis shared.notUsedYet=Ainda não usado shared.date=Data shared.sendFundsDetailsWithFee=Enviando: {0}\nDo endereço: {1}\nPara o endereço do recipiente: {2}\nTaxa de mineração é: {3} ({4} satoshis/byte)\nTamanho da transação: {5} Kb\n\nO recipiente irá receber: {6}\n\nVocê tem certeza de que quer sacar essa quantia? -shared.sendFundsDetailsDust=Bisq detectou que essa transação criaria um troco que está abaixo do limite mínimo de poeira (e não permitida pelas regras de consenso do Bitcoin). Ao invés disso, essa poeira ({0} satoshi{1}) será adicionada à taxa de mineração.\n +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=Copiar para área de transferência shared.language=Idioma shared.country=País shared.applyAndShutDown=Aplicar e desligar shared.selectPaymentMethod=Selecionar método de pagamento -shared.accountNameAlreadyUsed=Esse nome de conta já está sendo usado em uma conta salva.\nPor gentileza, utilize outro nome. +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=Você realmente quer apagar a conta selecionada? -shared.cannotDeleteAccount=Você não pode apagar a conta pois ela está sendo usada em uma oferta aberta ou negociação. +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=Ainda não há contas configuradas shared.manageAccounts=Gerenciar contas shared.addNewAccount=Adicionar conta nova @@ -214,7 +214,8 @@ shared.mediator=Mediador shared.arbitrator=Árbitro shared.refundAgent=Árbitro shared.refundAgentForSupportStaff=Árbitro -shared.delayedPayoutTxId=ID de transação colateral de reembolso +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=No momento, você possui muitas transações não-confirmadas. Tente novamente mais tarde. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=Países aceitos como sede bancária (tomador offerbook.availableOffers=Ofertas disponíveis offerbook.filterByCurrency=Filtrar por moeda offerbook.filterByPaymentMethod=Filtrar por método de pagamento -offerbook.timeSinceSigning=Tempo desde a assinatura: +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=Esta conta foi verificada e {0} offerbook.timeSinceSigning.info.arbitrator=assinada por um árbitro e pode assinar contas de pares offerbook.timeSinceSigning.info.peer=assinada por um par, esperando os limites serem levantados @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=assinada por um par e pode assinar contas offerbook.timeSinceSigning.info.banned=conta foi banida offerbook.timeSinceSigning.daysSinceSigning={0} dias offerbook.timeSinceSigning.daysSinceSigning.long={0} desde a assinatura +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=Quando você completa uma negociação bem sucedida com um par que tem uma conta de pagamento assinada, a sua conta de pagamento é assinada.\n{0} dias depois, o limite inicial de {1} é levantado e sua conta pode assinar as contas de pagamento de outros pares. offerbook.timeSinceSigning.notSigned=Ainda não assinada @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=Erro ao desativar oferta:\n{0} offerbook.activateOffer.failed=Erro ao publicar oferta:\n{0} offerbook.withdrawFundsHint=Você pode retirar fundos que você pagou da tela {0}. -offerbook.warning.noTradingAccountForCurrency.headline=Não há conta de negociação para a moeda selecionada. -offerbook.warning.noTradingAccountForCurrency.msg=Você não possui conta de negociação para a moeda selecionada.\nDeseja criar uma oferta com uma de suas contas existentes? -offerbook.warning.noMatchingAccount.headline=Não há conta de negociação compatível. -offerbook.warning.noMatchingAccount.msg=Para aceitar essa oferta, é necessário configurar uma conta de pagamento usando esse método de pagamento.\n\nGostaria de fazer isso agora? +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=Esta oferta não pode ser tomada por restrições de negociação da outra parte @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=O método de pagamento usado nesta oferta offerbook.warning.nodeBlocked=O endereço onion daquele negociador foi bloqueado pelos desenvolvedores do Bisq.\nProvavelmente há um problema não resolvido associado àquele negociador. offerbook.warning.requireUpdateToNewVersion=A sua versão do Bisq deixou de ser compatível para negociação.\nAtualize para a versão mais recente do Bisq em https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=A sua conta de pagamentos foi criada {0} atrás. O seu limite de negociação é baseado na idade da conta e é insuficiente para essa oferta.\n\nO seu limite de negociação é: {1}\nA quantia mínima de negociação para essa oferta é: {2}.\n\nVocê não pode aceitar essa oferta neste momento. Essa restrição será removida assim que sua conta atingir 2 meses de criação. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=Você irá vender a preço de mercado (atualizado a cada minuto). offerbook.info.buyAtMarketPrice=Você irá comprar a preço de mercado (atualizado a cada minuto). @@ -541,7 +543,7 @@ portfolio.tab.history=Histórico portfolio.tab.failed=Falha portfolio.tab.editOpenOffer=Editar oferta -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=Aguardar confirmação da blockchain portfolio.pending.step2_buyer.startPayment=Iniciar pagamento @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=Aguardar recebimento do pagamen portfolio.pending.step3_seller.confirmPaymentReceived=Confirmar recebimento do pagamento portfolio.pending.step5.completed=Concluído +portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status +portfolio.pending.autoConf=Auto-confirmed +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key +portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Auto-confirm feature is disabled. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=A transação de depósito foi publicada\n{0} precisa esperar ao menos uma confirmação da blockchain antes de iniciar o pagamento. portfolio.pending.step1.warn=A transação do depósito ainda não foi confirmada.\nIsto pode ocorrer em casos raros em que a taxa de financiamento de um dos negociadores enviada a partir de uma carteira externa foi muito baixa. portfolio.pending.step1.openForDispute=A transação de depósito ainda não foi confirmada. Você pode aguardar um pouco mais ou entrar em contato com o mediador para pedir assistência. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=Por favor, acesse sua conta bancária online # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Por favor, entre em contato com o vendedor de BTC através do contato fornecido e combine um encontro para pagá-lo {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Iniciar pagamento usando {0} +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=Quantia a ser transferida portfolio.pending.step2_buyer.sellersAddress=Endereço {0} do vendedor portfolio.pending.step2_buyer.buyerAccount=A sua conta de pagamento a ser usada @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Alguns bancos podem v portfolio.pending.step2_buyer.confirmStart.headline=Confirme que você iniciou o pagamento portfolio.pending.step2_buyer.confirmStart.msg=Você iniciou o pagamento {0} para o seu parceiro de negociação? portfolio.pending.step2_buyer.confirmStart.yes=Sim, iniciei o pagamento - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=Aguardar pagamento portfolio.pending.step2_seller.f2fInfo.headline=Informações de contato do comprador portfolio.pending.step2_seller.waitPayment.msg=A transação de depósito tem pelo menos uma confirmação blockchain do protocolo.\nVocê precisa aguardar até que o comprador de BTC inicie o pagamento de {0}. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=Quantia a receber portfolio.pending.step3_seller.yourAddress=Seu endereço {0} portfolio.pending.step3_seller.buyersAddress=Endereço {0} do comprador portfolio.pending.step3_seller.yourAccount=Sua conta de negociação -portfolio.pending.step3_seller.buyersAccount=Conta de negociação do comprador +portfolio.pending.step3_seller.xmrTxHash=ID da Transação +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=Confirmar recebimento do pagamento portfolio.pending.step3_seller.buyerStartedPayment=O comprador de BTC iniciou o pagamento {0}.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Verifique as confirmações de transação em sua carteira altcoin ou explorador de blockchain e confirme o pagamento quando houver confirmações suficientes. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Por favor, apenas use esta função em c portfolio.pending.timeLockNotOver=Você precisa aguardar até ≈{0} (mais {1} blocos) para conseguir abrir uma disputa com um árbitro. portfolio.pending.error.depositTxNull=A transação de depósito está ausente. Você não pode abrir uma disputa sem uma transação de depósito válida. Por favor, vá até "Configurações/Informações da rede" e ressincronize o arquivo SPV.\n\nPara mais informações, por favor acesse o canal #support do time da Bisq na Keybase. -portfolio.pending.mediationResult.error.depositTxNull=A transação de depósito é nula. A negociação será movida para a seção de negociações com erro. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=A transação de depósito não está confirmada. Você não pode abrir uma disputa com uma transação não-confirmada. Por favor, espere até que a transação seja confirmada ou vá até "Configurações/Informações da rede" e ressincronize o arquivo SPV.\n\nPara mais informações, por favor acesse o canal #support do time da Bisq na Keybase. portfolio.pending.notification=Notificação @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=Ver solução proposta portfolio.pending.mediationResult.popup.headline=Resultado da mediação para a negociação com ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=O seu parceiro de negociação aceitou a sugestão do mediador para a negociação {0} portfolio.pending.mediationResult.popup.info=O mediador sugeriu o seguinte pagamento:\nVocê recebe: {0}\nSeu parceiro de negociação recebe: {1}\n\nVocê pode aceitar ou rejeitar esta sugestão de pagamento.\n\nAceitando, você assina a transação de pagamentos proposta. Se seu parceiro de negociação também aceitar e assinar, o pagamento será efetuado, e a negociação será fechada.\n\nSe um ou ambos de vocês rejeitarem a sugestão você terá que esperar até {2} (bloco {3}) para abrir uma segunda rodada de disputa com um árbitro que investigará o caso novamente e fará um pagamento baseado nas evidências.\n\nO árbitro poderá cobrar uma pequena taxa (taxa máxima: o depósito de segurança do negociador) como compensação pelo seu trabalho. Ambos negociadores concordarem com as sugestões do mediador é o caminho feliz—requisitar árbitro é destinado a circunstâncias excepcionais, como quando um negociador está certo de que o mediador não fez uma sugestão de pagamento justa (ou se o outro parceiro de negociação não responde).\n\nPara mais detalhes sobre o novo modelo de arbitragem:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Rejeitar e solicitar arbitramento portfolio.pending.mediationResult.popup.alreadyAccepted=Você já aceitou +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=Concluído portfolio.closed.ticketClosed=Arbitrado portfolio.closed.mediationTicketClosed=Mediado @@ -763,8 +817,8 @@ portfolio.closed.canceled=Cancelado portfolio.failed.Failed=Falha portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Arbitração antiga support.tab.ArbitratorsSupportTickets=Tickets de {0} support.filter=Search disputes support.filter.prompt=Insira ID da negociação. data. endereço onion ou dados da conta + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=Enviar notificação privada -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=Notificação privada +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=Não há tickets de suporte abertos support.sendingMessage=Enviando mensagem... support.receiverNotOnline=O recipiente não está online. A mensagem será salva na caixa postal dele. @@ -905,6 +970,9 @@ support.peerOpenedDispute=O seu parceiro de negociação solicitou uma disputa.\ support.peerOpenedDisputeForMediation=O seu parceiro de negociação solicitou mediação.\n\n{0}\n\nVersão do Bisq: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Endereço do nó do mediador: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=Informações da rede settings.tab.about=Sobre setting.preferences.general=Preferências gerais -setting.preferences.explorer=Explorador de blocos do Bitcoin -setting.preferences.explorer.bsq=Explorador de blocos de BSQ +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Desvio máx. do preço do mercado setting.preferences.avoidStandbyMode=Impedir modo de economia de energia +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=Valores acima de {0}% não são permitidos. setting.preferences.txFee=Taxa da transação de retirada (satoshis/byte) setting.preferences.useCustomValue=Usar valor personalizado @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Abrir ferramenta de emergênc setting.about.shortcuts.showTorLogs=Ativar registro de logs para mensagens Tor de níveis entre DEBUG e WARN -setting.about.shortcuts.showDisputeStatistics=Mostrar resumo de todas as disputas -setting.about.shortcuts.showDisputeStatistics.value=Navegar para a visualização de disputas e pressionar: {0} - setting.about.shortcuts.manualPayoutTxWindow=Abrir janela de pagamento manual do multisig 2-de-2 da transação de depósito setting.about.shortcuts.reRepublishAllGovernanceData=Republicar dados de governança da DAO (propostas, votos) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navegue até à conta e pressio setting.about.shortcuts.registerMediator=Registrar mediador (apenas mediador/árbitro) setting.about.shortcuts.registerMediator.value=Navegue até à conta e pressione: {0} -setting.about.shortcuts.reOpenDispute=Re-abrir disputa já encerrada (apenas mediador/árbitro) -setting.about.shortcuts.reOpenDispute.value=Selecione a disputa encerrada e pressione: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Abrir janela para assinar idade da conta (apenas para árbitros legados) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navegue até a tela de árbitro legado e pressione: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Enviar alerta ou atualizar mensagem (ativid setting.about.shortcuts.sendFilter=Definir Filtro (atividade privilegiada) setting.about.shortcuts.sendPrivateNotification=Enviar notificação privada ao par (atividade privilegiada) -setting.about.shortcuts.sendPrivateNotification.value=Abrir informações de par no avatar ou em disputa e pressione: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Por favor, certifique-se de seguir os requisito account.altcoin.popup.wallet.confirm=Eu entendo e confirmo que sei qual carteira preciso usar. account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. account.altcoin.popup.arq.msg=Trading ARQ on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending ARQ, you need to use either the official ArQmA GUI wallet or ArQmA CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\narqma-wallet-cli (use the command get_tx_key)\narqma-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The ARQ sender is responsible for providing verification of the ARQ transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit ArQmA discord channel (https://discord.gg/s9BQpJT) or the ArQmA forum (https://labs.arqma.com) to find more information. -account.altcoin.popup.xmr.msg=Negociar XMR na Bisq requer que você entenda e cumpra os seguintes requisitos:\n\nComprovar pagamentos: como a Monero é uma moeda privada, alguns detalhes da transação não estão disponíveis publicamente na blockchain e, em caso de uma disputa, o mediador ou o árbitro precisará deles para verificar se a transação realmente foi realizada. Na Bisq, o remetente da transação com XMR é a pessoa responsável por fornecer essas informações para o mediador ou o árbitro em caso de uma disputa. Para fazer isso, você precisa enviar XMR usando uma carteira que forneça as informações necessárias para comprovar que o pagamento foi feito, que incluem:\n\n- a chave da transação (Tx Key, Tx Secret Key ou Tx Private Key)\n- o ID da transação (Tx ID ou Tx Hash)\n- o endereço de destino (endereço do recipiente)\n\nEssas informações podem ser encontradas nas carteiras de desktop Monero GUI & CLI, MyMonero e Exodus assim como nas carteiras móveis Cake Wallet, MyMonero e Monerujo, nas seguintes localizações:\n\n- Monero GUI: vá para a aba Transações\n- Monero CLI: use o comando get_tx_key TXID. A flag store-tx-info deve estar habilitada (habilitada por padrão nas versões mais recentes)\n- Outras carteiras: vá para o histórico de Transações e procure pela chave da transação (Tx key ou Secret key) e o endereço destino em uma transação prévia. Se você estiver usando a Cake Wallet, a opção salvar endereço do recipiente deve estar habilitada nas configurações.\n\nSe você estiver usando uma carteira diferente das mencionadas acima, certifique-se de que você consegue acessar essas três informações. Como a chave da transação e o endereço de destino são armazenados no software da carteira Monero, e eles não podem ser recuperados na blockchain do Monero, você jamais deve deletar ou restaurar a sua carteira Monero antes de completar uma negociação na Bisq. Se você não conseguir fornecer as informações necessárias, você perderá a disputa.\n\nVerificando pagamentos: com essas três informações, a verificação de que uma quantia de Monero foi enviada para um endereço específico pode ser realizada da seguinte maneira:\n\n- Monero GUI: mude a carteira para o modo Avançado e vá para Avançado > Provar/verificar > Verificar Transação\n- Monero CLI: use o comando check_tx_key TXID TXKEY ADDRESS\n- XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Site Explore Monero (https://www.exploremonero.com/receipt)\n\nSe você ainda tem dúvidas sobre o processo, visite (https://www.getmonero.org/pt-br/resources/user-guides/prove-payment.html) para obter maiores informações ou faça uma pergunta no subreddit do suporte do Monero (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Negociar MSR Bisq requer que você entenda e cumpra os seguintes requisitos:\n\nPara enviar MSR, você deve usar uma destas: carteira oficial GUI Masari, carteira CLI Masari com a opção store-tx-info habilitada (habilitada por padrão) ou a carteira web Masari (https://wallet.getmasari.org). Por favor, certifique-se que você tenha acesso à chave da transação pois esta seria necessária em caso de uma disputa.\nmasari-wallet-cli (use o comando get_tx_key)\nmasari-wallet-gui (vá até a aba histórico e clique no botão (P) para prova de pagamento)\n\nCarteira Web Masari (vá até Conta -> histórico de transações e veja os detalhes da sua transação enviada.)\n\nVerificação pode ser feita dentro da carteira.\nmasari-wallet-cli : usando um comando (check_tx_key).\nmasari-wallet-gui : na página Avançado > Comprovar/Verificar.\nVerificação pode ser feita via explorador de blocos.\nAbra o explorador de blocos (https://explorer.getmasari.org) e use a barra de busca para encontrar o hash da sua transação.\nAssim que a transação for encontrada, role até o final da seção 'Comprovar envio' e preencha os detalhes conforme necessário.\nVocê precisa fornecer os seguintes dados ao mediador ou árbitro em caso de uma disputa:\n- Chave privada da transação\n- Hash da transação\n- Endereço público do destinatário\n\nA impossibilidade de fornecer as informações acima ou uso de uma carteira incompatível resultará na perda do caso de disputa. Em caso de uma disputa, o remetente de MSR é responsável por providenciar, ao mediador ou árbitro, a verificação do envio de MSR.\n\nNão é necessário um ID de pagamento, apenas o endereço público convencional.\nCaso tenha dúvidas sobre este processo, solicite ajuda no Discord oficial Masari (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Operador do node de preço # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Operador do nó de BTC # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador da API de mercados +dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador de mercados # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Operador do explorador de BSQ +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operador da transmissão de notificações móveis # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=Notas de resumo disputeSummaryWindow.addSummaryNotes=Adicionar notas de resumo disputeSummaryWindow.close.button=Fechar ticket -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nPróximos passos:\nAbra a negociação e aceite ou recuse a sugestão do mediador -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nPróximos passos:\nNão é preciso fazer mais nada. Se o árbitro decidir em seu favor, você verá uma transação com etiqueta "Reembolso de arbitração" em Fundos/Transações + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=Você também precisa fechar o ticket dos parceiros de negociação! disputeSummaryWindow.close.txDetails.headline=Publicar transação de reembolso disputeSummaryWindow.close.txDetails.buyer=Comprador recebe {0} no endereço: {1}\n disputeSummaryWindow.close.txDetails.seller=Vendedor recebe {0} no endereço: {1}\n disputeSummaryWindow.close.txDetails=Gastando: {0}\n{1}{2}Taxa de transação: {3} ({4} satoshis/byte)\nTamanho da transação: {5} Kb\n\nTem certeza de que quer publicar essa transação? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} ferramenta de emergência da carteira emptyWalletWindow.info=Por favor, utilize essa opção apenas em caso de emergência, caso você não consiga acessar seus fundos a partir do programa.\n\nNote que todas as ofertas abertas serão fechadas automaticamente quando você utilizar esta ferramenta.\n\nAntes de usar esta ferramenta, faça um backup da sua pasta de dados. Você pode fazer isso em \"Conta/Backup\".\n\nHavendo qualquer problema, avise-nos através do GitHub ou do fórum Bisq, para que assim possamos investigar o que causou o problema. emptyWalletWindow.balance=Seu saldo disponível na carteira @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=Nós de transmissão de preço filtrados (endereços filterWindow.btcNode=Nós de Bitcoin filtrados (endereços + portas sep. por vírgula) filterWindow.preventPublicBtcNetwork=Prevenir uso da rede de Bitcoin pública filterWindow.disableDao=Desativar DAO +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Versão mín. necessária para a DAO filterWindow.disableTradeBelowVersion=Versão mínima necessária para negociação filterWindow.add=Adicionar filtro @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} bitcoin offerDetailsWindow.creationDate=Criada em offerDetailsWindow.makersOnion=Endereço onion do ofertante -qRCodeWindow.headline=Código QR -qRCodeWindow.msg=Utilize esse código QR para realizar depósitos em sua carteira Bisq a partir de uma carteira externa. +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=Solicitação de pagamento:\n{0} selectDepositTxWindow.headline=Selecionar transação de depósito para disputa @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=Enviar notificação privada showWalletDataWindow.walletData=Dados da carteira showWalletDataWindow.includePrivKeys=Incluir chaves privadas +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=Acordo de usuário @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=A transação de depósito da negocia error.closedTradeWithNoDepositTx=A transação de depósito da negociação já fechada com ID {0} está ausente.\n\nPor favor, reinicie o aplicativo para atualizar a lista de negociações encerradas. popup.warning.walletNotInitialized=A carteira ainda não foi inicializada +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=Você provavelmente está usando a versão incorreta do Bisq para este computador.\nA arquitetura do seu computador é: {0}.\nO binário do Bisq que você instalou é: {1}.\nPor favor, feche o programa e instale a versão correta ({2}). popup.warning.incompatibleDB=Detectamos arquivos de base de dados incompatíveis!\n\nEsse(s) arquivo(s) de base de dados não são compatíveis com nossa base de código atual:\n{0}\n\nFizemos um backup do(s) arquivo(s) corrompido(s) e aplicamos os valores padrão a uma nova versão da base de dados.\n\nO backup está em:\n{1} /db/backup_of_corrupted_data.\n\nPor favor, verifique se você tem a última versão do Bisq instalada.\nVocê pode baixá-lo em:\nhttps://bisq.network/downloads\n\nPor favor, reinicie o programa. popup.warning.startupFailed.twoInstances=O Bisq já está sendo executado. Você não pode executar duas instâncias do Bisq ao mesmo tempo. @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=Sua carteira BSQ não possui fundos suf popup.warning.messageTooLong=Sua mensagem excede o tamanho máximo permitido. Favor enviá-la em várias partes ou utilizando um serviço como https://pastebin.com. popup.warning.lockedUpFunds=Você possui fundos travados em uma negociação com erro.\nSaldo travado: {0}\nEndereço da transação de depósito: {1}\nID da negociação: {2}.\n\nPor favor, abra um ticket de suporte selecionando a negociação na tela de negociações em aberto e depois pressionando "\alt+o\" ou \"option+o\". -popup.warning.nodeBanned=Um dos {0} nodes foi banido. Por favor, reinicie o programa para certificar-se de que você não está conectado ao node banido. +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=transmissão de preço popup.warning.seed=semente popup.warning.mandatoryUpdate.trading=Faça o update para a última versão do Bisq. Um update obrigatório foi lançado e desabilita negociações em versões antigas. Por favor, veja o Fórum do Bisq para mais informações. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=Para garantir que ambas as partes sigam o protoco popup.info.cashDepositInfo=Certifique-se de que você possui uma agência bancária em sua região para poder fazer o depósito em dinheiro.\nO ID (BIC/SWIFT) do banco do vendedor é: {0}. popup.info.cashDepositInfo.confirm=Eu confirmo que posso fazer o depósito popup.info.shutDownWithOpenOffers=O Bisq está desligando, mas há ofertas abertas.\n\nEstas ofertas não ficarão disponíveis na rede P2P enquanto o Bisq estiver desligado, mas elas serão republicadas na rede assim que você reiniciar o programa.\n\nPara manter suas ofertas online, mantenha o Bisq aberto e certifique-se de que o seu computador continua online (ex: certifique-se de que o computador não está entrando em modo de hibernação). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=Notificação privada importante! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: a rede de exchange decentralizada de bitcoin # GUI Util #################################################################### -guiUtil.miningFeeInfo=Certifique-se de que a taxa de mineração utilizada na sua carteira externa tenha pelo menos {0} satoshis/byte. Caso contrário, as transações da negociação podem não ser confirmadas e a negociação pode resultar numa disputa. +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=Contas de negociação salvas na pasta:\n{0} guiUtil.accountExport.noAccountSetup=Você não tem contas de negociação para exportar. @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=Conta de pagamento criada {0} atrás. peerInfoIcon.tooltip.unknownAge=Idade da conta de pagamento desconhecida. tooltip.openPopupForDetails=Abrir popup para mais detalhes +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=Abrir um explorer de blockchain externo para o endereço: {0} tooltip.openBlockchainForTx=Abrir um explorer de blockchain externo para a transação: {0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=Erro ao abrir a carteira padrão Bitcoin. Tal peerInfoIcon.tooltip={0}\nRótulo: {1} txIdTextField.copyIcon.tooltip=Copiar ID da transação -txIdTextField.blockExplorerIcon.tooltip=Abrir um explorer de blockchain com o ID dessa transação +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=Abrir um explorer de blockchain com o ID navigation.account=\"Conta\" navigation.account.walletSeed=\"Conta/Semente da carteira\" -navigation.funds.availableForWithdrawal=\"Fundos/Enviar fundos\" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"Portfolio/Minhas ofertas\" navigation.portfolio.pending=\"Portfolio/Negociações em aberto\" navigation.portfolio.closedTrades=\"Portfólio/Histórico\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=Suas carteiras estão encriptadas.\n\nApós a rest seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=Carteiras recuperadas com sucesso com as novas palavras semente.\n\nVocê precisa desligar e reiniciar o aplicativo. seed.restore.error=Ocorreu um erro ao restaurar as carteiras com palavras semente.{0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=Conta payment.account.no=Nº da conta payment.account.name=Nome da conta payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=Nome completo do titular da conta payment.account.fullName=Nome completo (nome e sobrenome) payment.account.state=Estado/Província/Região diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 2cc66d379d9..3885e9f10e8 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -35,7 +35,7 @@ shared.no=Não shared.iUnderstand=Eu compreendo shared.na=N/D shared.shutDown=Desligar -shared.reportBug=Reportar erro em Gitub Issues +shared.reportBug=Report bug on GitHub shared.buyBitcoin=Comprar bitcoin shared.sellBitcoin=Vender bitcoin shared.buyCurrency=Comprar {0} @@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (mín - máx) shared.removeOffer=Remover oferta shared.dontRemoveOffer=Não remover a oferta shared.editOffer=Editar oferta -shared.openLargeQRWindow=Abrir QR-Code em janela grande +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=Conta de negociação -shared.faq=Visite a página web de FAQ +shared.faq=Visit FAQ page shared.yesCancel=Sim, cancelar shared.nextStep=Próximo passo shared.selectTradingAccount=Selecionar conta de negociação shared.fundFromSavingsWalletButton=Transferir fundos da carteira Bisq shared.fundFromExternalWalletButton=Abrir sua carteira externa para o financiamento -shared.openDefaultWalletFailed=Falha na abertura do programa padrão da carteira Bitcoin. Talvez você não possua uma instalada? +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=Distância em % do preço de mercado shared.belowInPercent=Abaixo % do preço de mercado shared.aboveInPercent=Acima % do preço de mercado shared.enterPercentageValue=Insira % do valor shared.OR=OU -shared.notEnoughFunds=Você não tem fundos suficientes na sua carteira Bisq.\nVocê precisa de {0} mas tem apenas {1} na sua carteira Bisq.\n\nPor favor reembolse o negócio à partir de uma carteira de Bitcoin externa ou reembolse a sua carteira Bisq em \"Fundos/Receber fundos\". +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=Esperando pelos fundos... shared.depositTransactionId=ID da transação de depósito shared.TheBTCBuyer=O comprador de BTC @@ -116,22 +116,22 @@ shared.You=Você shared.reasonForPayment=Motivo do pagamento shared.sendingConfirmation=Enviando confirmação... shared.sendingConfirmationAgain=Por favor envia a confirmação de novo -shared.exportCSV=Exportar para csv +shared.exportCSV=Export to CSV shared.exportJSON=Exportar para JSON shared.noDateAvailable=Sem dada disponível shared.noDetailsAvailable=Sem detalhes disponíveis shared.notUsedYet=Ainda não usado shared.date=Data shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? -shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=Copiar para área de transferência shared.language=Idioma shared.country=País shared.applyAndShutDown=Aplicar e desligar shared.selectPaymentMethod=Selecionar método de pagamento -shared.accountNameAlreadyUsed=Este nome de conta já está a ser utilizado numa conta guardada.\nPor favor use um outro nome. +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=Você realmente quer apagar a conta selecionada? -shared.cannotDeleteAccount=Você não pode apagar a conta pois ela está sendo usada em uma oferta aberta ou negócio. +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=Ainda não há contas configuradas shared.manageAccounts=Gerir contas shared.addNewAccount=Adicionar uma nova conta @@ -214,7 +214,8 @@ shared.mediator=Mediador shared.arbitrator=Árbitro shared.refundAgent=Árbitro shared.refundAgentForSupportStaff=Agente de Reembolso -shared.delayedPayoutTxId=ID da transação do colateral de reembolso +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=Sede do banco aceite (aceitador):\n {0} offerbook.availableOffers=Ofertas disponíveis offerbook.filterByCurrency=Filtrar por moeda offerbook.filterByPaymentMethod=Filtrar por método de pagamento -offerbook.timeSinceSigning=Tempo desde a assinatura +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=Esta conta foi verificada e {0} offerbook.timeSinceSigning.info.arbitrator=assinada pelo árbitro e pode assinar contas de pares offerbook.timeSinceSigning.info.peer=assinada por um par, esperando que os limites sejam aumentados @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=assinada por um par e pode assinar contas offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} dias offerbook.timeSinceSigning.daysSinceSigning.long={0} desde a assinatura +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=Quando você completa com sucesso um negócio com um par que tenha uma conta de pagamento assinada, a sua conta de pagamento é assinada .\n{0} dias depois, o limite inicial de {1} é aumentado e a sua conta pode assinar contas de pagamento de outros pares. offerbook.timeSinceSigning.notSigned=Ainda não assinada @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=A desativação da oferta falhou:\n{0} offerbook.activateOffer.failed=A publicação da oferta falhou:\n{0} offerbook.withdrawFundsHint=Você pode levantar fundos que você pagou a partir do ecrã de {0}. -offerbook.warning.noTradingAccountForCurrency.headline=Não há conta de negociação para a moeda selecionada. -offerbook.warning.noTradingAccountForCurrency.msg=Você não tem uma conta de negociação para a moeda selecionada.\nVocê quer criar uma oferta com uma das suas contas de negociação existentes? -offerbook.warning.noMatchingAccount.headline=Não há conta de negociação compatível. -offerbook.warning.noMatchingAccount.msg=Para aceitar esta oferta, você precisará configurar uma conta de pagamento usando este método de pagamento.\n\nVocê gostaria de fazer isso agora? +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=Esta oferta não pode ser aceite devido às restrições de negócio da contraparte @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=O método de pagamento usado nessa oferta offerbook.warning.nodeBlocked=O endereço onion desse negociador foi bloqueado pelos desenvolvedores do Bisq.\nProvavelmente, há um erro não tratado causando problemas ao aceitar ofertas desse negociador. offerbook.warning.requireUpdateToNewVersion=A sua versão do Bisq deixou de ser compatível para negociação.\nAtualize para a versão mais recente do Bisq em https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=A sua conta de pagamento foi criada há {0}. O seu limite de negócio é baseado na idade da conta e não é suficiente para essa oferta.\n\nO seu limite de negócio é de: {1}\nA mín. quantia de negócio da oferta é de: {2}.\n\nVocê não pode aceitar essa oferta agora. Assim que a sua conta tiver mais de 2 meses esta restrição será removida. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=Venderá ao preço de mercado (atualizado à cada minuto). offerbook.info.buyAtMarketPrice=Comprará ao preço de mercado (atualizado à cada minuto). @@ -541,7 +543,7 @@ portfolio.tab.history=Histórico portfolio.tab.failed=Falhou portfolio.tab.editOpenOffer=Editar oferta -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=Esperando confirmação da blockchain portfolio.pending.step2_buyer.startPayment=Iniciar pagamento @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=Aguardar até que o pagamento c portfolio.pending.step3_seller.confirmPaymentReceived=Confirmar pagamento recebido portfolio.pending.step5.completed=Concluído +portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status +portfolio.pending.autoConf=Auto-confirmed +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key +portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Auto-confirm feature is disabled. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=A transação de depósito foi publicada.\n{0} precisa aguardar pelo menos uma confirmação da blockchain antes de iniciar o pagamento. portfolio.pending.step1.warn=A transação de depósito ainda não foi confirmada. Isso pode acontecer em casos raros, quando a taxa de financiamento de um negociador proveniente de uma carteira externa foi muito baixa. portfolio.pending.step1.openForDispute=A transação de depósito ainda não foi confirmada. Você pode esperar mais tempo ou entrar em contato com o mediador para obter assistência. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=Por favor vá à sua página web de serviços # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Por favor contacte o vendedor de BTC pelo contacto fornecido e marque um encontro para pagar {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Iniciar pagamento usando {0} +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=Quantia a transferir portfolio.pending.step2_buyer.sellersAddress=Endereço {0} do vendedor portfolio.pending.step2_buyer.buyerAccount=A sua conta de pagamento a ser usada @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might veri portfolio.pending.step2_buyer.confirmStart.headline=Confirme que você iniciou o pagamento portfolio.pending.step2_buyer.confirmStart.msg=Você iniciou o pagamento de {0} para o seu parceiro de negociação? portfolio.pending.step2_buyer.confirmStart.yes=Sim, iniciei o pagamento - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=Aguardar o pagamento portfolio.pending.step2_seller.f2fInfo.headline=Informação do contacto do comprador portfolio.pending.step2_seller.waitPayment.msg=A transação de depósito tem pelo menos uma confirmação da blockchain.\nVocê precisa esperar até que o comprador de BTC inicie o pagamento {0}. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=Quantia a receber portfolio.pending.step3_seller.yourAddress=Seu endereço de {0} portfolio.pending.step3_seller.buyersAddress=Endereço de {0} do comprador: portfolio.pending.step3_seller.yourAccount=Sua conta de negociação -portfolio.pending.step3_seller.buyersAccount=Conta de negociação do comprador +portfolio.pending.step3_seller.xmrTxHash=ID de transação +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=Confirmar recibo de pagamento portfolio.pending.step3_seller.buyerStartedPayment=O comprador de BTC começou o pagamento de {0}.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Verifique as confirmações da blockchain na sua carteira altcoin ou explorador de blocos e confirme o pagamento quando houverem confirmações da blockchain suficientes. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Por favor, use esta função apenas em c portfolio.pending.timeLockNotOver=Você deve esperar ≈{0} (mais {1} blocos) antes que você possa abrir uma disputa de arbitragem. portfolio.pending.error.depositTxNull=A transação de depósito é null. Você não pode abrir a disputa sem uma transação de depósito válida. Por favor vá à \"Definições/Informação da Rede\" e re-sincronize o ficheiro SPV.\n\nPara mais ajuda por favor contacte o canal de apoio do Bisq na equipa Keybase do Bisq. -portfolio.pending.mediationResult.error.depositTxNull=A transação de depósito é null. O negócio foi movido para a secção de negócios falhados. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=A transação de depósito não foi confirmada. Você pode abrir uma disputa de arbitragem com uma transação de depósito não confirmada. Por favor espere até que seja confirmada ou vá à \"Definições/Informação da Rede\" e re-sincronize o ficheiro SPV.\n\nPara mais ajuda por favor contacte o canal de apoio do Bisq na equipa Keybase do Bisq. portfolio.pending.notification=Notificação @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=Ver a resolução proposta portfolio.pending.mediationResult.popup.headline=Resultado da mediação para o negócio com o ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=O seu par de negócio aceitou a sugestão do mediador para o negócio {0} portfolio.pending.mediationResult.popup.info=O mediador sugeriu o seguinte pagamento:\nVocê recebeu: {0}\nO seu par de negociação recebe: {1}\n\nVocê pode aceitar ou rejeitar este pagamento sugerido.\n\nAo aceitar, você assina a transação do pagamento sugerido. Se o seu par de negociação também aceitar e assinar, o pagamento será completado e o negócio será fechado.\n\nSe ambos rejeitarem a sugestão, você terá que esperar até {2} (bloco {3}) para abrir uma segunda ronda de disputa com um árbitro que investigará o caso novamente e fará um pagamento baseado nas suas descobertas .\n\nO árbitro pode cobrar uma pequena taxa (máximo: o depósito de segurança do negociador) como compensação para o seu trabalho. Ter ambos os negociadores concordando com a sugestão do mediador é o caminho preferido - solicitar arbitragem é para circunstâncias excepcionais, tais como se um negociador estiver seguro de que o mediador não fez uma sugestão de pagamento justa (ou se o seu par estiver inacessível).\n\nPara mais detalhes sobre o novo modelo de arbitragem:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Rejeitar e solicitar arbitragem portfolio.pending.mediationResult.popup.alreadyAccepted=Você já aceitou +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=Concluído portfolio.closed.ticketClosed=Arbitrado portfolio.closed.mediationTicketClosed=Mediado @@ -763,8 +817,8 @@ portfolio.closed.canceled=Cancelado portfolio.failed.Failed=Falhado portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Arbitragem Antiga support.tab.ArbitratorsSupportTickets=Bilhetes de {0} support.filter=Search disputes support.filter.prompt=Insira o ID do negócio, data, endereço onion ou dados da conta + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=Enviar notificação privada -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=Notificação privada +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=Não há bilhetes abertos support.sendingMessage=Enviando mensagem... support.receiverNotOnline=O recipiente não está online. A mensagem foi guardada na caixa de correio. @@ -905,6 +970,9 @@ support.peerOpenedDispute=O seu par de negociação solicitou uma disputa.\n\n{0 support.peerOpenedDisputeForMediation=O seu par de negociação solicitou uma mediação.\n\n{0}\n\nVersão Bisq: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Endereço do nó do mediador: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=Informação da rede settings.tab.about=Sobre setting.preferences.general=Preferências gerais -setting.preferences.explorer=Explorador de blocos de Bitcoin -setting.preferences.explorer.bsq=Explorador de blocos de BSQ +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Máx. desvio do preço de mercado setting.preferences.avoidStandbyMode=Evite o modo espera +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=Valores acima de {0}% não são permitidos. setting.preferences.txFee=Taxa de transação de levantamento (satoshis/byte): setting.preferences.useCustomValue=Usar valor personalizado @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Abrir ferramenta e emergênci setting.about.shortcuts.showTorLogs=Escolher o nível de log para mensagens Tor entre DEBUG e WARN -setting.about.shortcuts.showDisputeStatistics=Mostrar resumo de todas as disputas -setting.about.shortcuts.showDisputeStatistics.value=Navigar para o ecrã de disputas e pressionar: {0} - setting.about.shortcuts.manualPayoutTxWindow=Abrir janela para pagamento manual da tx de depósito multi-assinatura 2de2 setting.about.shortcuts.reRepublishAllGovernanceData=Republicar informação da governação da OAD (propostas, votos) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navigar para conta e pressionar setting.about.shortcuts.registerMediator=Registar mediador (apenas medidor/árbitro) setting.about.shortcuts.registerMediator.value=Navigar para conta e pressionar: {0} -setting.about.shortcuts.reOpenDispute=Re-abrir disputas fechadas (apenas mediador/árbitro) -setting.about.shortcuts.reOpenDispute.value=Selecionar disputas fechadas e pressionar: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Abrir janela para assinatura de idade da conta (apenas árbitros legacy) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navigar para o ecrã de árbitro legacy e pressionar: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Enviar alerta ou mensagem de atualização setting.about.shortcuts.sendFilter=Definir Filtro (atividade privilegiada) setting.about.shortcuts.sendPrivateNotification=Enviar notificação privada ao par (atividade privilegiada) -setting.about.shortcuts.sendPrivateNotification.value=Abrir informação do par no avatar ou na disputa e pressionar: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Certifique-se de seguir os requisitos para o us account.altcoin.popup.wallet.confirm=Eu entendo e confirmo que eu sei qual carteira que preciso usar. account.altcoin.popup.upx.msg=Negociar UPX no Bisq exige que você entenda e cumpra os seguintes requerimentos:\n\nPara enviar o UPX, você precisa usar a carteira GUI oficial do uPlexa ou a carteira CLI do uPlexa com o sinalizador store-tx-info ativado (padrão em novas versões). Certifique-se de que você pode acessar a chave da tx, pois isso seria necessário em caso de disputa.\nuplexa-wallet-cli (use o comando get_tx_key)\nuplexa-wallet-gui (vá para a aba do histoórico e clique no botão (P) para prova de pagamento)\n\nEm exploradores de blocos normais, a transferência não é verificável.\n\nVocê precisa fornecer ao árbitro os seguintes dados em caso de disputa:\n- A chave privada da tx\n- O hash da transação\n- Endereço público do destinatário\n\nA falha no fornecimento dos dados acima, ou se você usou uma carteira incompatível, resultará na perda do caso da disputa. O remetente de UPX é responsável por fornecer a verificação da transferência de UPX ao árbitro em caso de disputa.\n\nNão é necessário um ID de pagamento, apenas o endereço público normal.\nSe você não tiver certeza sobre esse processo, visite o canal de discord do uPlexa (https://discord.gg/vhdNSrV) ou o chat do Telegram do uPlexa (https://t.me/uplexaOfficial) para encontrar mais informações. account.altcoin.popup.arq.msg=Negociar o ARQ no Bisq requer que você entenda e atenda aos seguintes requerimentos:\n\nPara enviar o ARQ, você precisa usar a wallet oficial do ArQmA GUI ou a carteira do ArQmA CLI com o marcador store-tx-info ativado (padrão em novas versões). Por favor, certifique-se que você pode acessar a chave da tx porque isso seria necessário em caso de uma disputa.\narqma-wallet-cli (use o comando get_tx_key)\narqma-wallet-gui (vá para a aba do histórico e clique no botão (P) para comprovar o pagamento)\n\nEm exploradores de blocos normais, a transferência não é verificável.\n\nVocê precisa fornecer ao mediador ou árbitro os seguintes dados em caso de disputa:\n- A chave privada da tx\n- O hash da transação\n- o endereço público do destinatário\n\nA falha em fornecer os dados acima, ou se você usou uma carteira incompatível, resultará na perda do caso de disputa. O remetente do ARQ é responsável por fornecer a verificação da transferência do ARQ ao mediador ou árbitro em caso de disputa.\n\nNão é necessário um código de pagamento, apenas o endereço público normal.\nSe você não tiver certeza sobre esse processo, visite o canal de discord do ArQmA (https://discord.gg/s9BQpJT) ou o fórum do ArQmA (https://labs.arqma.com) para obter mais informações. -account.altcoin.popup.xmr.msg=A negociação de XMR no Bisq exige que você entenda e cumpra os seguintes requerimentos:\n\nProvar pagamentos: como o Monero é uma moeda privada, alguns detalhes da transação não estão disponíveis publicamente na blockchain e, em caso de disputa, o mediador ou o árbitro precisa deles para verificar se a transação foi realmente feita. No Bisq, o remetente da transação XMR é o responsável por fornecer essas informações ao mediador ou árbitro no caso de uma disputa. Para fazer isso, você deve enviar o XMR usando uma carteira que forneça as informações necessárias para provar que o pagamento foi feito, o que inclui:\n\n- a chave da transação (chave TX, chave secreta TX ou chave privada TX)\n- o ID da transação (Tx ID ou Tx Hash)\n- o endereço de destino (endereço do destinatário)\n\nEssas informações podem ser encontradas nas carteiras oficiais Monero GUI & CLI, MyMonero e Exodus (desktop), bem como em Cake Wallet, MyMonero e Monerujo (mobile), nos seguintes locais:\n\n- GUI do Monero: vá para a aba Transações\n- CLI do Monero: use o comando get_tx_key TXID. O sinalizador store-tx-info deve estar ativado (ativado por padrão em novas versões)\n- Outras carteiras: acesse o histórico de transações e procure a chave de transação (chave Tx ou chave secreta) e o endereço de destino numa transação enviada. A opção Salvar endereço do destinatário deve estar ativada nas configurações da Cake Wallet.\n\nSe você estiver usando uma carteira diferente da mencionada acima, verifique se pode acessar essas três informações. Como a chave da transação e o endereço de destino estão armazenados no software da carteira Monero e não podem ser recuperados na blockchain Monero, você nunca deve excluir ou restaurar sua carteira Monero antes que um negócio do Bisq seja concluída. O não fornecimento dos dados acima resultará na perda do caso de disputa.\n\nVerificar pagamentos: com essas três informações, a verificação de que uma quantidade de Monero foi enviada para um endereço específico pode ser realizada da seguinte maneira:\n\n- Monero GUI: altere a carteira para o modo Avançado e vá para Avançado> Prove/check> Verificar transação\n- CLI Monero: use o comando check_tx_key TXID TXKEY ADDRESS\n- Ferramenta XMR checktx (https://xmr.llcoins.net/checktx.html)\n- Explore o site Monero (https://www.exploremonero.com/receipt)\n\nSe você ainda não tiver certeza sobre esse processo, visite (https://www.getmonero.org/resources/user-guides/prove-payment.html) para encontrar mais informações ou pergunte no subreddit de suporte do Monero (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Negociar MSR no Bisq requer que você entenda e cumpra os seguintes requerimentos:\n\nPara enviar MSR, você precisa usar a carteira GUI oficial do Masari ou a carteira CLI do Masari com o marcador store-tx-info ativado (ativado por padrão) ou a carteira web do Masari (https://wallet.getmasari.org). Por favor, certifique-se que você pode acessar a chave da tx porque isso seria necessário em caso de uma disputa.\nmasari-wallet-cli (use o comando get_tx_key)\nmasari-wallet-gui (vá para a aba do histórico e clique no botão (P) para comprovar o pagamento)\n\nMasari Web Wallet (vá para Account -> histórico de transação e veja os detalhes da sua transação enviada)\n\nA verificação pode ser realizada na carteira.\nmasari-wallet-cli: usando o comando (check_tx_key).\nmasari-wallet-gui: na aba Advanced > Prove/Check.\nA verificação pode ser realizada no eplorador de blocos\nExplorador de blocos aberto (https://explorer.getmasari.org), use a barra de procurar para encontrar o hash da transação.\nUma que vez que a transação for encontrada, desça até ao baixo da àrea 'Prove Sending' e preencha os detalhes necessários.\nVocê precisa fornecer ao mediador ou ao árbitro os seguintes dados em caso de disputa:\n- A chave privada da tx\n- O hash da transação\n- o endereço público do destinatário\n\nFalha em fornecer os dados acima, ou se você usou uma carteira incompatível, resultará na perda do caso de disputa. O remetente da XMR é responsável por fornecer a verificação da transferência da MSR para o mediador ou o árbitro no caso de uma disputa.\n\nNão é necessário um código de pagamento, apenas o endereço público normal.\nSe você não tem certeza sobre o processo, peça ajuda no Discord official do Masari (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Negociar o BLUR no Bisq requer que você entenda e cumpra os seguintes requerimentos:\n\nPara enviar o BLUR você deve usar a carteira CLI da Blur Network ou a carteira GUI.\n\nSe você estiver usando a carteira CLI, um hash da transação (tx ID) será exibido após uma transferência ser enviada. Você deve guardar esta informação. Imediatamente após o envio da transferência, você deve usar o comando 'get_tx_key' para recuperar a chave privada da transação. Se você não conseguir executar essa etapa, talvez não consiga recuperar a chave mais tarde.\n\nSe você estiver usando a carteira GUI do Blur Network, a chave privada da transação e a ID da transação podem ser encontradas convenientemente na aba "Histórico". Imediatamente após o envio, localize a transação de interesse. Clique no símbolo "?" no canto inferior direito da caixa que contém a transação. Você deve guardar esta informação.\n\nCaso a arbitragem seja necessária, você deve apresentar o seguinte à um mediador ou árbitro: 1.) a ID da transação, 2.) a chave privada da transação e 3.) o endereço do destinatário. O mediador ou árbitro verificará a transferência do BLUR usando o Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nO não fornecimento das informações necessárias ao mediador ou árbitro resultará na perda da disputa. Em todos os casos de disputa, o remetente de BLUR tem 100% de responsabilidade na verificação de transações para um mediador ou árbitro.\n\nSe você não entender esses requerimentos não negocie no Bisq. Primeiro, procure ajuda no Discord da Rede de Blur (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Price node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets API operator +dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador dos mercados # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Operador do explorador de BSQ +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operador da transmissão de notificações móveis # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=Notas de resumo disputeSummaryWindow.addSummaryNotes=Adicionar notas de resumo disputeSummaryWindow.close.button=Fechar bilhete -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nPróximos passos:\nAbrir negócio e aceitar ou rejeitar as sugestões do mediador -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nPróximos passos:\nNenhuma ação adicional é necessária. Se o árbitro decidir a seu favor, você verá uma transação de "Reembolso da arbitragem" em Fundos/Transações + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=Você também precisa fechar o bilhete dos pares de negociação! disputeSummaryWindow.close.txDetails.headline=Publicar transação de reembolso disputeSummaryWindow.close.txDetails.buyer=O comprador recebe {0} no endereço: {1}\n disputeSummaryWindow.close.txDetails.seller=O vendedor recebe {0} no endereço: {1}\n disputeSummaryWindow.close.txDetails=Gastando: {0}\n{1}{2}Taxa da transação: {3} ({4} satoshis/byte)\nTamanho da transação: {5} Kb\n\nTem a certeza de que quer publicar esta transação? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} ferramenta de emergência da carteira emptyWalletWindow.info=Por favor, use isso apenas em caso de emergência, se você não puder aceder o seu fundo a partir da interface do utilizador.\n\nPor favor, note que todas as ofertas abertas serão fechadas automaticamente ao usar esta ferramenta.\n\nAntes de usar essa ferramenta, faça backup do seu diretório de dados. Você pode fazer isso em \"Conta/Backup\".\n\nPor favor comunique-nos o seu problema e envie um relatório de erros no Github ou no fórum Bisq para que possamos investigar o que causou o problema. emptyWalletWindow.balance=O saldo disponível da sua carteira: @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=Nós de transmissão de preço filtrados (endereços filterWindow.btcNode=Nós de Bitcoin filtrados (endereços + portas sep. por vírgula) filterWindow.preventPublicBtcNetwork=Prevenir uso da rede de Bitcoin pública filterWindow.disableDao=Desativar OAD +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Versão mín. necessária para a OAD filterWindow.disableTradeBelowVersion=Mín. versão necessária para negociação filterWindow.add=Adicionar filtro @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} bitcoin offerDetailsWindow.creationDate=Data de criação offerDetailsWindow.makersOnion=Endereço onion do ofertante -qRCodeWindow.headline=Código QR -qRCodeWindow.msg=Por favor, use esse Código QR para financiar sua carteira Bisq à partir da sua carteira externa. +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=Pedido de pagamento:\n{0} selectDepositTxWindow.headline=Selecionar transação de depósito para disputa @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=Enviar notificação privada showWalletDataWindow.walletData=Dados da carteira showWalletDataWindow.includePrivKeys=Incluir chaves privadas +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=Acordo de utilizador @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=A transação de depósito do negócio error.closedTradeWithNoDepositTx=A transação de depósito do negócio fechado com o ID de negócio {0} é null.\n\nPor favor reinicie o programa para limpar a lista de negócios fechados. popup.warning.walletNotInitialized=A carteira ainda não foi inicializada +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=Você provavelmente tem a versão errada do Bisq para este computador.\nA arquitetura do seu computador é: {0}.\nO binário Bisq que você instalou é: {1}.\nPor favor, desligue e reinstale a versão correta ({2}). popup.warning.incompatibleDB=Detectamos ficheiros de base de dados incompatíveis!\n\nEsses ficheiros de base de dados não são compatíveis com nossa base de código atual:\n{0}\n\nFizemos um backup do(s) ficheiro(s) corrompido(s) e aplicamos os valores padrão para uma nova versão da base de dados.\n\nO backup está localizado em:\n{1} /db/backup_of_corrupted_data.\n\nPor favor, verifique se você tem a última versão do Bisq instalada.\nVocê pode baixá-lo em:\nhttps://bisq.network/downloads\n\nPor favor, reinicie o programa. popup.warning.startupFailed.twoInstances=Bisq já está em execução. Você não pode executar duas instâncias do Bisq. @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=Sua carteira BSQ não possui fundos suf popup.warning.messageTooLong=Sua mensagem excede o tamanho máx. permitido. Por favor enviá-la em várias partes ou carregá-la utilizando um serviço como https://pastebin.com. popup.warning.lockedUpFunds=Você trancou fundos de um negócio falhado..\nSaldo trancado: {0} \nEndereço da tx de Depósito: {1}\nID de negócio: {2}.\n\nPor favor abra um bilhete de apoio selecionando o negócio no ecrã de negócios abertos e pressione \"alt + o\" ou \"option + o\"." -popup.warning.nodeBanned=Um dos nós de {0} foi banido. Por favor reinicie seu programa para ter certeza de não estar conectado ao nó banido. +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=transmissão de preço popup.warning.seed=semente popup.warning.mandatoryUpdate.trading=Por favor, atualize para a versão mais recente do Bisq. Uma atualização obrigatória que desativa negociação para versões antigas foi lançada. Por favor, confira o Fórum Bisq para mais informações. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=Para garantir que ambos os negociadores seguem o popup.info.cashDepositInfo=Por favor, certifique-se de que você tem uma agência bancária na sua área para poder fazer o depósito em dinheiro.\nO ID do banco (BIC/SWIFT) do vendedor é: {0}. popup.info.cashDepositInfo.confirm=Eu confirmo que eu posso fazer o depósito popup.info.shutDownWithOpenOffers=Bisq está sendo fechado, mas há ofertas abertas. \n\nEstas ofertas não estarão disponíveis na rede P2P enquanto o Bisq estiver desligado, mas elas serão publicadas novamente na rede P2P na próxima vez que você iniciar o Bisq.\n\nPara manter suas ofertas on-line, mantenha o Bisq em execução e certifique-se de que este computador também permaneça online (ou seja, certifique-se de que ele não entra no modo de espera... o modo de espera do monitor não causa problema). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=Notificação privada importante! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: Uma rede de echange de bitcoin descentralizada # GUI Util #################################################################### -guiUtil.miningFeeInfo=Certifique-se de que a taxa de mineração usada na sua carteira externa seja pelo menos {0} satoshis/byte. Caso contrário, as transacções de negócio não podem ser confirmadas e um negócio acabaria em disputa. +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=Contas de negociação guardadas em:\n{0} guiUtil.accountExport.noAccountSetup=Você não tem contas de negociação prontas para exportar. @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=Conta de pagamento criada há {0}. peerInfoIcon.tooltip.unknownAge=Idade de conta de pagamento desconhecida. tooltip.openPopupForDetails=Abrir popup para mais detalhes +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=Abrir um explorador de blockchain externo para endereço: {0} tooltip.openBlockchainForTx=Abrir um explorador de blockchain externo para transação: {0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=Abrir a programa de carteira Bitcoin padrão peerInfoIcon.tooltip={0}\nRótulo: {1} txIdTextField.copyIcon.tooltip=Copiar ID de transação -txIdTextField.blockExplorerIcon.tooltip=Abrir um explorador de blockchain com o ID dessa transação +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=Abrir um explorador de blockchain com o navigation.account=\"Conta\" navigation.account.walletSeed=\"Conta/Semente da carteira\" -navigation.funds.availableForWithdrawal=\"Fundos/Enviar fundos\" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"Portefólio/As minhas ofertas abertas\" navigation.portfolio.pending=\"Portefólio/Negócios abertos\" navigation.portfolio.closedTrades=\"Portefólio/Histórico\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=Suas carteiras são encriptadas.\n\nApós a restau seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=Carteiras restauradas com sucesso com as novas palavras-semente.\n\nVocê precisa desligar e reiniciar o programa. seed.restore.error=Um erro ocorreu ao restaurar as carteiras com palavras-semente.{0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=Conta payment.account.no=Nº da conta payment.account.name=Nome da conta payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=Nome completo do titular da conta payment.account.fullName=Nome completo (primeiro, nome do meio, último) payment.account.state=Estado/Província/Região diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index c0124177f9a..d9ab63b2f73 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -35,7 +35,7 @@ shared.no=Нет shared.iUnderstand=Я понимаю shared.na=Н/Д shared.shutDown=Закрыть -shared.reportBug=Сообщить об ошибке на Github +shared.reportBug=Report bug on GitHub shared.buyBitcoin=Купить биткойн shared.sellBitcoin=Продать биткойн shared.buyCurrency=Купить {0} @@ -94,21 +94,21 @@ shared.BTCMinMax=ВТС (мин. — макс.) shared.removeOffer=Удалить предложение shared.dontRemoveOffer=Не удалять предложение shared.editOffer=Изменить предложение -shared.openLargeQRWindow=Показать QR-код в большом окне +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=Торговый счёт -shared.faq=Посетить страницу ЧаВо +shared.faq=Visit FAQ page shared.yesCancel=Да, отменить shared.nextStep=Далее shared.selectTradingAccount=Выбрать торговый счёт shared.fundFromSavingsWalletButton=Перевести средства с кошелька Bisq shared.fundFromExternalWalletButton=Открыть внешний кошелёк для пополнения -shared.openDefaultWalletFailed=Не удалось открыть приложение биткойн-кошелька. Возможно, вы ещё его не установили? +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=Отклонение от рыночного курса в % shared.belowInPercent=% ниже рыночного курса shared.aboveInPercent=% выше рыночного курса shared.enterPercentageValue=Ввести величину в % shared.OR=ИЛИ -shared.notEnoughFunds=В вашем кошельке Bisq недостаточно средств.\nНеобходимо {0}; имеется {1}.\n\nПереведите средства из внешнего биткойн-кошелька или пополните свой кошелёк Bisq в разделе \«Средства/Получить средства\». +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=Ожидание средств... shared.depositTransactionId=Идентификатор зачисления на счёт shared.TheBTCBuyer=Покупатель ВТС @@ -116,22 +116,22 @@ shared.You=Вы shared.reasonForPayment=Назначение платежа shared.sendingConfirmation=Отправка подтверждения... shared.sendingConfirmationAgain=Отправьте подтверждение повторно -shared.exportCSV=Экспорт в csv +shared.exportCSV=Export to CSV shared.exportJSON=Экспорт в JSON shared.noDateAvailable=Дата не указана shared.noDetailsAvailable=Подробности не указаны shared.notUsedYet=Ещё не использовано shared.date=Дата shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? -shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=Скопировать в буфер shared.language=Язык shared.country=Страна shared.applyAndShutDown=Применить и закрыть приложение shared.selectPaymentMethod=Выбрать способ оплаты -shared.accountNameAlreadyUsed=Это название счёта уже используется.\nУкажите иное название. +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=Вы действительно хотите удалить выбранный счёт? -shared.cannotDeleteAccount=Невозможно удалить счёт, так как он используется в открытом предложении или сделке. +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=Нет настроенных счетов shared.manageAccounts=Управление счетами shared.addNewAccount=Добавить новый счёт @@ -214,7 +214,8 @@ shared.mediator=Посредник shared.arbitrator=Арбитр shared.refundAgent=Арбитр shared.refundAgentForSupportStaff=Refund agent -shared.delayedPayoutTxId=Refund collateral transaction ID +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=Допустимые страны банка offerbook.availableOffers=Доступные предложения offerbook.filterByCurrency=Фильтровать по валюте offerbook.filterByPaymentMethod=Фильтровать по способу оплаты -offerbook.timeSinceSigning=Time since signing +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts offerbook.timeSinceSigning.info.peer=signed by a peer, waiting for limits to be lifted @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=signed by peer and can sign peer accounts offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} дн. offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=Не удалось деактивироват offerbook.activateOffer.failed=Не удалось опубликовать предложение:\n{0} offerbook.withdrawFundsHint=Вы можете вывести внесённые средства в разделе «{0}». -offerbook.warning.noTradingAccountForCurrency.headline=У вас нет торгового счёта для выбранной валюты -offerbook.warning.noTradingAccountForCurrency.msg=У вас нет торгового счёта для выбранной валюты.\nСоздать предложение, используя один из существующих торговых счетов? -offerbook.warning.noMatchingAccount.headline=Нет подходящего торгового счёта. -offerbook.warning.noMatchingAccount.msg=Чтобы принять это предложение, вам необходимо добавить платежный счёт с использованием данного метода платежа.\n\nХотите сделать это сейчас? +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due to counterparty trade restrictions @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=Метод платежа, использ offerbook.warning.nodeBlocked=Onion-адрес этого трейдера заблокирован разработчиками Bisq.\nВероятно, принятие предложения от данного трейдера вызывает необрабатываемую ошибку. offerbook.warning.requireUpdateToNewVersion=Ваша версия Bisq больше не подходит для торговли.\nУстановите последнюю версию Bisq, перейдя по ссылке https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=Your payment account has been created {0} ago. Your trade limit is based on the account age and is not sufficient for that offer.\n\nYour trade limit is: {1}\nThe min. trade amount of the offer is: {2}.\n\nYou cannot take that offer at the moment. Once your account is older than 2 months this restriction gets removed. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=Продажа по рыночному курсу (обновляется ежеминутно). offerbook.info.buyAtMarketPrice=Покупка по рыночному курсу (обновляется ежеминутно). @@ -541,7 +543,7 @@ portfolio.tab.history=История portfolio.tab.failed=Не удалось portfolio.tab.editOpenOffer=Изменить предложение -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=Ожидание подтверждения в блокчейне portfolio.pending.step2_buyer.startPayment=Сделать платеж @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=Дождитесь получе portfolio.pending.step3_seller.confirmPaymentReceived=Подтвердите получение платежа portfolio.pending.step5.completed=Завершено +portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status +portfolio.pending.autoConf=Auto-confirmed +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key +portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Auto-confirm feature is disabled. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=Депозитная транзакция опубликована.\n{0} должен дождаться хотя бы одного подтверждения в блокчейне перед началом платежа. portfolio.pending.step1.warn=The deposit transaction is still not confirmed. This sometimes happens in rare cases when the funding fee of one trader from an external wallet was too low. portfolio.pending.step1.openForDispute=The deposit transaction is still not confirmed. You can wait longer or contact the mediator for assistance. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=Заплатите {0} продавцу BTC # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Свяжитесь с продавцом BTC с помощью указанных контактных данных и договоритесь о встрече для оплаты {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Начать оплату, используя {0} +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=Сумма для перевода portfolio.pending.step2_buyer.sellersAddress={0}-адрес продавца portfolio.pending.step2_buyer.buyerAccount=Используемый платёжный счет @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might veri portfolio.pending.step2_buyer.confirmStart.headline=Подтвердите начало платежа portfolio.pending.step2_buyer.confirmStart.msg=Вы начали платеж {0} своему контрагенту? portfolio.pending.step2_buyer.confirmStart.yes=Да - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=Ожидайте платеж portfolio.pending.step2_seller.f2fInfo.headline=Контактная информация покупателя portfolio.pending.step2_seller.waitPayment.msg=Депозитная транзакция подтверждена в блокчейне не менее одного раза.\nДождитесь начала платежа в {0} покупателем BTC. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=Сумма поступления portfolio.pending.step3_seller.yourAddress=Ваш адрес {0} portfolio.pending.step3_seller.buyersAddress=Адрес {0} покупателя portfolio.pending.step3_seller.yourAccount=Ваш торговый счёт -portfolio.pending.step3_seller.buyersAccount=Торговый счёт покупателя +portfolio.pending.step3_seller.xmrTxHash=Идентификатор транзакции +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=Подтвердить получение платежа portfolio.pending.step3_seller.buyerStartedPayment=Покупатель ВТС начал оплату в {0}.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Проверьте количество подтверждений в блокчейне в своём алтькойн-кошельке или обозревателе блоков и подтвердите платеж, если подтверждений достаточно. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Please use this function only in emergen portfolio.pending.timeLockNotOver=You have to wait until ≈{0} ({1} more blocks) before you can open an arbitration dispute. portfolio.pending.error.depositTxNull=The deposit transaction is null. You cannot open a dispute without a valid deposit transaction. Please go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. -portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. The trade gets moved to the failed trades section. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=The deposit transaction is not confirmed. You can not open an arbitration dispute with an unconfirmed deposit transaction. Please wait until it is confirmed or go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. portfolio.pending.notification=Уведомление @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=Завершена portfolio.closed.ticketClosed=Arbitrated portfolio.closed.mediationTicketClosed=Mediated @@ -763,8 +817,8 @@ portfolio.closed.canceled=Отменена portfolio.failed.Failed=Не удалась portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets support.filter=Search disputes support.filter.prompt=Введите идентификатор сделки, дату, onion-адрес или данные учётной записи + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=Отправить личное уведомление -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=Личное уведомление +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=Нет текущих обращений support.sendingMessage=Отправка сообщения... support.receiverNotOnline=Receiver is not online. Message is saved to their mailbox. @@ -905,6 +970,9 @@ support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nB support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nBisq version: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Mediator''s node address: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=Информация о сети settings.tab.about=О проекте setting.preferences.general=Основные настройки -setting.preferences.explorer=Обозреватель блоков сети Биткойн -setting.preferences.explorer.bsq=BSQ block explorer +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Макс. отклонение от рыночного курса setting.preferences.avoidStandbyMode=Избегать режима ожидания +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=Значения выше {0}% запрещены. setting.preferences.txFee=Комиссия за снятие средств (сатоши/байт) setting.preferences.useCustomValue=Задать своё значение @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool fo setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN -setting.about.shortcuts.showDisputeStatistics=Show summary of all disputes -setting.about.shortcuts.showDisputeStatistics.value=Navigate to disputes view and press: {0} - setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navigate to account and press: setting.about.shortcuts.registerMediator=Register mediator (mediator/arbitrator only) setting.about.shortcuts.registerMediator.value=Navigate to account and press: {0} -setting.about.shortcuts.reOpenDispute=Re-open already closed dispute (mediator/arbitrator only) -setting.about.shortcuts.reOpenDispute.value=Select closed dispute and press: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Open window for account age signing (legacy arbitrators only) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navigate to legacy arbitrator view and press: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Send alert or update message (privileged ac setting.about.shortcuts.sendFilter=Set Filter (privileged activity) setting.about.shortcuts.sendPrivateNotification=Send private notification to peer (privileged activity) -setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar or dispute and press: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements account.altcoin.popup.wallet.confirm=Я понимаю и подтверждаю, что знаю, какой кошелёк нужно использовать. account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. account.altcoin.popup.arq.msg=Trading ARQ on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending ARQ, you need to use either the official ArQmA GUI wallet or ArQmA CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\narqma-wallet-cli (use the command get_tx_key)\narqma-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The ARQ sender is responsible for providing verification of the ARQ transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit ArQmA discord channel (https://discord.gg/s9BQpJT) or the ArQmA forum (https://labs.arqma.com) to find more information. -account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand and fulfill the following requirements:\n\nProve payments: since Monero is a private coin, some transaction details aren't publicly available in the blockchain, and, in case of a dispute, the mediator or arbitrator needs them to check if the transaction was really made. In Bisq, the sender of the XMR transaction is the one responsible for providing this information to the mediator or arbitrator in case of a dispute. In order to do that, you must send XMR using a wallet that provides the information required to prove the payment was made, which includes:\n\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nThis information can be found in the official Monero GUI & CLI wallets, MyMonero, and Exodus (desktop) as well as in Cake Wallet, MyMonero, and Monerujo (mobile), in the following locations:\n\n- Monero GUI: go to Transactions tab\n- Monero CLI: use the command get_tx_key TXID. The flag store-tx-info must be enabled (enabled by default in new versions)\n- Other wallets: go to Transactions history and search for Transaction key (Tx key or Secret key) and the destination address in a sent transaction. Save recipient address option must be enabled in Cake Wallet settings.\n\nIf you are using a wallet different from the mentioned above, please be sure you can access those three pieces of information.Since the transaction key and the destination address are stored in the Monero wallet software, and they cannot be recovered in the Monero blockchain, you should never delete or restore your Monero wallet before a Bisq trade is completed. Failure to provide the above data will result in losing the dispute case.\n\nCheck payments: with those three pieces of information, the verification that a quantity of Monero was sent to a specific address can be accomplished the following way:\n\n- Monero GUI: change wallet to Advanced mode and go to Advanced > Prove/check > Check Transaction\n- Monero CLI: use the command check_tx_key TXID TXKEY ADDRESS\n- XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Explore Monero website (https://www.exploremonero.com/receipt)\n\nIf you are still not sure about this process, visit (https://www.getmonero.org/resources/user-guides/prove-payment.html) to find more information or ask a question on the Monero support subreddit (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Trading MSR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending MSR, you need to use either the official Masari GUI wallet, Masari CLI wallet with the store-tx-info flag enabled (enabled by default) or the Masari web wallet (https://wallet.getmasari.org). Please be sure you can access the tx key as that would be required in case of a dispute.\nmasari-wallet-cli (use the command get_tx_key)\nmasari-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nMasari Web Wallet (goto Account -> transaction history and view details on your sent transaction)\n\nVerification can be accomplished in-wallet.\nmasari-wallet-cli : using command (check_tx_key).\nmasari-wallet-gui : on the Advanced > Prove/Check page.\nVerification can be accomplished in the block explorer \nOpen block explorer (https://explorer.getmasari.org), use the search bar to find your transaction hash.\nOnce transaction is found, scroll to bottom to the 'Prove Sending' area and fill in details as needed.\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The MSR sender is responsible for providing verification of the MSR transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process, ask for help on the Official Masari Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Оператор узла да # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets API operator +dao.bond.bondedRoleType.MARKETS_OPERATOR=Оператор узла данных рынка # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Оператор обозревателя блоков BSQ +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Оператор ретранс. моб. уведомлений # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=Примечания disputeSummaryWindow.addSummaryNotes=Добавить примечания disputeSummaryWindow.close.button=Закрыть обращение -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nNext steps:\nOpen trade and accept or reject suggestion from mediator -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=Вам также необходимо закрыть обращение контрагента! disputeSummaryWindow.close.txDetails.headline=Publish refund transaction disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to publish this transaction? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline=Аварийный кошелёк {0} emptyWalletWindow.info=Используйте этот инструмент только в экстренном случае, если вам недоступны средства из пользовательского интерфейса.\n\nУчтите, что все открытые предложения будут автоматически закрыты при использовании этого инструмента.\n\nПрежде чем воспользоваться этим инструментом, создайте резервную копию своего каталога данных. Это можно сделать в разделе \«Счёт/Резервное копирование\».\n\nСообщите нам о неисправности и создайте отчёт о ней в Github или на форуме Bisq, чтобы мы могли выявить её причину. emptyWalletWindow.balance=Доступный баланс кошелька @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=Отфильтрованные ретранслят filterWindow.btcNode=Отфильтрованные узлы Биткойн (адреса + порты через запят.) filterWindow.preventPublicBtcNetwork=Не использовать общедоступную сеть Биткойн filterWindow.disableDao=Отключить ДАО +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Мин. версия, необходимая для работы с ДАО filterWindow.disableTradeBelowVersion=Мин. версия, необходимая для торговли filterWindow.add=Добавить фильтр @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=Подтвердите: принять пред offerDetailsWindow.creationDate=Дата создания offerDetailsWindow.makersOnion=Onion-адрес мейкера -qRCodeWindow.headline=QR-код -qRCodeWindow.msg=Используйте этот QR-код для пополнения своего кошелька Bisq из внешнего кошелька. +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=Запрос платежа:\n{0} selectDepositTxWindow.headline=Выберите транзакцию ввода средств для включения в спор @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=Отправить личное уведом showWalletDataWindow.walletData=Данные кошелька showWalletDataWindow.includePrivKeys=Добавить приватные ключи +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=Пользовательское соглашение @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=The deposit transaction of the closed error.closedTradeWithNoDepositTx=The deposit transaction of the closed trade with the trade ID {0} is null.\n\nPlease restart the application to clean up the closed trades list. popup.warning.walletNotInitialized=Кошелёк ещё не инициализирован +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=Вероятно, у вас установлена не та версия Bisq.\nАрхитектура Вашего компьютера: {0}.\nУстановленная версия Bisq: {1}.\nЗакройте приложение и установите нужную версию ({2}). popup.warning.incompatibleDB=Обнаружены несовместимые файлы базы данных!\nБазы данных этих файлов несовместимы с нашей текущей базой кода:\n{0}\n\nМы сделали резервную копию повреждённых файлов и применили значения по умолчанию к новой версии базы данных.\n\nРезервная копия находится по адресу:\n{1}/db/backup_of_corrupted_data.\n\nПроверьте, установлена ли у вас новейшая версия Bisq.\nВы можете скачать её по адресу:\nhttps://bisq.network/downloads\n\nПерезапустите приложение. popup.warning.startupFailed.twoInstances=Bisq уже запущен. Нельзя запустить два экземпляра Bisq. @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=В вашем кошельке BSQ не popup.warning.messageTooLong=Ваше сообщение превышает макс. разрешённый размер. Разбейте его на несколько частей или загрузите в веб-приложение для работы с отрывками текста, например https://pastebin.com. popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." -popup.warning.nodeBanned=Один из узлов {0} был заблокирован. Перезапустите приложение, чтобы убедиться, что оно не подключено к заблокированному узлу. +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=ретранслятор курса popup.warning.seed=мнемоническая фраза popup.warning.mandatoryUpdate.trading=Обновите Bisq до последней версии. Вышло обязательное обновление, которое делает невозможной торговлю в старых версиях приложения. Посетите форум Bisq, чтобы узнать подробности. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=Чтобы гарантировать соблю popup.info.cashDepositInfo=Убедитесь, что в вашем районе есть отделение банка, где можно произвести перевод наличных.\nИдентификатор (BIC/SWIFT) банка продавца: {0}. popup.info.cashDepositInfo.confirm=Я подтверждаю, что могу внести оплату popup.info.shutDownWithOpenOffers=Bisq закрывается, но у вас есть открытые предложения.\n\nЭти предложения будут недоступны в сети P2P, пока приложение Bisq закрыто, но будут повторно опубликованы в сети P2P при следующем запуске Bisq.\n\nЧтобы ваши предложения были доступны в сети, компьютер и приложение должны быть включены и подключены к сети (убедитесь, что компьютер не перешёл в режим ожидания; переход монитора в спящий режим не влияет на работу приложения). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=Важное личное уведомление! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=Удостоверьтесь, что комиссия майнера, используемая в вашем внешнем кошельке, составляет не менее {0} сатоши/байт. В противном случае, сделки не будут подтверждены сетью Биткойн, и ваш контрагент начнет спор. +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=Торговые счета в:\n{0} guiUtil.accountExport.noAccountSetup=У вас нет торговых счетов для экспортирования. @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=Платёжный счёт создан {0} назад peerInfoIcon.tooltip.unknownAge=Возраст платёжного счёта неизвестен. tooltip.openPopupForDetails=Открыть окно с подробностями +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=Открыть адрес {0} во внешнем обозревателе блоков tooltip.openBlockchainForTx=Открыть транзакцию {0} во внешнем обозревателе блоков @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=Не удалось открыть Битко peerInfoIcon.tooltip={0}\nМетка: {1} txIdTextField.copyIcon.tooltip=Скопировать идентификатор транзакции в буфер -txIdTextField.blockExplorerIcon.tooltip=Открыть идентификатор этой транзакции в обозревателе блоков +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=Открыть идентификато navigation.account=\«Счёт\» navigation.account.walletSeed=\«Счёт/Мнемоническая фраза\» -navigation.funds.availableForWithdrawal=\«Средства/Отправить средства\» +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\«Сделки/Мои текущие предложения\» navigation.portfolio.pending=\«Сделки/Текущие сделки\» navigation.portfolio.closedTrades=\«Сделки/История\» @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=Ваши кошельки зашифрованы.\ seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=Кошельки успешно восстановлены с новой мнемонической фразой.\n\nЗакройте и перезапустите приложение. seed.restore.error=Произошла ошибка при восстановлении кошельков с помощью мнемонической фразы.{0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=Счёт payment.account.no=Номер счёта payment.account.name=Название счёта payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=Полное имя владельца счёта payment.account.fullName=Полное имя (имя, отчество, фамилия) payment.account.state=Штат/Провинция/Область diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 378c2344b48..ebddfe418ed 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -35,7 +35,7 @@ shared.no=ไม่ใช่ shared.iUnderstand=ฉันเข้าใจ shared.na=ไม่พร้อมใช้งาน shared.shutDown=ปิดใช้งาน -shared.reportBug=รายงานปัญหาข้อบกพร่องได้ที่ GitHub +shared.reportBug=Report bug on GitHub shared.buyBitcoin=ซื้อ bitcoin (บิตคอยน์) shared.sellBitcoin=ขาย bitcoin (บิตคอยน์) shared.buyCurrency=ซื้อ {0} @@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (ต่ำสุด-สูงสุด) shared.removeOffer=ลบข้อเสนอ shared.dontRemoveOffer=ห้ามลบข้อเสนอ shared.editOffer=แก้ไขข้อเสนอ -shared.openLargeQRWindow=เปิดหน้าต่างใหญ่ QR-Code +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=บัญชีการซื้อขาย -shared.faq=คำถามที่พบบ่อย +shared.faq=Visit FAQ page shared.yesCancel=ใช่ ยกเลิก shared.nextStep=ขั้นถัดไป shared.selectTradingAccount=เลือกบัญชีการซื้อขาย shared.fundFromSavingsWalletButton=โอนเงินจาก Bisq wallet shared.fundFromExternalWalletButton=เริ่มทำการระดมเงินทุนหาแหล่งเงินจากกระเป๋าสตางค์ภายนอกของคุณ -shared.openDefaultWalletFailed=พบข้อผิดพลาดในการเปิดแอปพลิเคชั่นด้วยค่าเริ่มต้นกระเป๋าสตางค์ Bitcoin หรืออาจเป็นเพราะคุณอาจจะยังไม่ได้ทำการติดตั้งแอป +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=ระดับราคาในรูปแบบ % จากราคาตลาด shared.belowInPercent=ต่ำกว่า % จากราคาตลาด shared.aboveInPercent=สูงกว่า % จากราคาตาด shared.enterPercentageValue=เข้าสู่ % ตามมูลค่า shared.OR=หรือ -shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet.\nYou need {0} but you have only {1} in your Bisq wallet.\n\nPlease fund the trade from an external Bitcoin wallet or fund your Bisq wallet at \"Funds/Receive funds\". +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=กำลังรอเงิน ... shared.depositTransactionId=รหัสธุรกรรมการฝากเงิน (transaction ID) shared.TheBTCBuyer=ผู้ซื้อ BTC @@ -116,22 +116,22 @@ shared.You=คุณ shared.reasonForPayment=เหตุผลในการชำระเงิน shared.sendingConfirmation=กำลังส่งการยืนยัน ... shared.sendingConfirmationAgain=โปรดยืนยันการส่งอีกครั้ง -shared.exportCSV=ส่งไปที่ csv +shared.exportCSV=Export to CSV shared.exportJSON=Export to JSON shared.noDateAvailable=ไม่มีวันที่ให้แสดง shared.noDetailsAvailable=ไม่มีรายละเอียด shared.notUsedYet=ยังไม่ได้ใช้งาน shared.date=วันที่ shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? -shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=คัดลอกไปที่คลิปบอร์ด shared.language=ภาษา shared.country=ประเทศ shared.applyAndShutDown=ใช้และปิดใช้งาน shared.selectPaymentMethod=เลือกวิธีการชำระเงิน -shared.accountNameAlreadyUsed=ชื่อบัญชีนี้มีผู้ใช้อยู่แล้วในบัญชีที่บันทึกไว้\nโปรดใช้ชื่ออื่น +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=คุณต้องการลบบัญชีที่เลือกหรือไม่ -shared.cannotDeleteAccount=คุณไม่สามารถลบบัญชีนั้นได้เนื่องจากเป็นบัญชีที่ใช้ในการเปิดข้อเสนอหรือในการซื้อขาย +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=บัญชียังไม่ได้มีการตั้งค่าใดๆ shared.manageAccounts=จัดการบัญชี shared.addNewAccount=เพิ่มบัญชีใหม่ @@ -214,7 +214,8 @@ shared.mediator=ผู้ไกล่เกลี่ย shared.arbitrator=ผู้ไกล่เกลี่ย shared.refundAgent=ผู้ไกล่เกลี่ย shared.refundAgentForSupportStaff=Refund agent -shared.delayedPayoutTxId=Refund collateral transaction ID +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=ยอมรับตำแหน่งป offerbook.availableOffers=ข้อเสนอที่พร้อมใช้งาน offerbook.filterByCurrency=กรองตามสกุลเงิน offerbook.filterByPaymentMethod=ตัวกรองตามวิธีการชำระเงิน -offerbook.timeSinceSigning=Time since signing +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts offerbook.timeSinceSigning.info.peer=signed by a peer, waiting for limits to be lifted @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=signed by peer and can sign peer accounts offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} วัน offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=เกิดข้อผิดพลาดใ offerbook.activateOffer.failed=การเผยแพร่ข้อเสนอล้มเหลว: \n{0} offerbook.withdrawFundsHint=คุณสามารถถอนเงินที่คุณชำระมาได้จาก {0} หน้าจอ -offerbook.warning.noTradingAccountForCurrency.headline=ไม่มีบัญชีการซื้อขายสำหรับสกุลเงินที่เลือก -offerbook.warning.noTradingAccountForCurrency.msg=คุณไม่มีบัญชีการค้าสำหรับสกุลเงินที่เลือก\nคุณต้องการสร้างข้อเสนอกับบัญชีซื้อขายที่มีอยู่ของคุณหรือไม่? -offerbook.warning.noMatchingAccount.headline=ไม่มีบัญชีซื้อขายที่ตรงกัน -offerbook.warning.noMatchingAccount.msg=To take this offer, you will need to set up a payment account using this payment method.\n\nWould you like to do this now? +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due to counterparty trade restrictions @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=วิธีการชำระเงิ offerbook.warning.nodeBlocked=ที่อยู่ onion ของผู้ซื้อขายรายนั้นถูกบล็อกโดยนักพัฒนา Bisq\nอาจมีข้อบกพร่องที่ไม่ได้รับการจัดการ ซึ่งก่อให้เกิดปัญหาเมื่อรับข้อเสนอจากผู้ซื้อขายรายนั้น offerbook.warning.requireUpdateToNewVersion=Your version of Bisq is not compatible for trading anymore.\nPlease update to the latest Bisq version at https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=Your payment account has been created {0} ago. Your trade limit is based on the account age and is not sufficient for that offer.\n\nYour trade limit is: {1}\nThe min. trade amount of the offer is: {2}.\n\nYou cannot take that offer at the moment. Once your account is older than 2 months this restriction gets removed. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=คุณจะขายในราคาตลาด (อัปเดตทุกนาที) offerbook.info.buyAtMarketPrice=คุณจะซื้อในราคาตลาด (อัปเดตทุกนาที) @@ -541,7 +543,7 @@ portfolio.tab.history=ประวัติ portfolio.tab.failed=ผิดพลาด portfolio.tab.editOpenOffer=แก้ไขข้อเสนอ -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=รอการยืนยันของบล็อกเชน portfolio.pending.step2_buyer.startPayment=เริ่มการชำระเงิน @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=รอจนกว่าจะ portfolio.pending.step3_seller.confirmPaymentReceived=การยืนยันการชำระเงินที่ได้รับ portfolio.pending.step5.completed=เสร็จสิ้น +portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status +portfolio.pending.autoConf=Auto-confirmed +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key +portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Auto-confirm feature is disabled. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=ธุรกรรมเงินฝากได้รับการเผยแพร่แล้ว\n{0} ต้องรอการยืนยันของบล็อกเชนอย่างน้อยหนึ่งครั้งก่อนที่จะเริ่มการชำระเงิน portfolio.pending.step1.warn=The deposit transaction is still not confirmed. This sometimes happens in rare cases when the funding fee of one trader from an external wallet was too low. portfolio.pending.step1.openForDispute=The deposit transaction is still not confirmed. You can wait longer or contact the mediator for assistance. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=โปรดไปที่หน้าเว # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=กรุณาติดต่อผู้ขายของ BTC ตามรายชื่อที่ได้รับและนัดประชุมเพื่อจ่ายเงิน {0}\n\n portfolio.pending.step2_buyer.startPaymentUsing=เริ่มต้นการชำระเงินโดยใช้ {0} +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=จำนวนเงินที่จะโอน portfolio.pending.step2_buyer.sellersAddress=ที่อยู่ของผู้ขาย {0} portfolio.pending.step2_buyer.buyerAccount=บัญชีการชำระเงินที่ต้องการใข้งาน @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might veri portfolio.pending.step2_buyer.confirmStart.headline=ยืนยันว่าคุณได้เริ่มต้นการชำระเงินแล้ว portfolio.pending.step2_buyer.confirmStart.msg=คุณได้เริ่มต้นการ {0} การชำระเงินให้กับคู่ค้าของคุณแล้วหรือยัง portfolio.pending.step2_buyer.confirmStart.yes=ใช่ฉันได้เริ่มต้นการชำระเงินแล้ว - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=รอการชำระเงิน portfolio.pending.step2_seller.f2fInfo.headline=ข้อมูลการติดต่อของผู้ซื้อ portfolio.pending.step2_seller.waitPayment.msg=ธุรกรรมการฝากเงินมีการยืนยันบล็อกเชนอย่างน้อยหนึ่งรายการ\nคุณต้องรอจนกว่าผู้ซื้อ BTC จะเริ่มการชำระเงิน {0} @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=จำนวนเงินที portfolio.pending.step3_seller.yourAddress=ที่อยู่ {0} ของคุณ portfolio.pending.step3_seller.buyersAddress=ที่อยู่ {0} ผู้ซื้อ portfolio.pending.step3_seller.yourAccount=บัญชีการซื้อขายของคุณ -portfolio.pending.step3_seller.buyersAccount=บัญชีการซื้อขายของผู้ซื้อ +portfolio.pending.step3_seller.xmrTxHash=เลขอ้างอิงการทำธุรกรรม +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=ใบเสร็จยืนยันการชำระเงิน portfolio.pending.step3_seller.buyerStartedPayment=ผู้ซื้อ BTC ได้เริ่มการชำระเงิน {0}\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=ตรวจสอบการยืนยันบล็อกเชนที่ altcoin wallet ของคุณหรือบล็อก explorer และยืนยันการชำระเงินเมื่อคุณมีการยืนยันบล็อกเชนที่เพียงพอ @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Please use this function only in emergen portfolio.pending.timeLockNotOver=You have to wait until ≈{0} ({1} more blocks) before you can open an arbitration dispute. portfolio.pending.error.depositTxNull=The deposit transaction is null. You cannot open a dispute without a valid deposit transaction. Please go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. -portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. The trade gets moved to the failed trades section. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=The deposit transaction is not confirmed. You can not open an arbitration dispute with an unconfirmed deposit transaction. Please wait until it is confirmed or go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. portfolio.pending.notification=การแจ้งเตือน @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=เสร็จสิ้น portfolio.closed.ticketClosed=Arbitrated portfolio.closed.mediationTicketClosed=Mediated @@ -763,8 +817,8 @@ portfolio.closed.canceled=ยกเลิกแล้ว portfolio.failed.Failed=ผิดพลาด portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets support.filter=Search disputes support.filter.prompt=Enter trade ID, date, onion address or account data + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=ส่งการแจ้งเตือนส่วนตัว -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=การแจ้งเตือนส่วนตัว +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=ไม่มีการเปิดรับคำขอร้องหรือความช่วยเหลือ support.sendingMessage=กำลังส่งข้อความ... support.receiverNotOnline=Receiver is not online. Message is saved to their mailbox. @@ -905,6 +970,9 @@ support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nB support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nBisq version: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Mediator''s node address: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=ข้อมูลเครือข่าย settings.tab.about=เกี่ยวกับ setting.preferences.general=การตั้งค่าทั่วไป -setting.preferences.explorer=ตัวค้นหาบล็อก Bitcoin -setting.preferences.explorer.bsq=BSQ block explorer +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=สูงสุด ส่วนเบี่ยงเบนจากราคาตลาด setting.preferences.avoidStandbyMode=หลีกเลี่ยงโหมดแสตนบายด์ +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=ค่าที่สูงกว่า {0}% ไม่ได้รับอนุญาต setting.preferences.txFee=ค่าธรรมเนียมการทำรายการถอนเงิน (satoshis / byte) setting.preferences.useCustomValue=ใช้ค่าที่กำหนดเอง @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool fo setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN -setting.about.shortcuts.showDisputeStatistics=Show summary of all disputes -setting.about.shortcuts.showDisputeStatistics.value=Navigate to disputes view and press: {0} - setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navigate to account and press: setting.about.shortcuts.registerMediator=Register mediator (mediator/arbitrator only) setting.about.shortcuts.registerMediator.value=Navigate to account and press: {0} -setting.about.shortcuts.reOpenDispute=Re-open already closed dispute (mediator/arbitrator only) -setting.about.shortcuts.reOpenDispute.value=Select closed dispute and press: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Open window for account age signing (legacy arbitrators only) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navigate to legacy arbitrator view and press: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Send alert or update message (privileged ac setting.about.shortcuts.sendFilter=Set Filter (privileged activity) setting.about.shortcuts.sendPrivateNotification=Send private notification to peer (privileged activity) -setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar or dispute and press: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements account.altcoin.popup.wallet.confirm=ฉันเข้าใจและยืนยันว่าฉันรู้ว่า wallet ใดที่ฉันต้องการใช้ account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. account.altcoin.popup.arq.msg=Trading ARQ on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending ARQ, you need to use either the official ArQmA GUI wallet or ArQmA CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\narqma-wallet-cli (use the command get_tx_key)\narqma-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The ARQ sender is responsible for providing verification of the ARQ transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit ArQmA discord channel (https://discord.gg/s9BQpJT) or the ArQmA forum (https://labs.arqma.com) to find more information. -account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand and fulfill the following requirements:\n\nProve payments: since Monero is a private coin, some transaction details aren't publicly available in the blockchain, and, in case of a dispute, the mediator or arbitrator needs them to check if the transaction was really made. In Bisq, the sender of the XMR transaction is the one responsible for providing this information to the mediator or arbitrator in case of a dispute. In order to do that, you must send XMR using a wallet that provides the information required to prove the payment was made, which includes:\n\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nThis information can be found in the official Monero GUI & CLI wallets, MyMonero, and Exodus (desktop) as well as in Cake Wallet, MyMonero, and Monerujo (mobile), in the following locations:\n\n- Monero GUI: go to Transactions tab\n- Monero CLI: use the command get_tx_key TXID. The flag store-tx-info must be enabled (enabled by default in new versions)\n- Other wallets: go to Transactions history and search for Transaction key (Tx key or Secret key) and the destination address in a sent transaction. Save recipient address option must be enabled in Cake Wallet settings.\n\nIf you are using a wallet different from the mentioned above, please be sure you can access those three pieces of information.Since the transaction key and the destination address are stored in the Monero wallet software, and they cannot be recovered in the Monero blockchain, you should never delete or restore your Monero wallet before a Bisq trade is completed. Failure to provide the above data will result in losing the dispute case.\n\nCheck payments: with those three pieces of information, the verification that a quantity of Monero was sent to a specific address can be accomplished the following way:\n\n- Monero GUI: change wallet to Advanced mode and go to Advanced > Prove/check > Check Transaction\n- Monero CLI: use the command check_tx_key TXID TXKEY ADDRESS\n- XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Explore Monero website (https://www.exploremonero.com/receipt)\n\nIf you are still not sure about this process, visit (https://www.getmonero.org/resources/user-guides/prove-payment.html) to find more information or ask a question on the Monero support subreddit (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Trading MSR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending MSR, you need to use either the official Masari GUI wallet, Masari CLI wallet with the store-tx-info flag enabled (enabled by default) or the Masari web wallet (https://wallet.getmasari.org). Please be sure you can access the tx key as that would be required in case of a dispute.\nmasari-wallet-cli (use the command get_tx_key)\nmasari-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nMasari Web Wallet (goto Account -> transaction history and view details on your sent transaction)\n\nVerification can be accomplished in-wallet.\nmasari-wallet-cli : using command (check_tx_key).\nmasari-wallet-gui : on the Advanced > Prove/Check page.\nVerification can be accomplished in the block explorer \nOpen block explorer (https://explorer.getmasari.org), use the search bar to find your transaction hash.\nOnce transaction is found, scroll to bottom to the 'Prove Sending' area and fill in details as needed.\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The MSR sender is responsible for providing verification of the MSR transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process, ask for help on the Official Masari Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=ผู้ดำเนินก # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets API operator +dao.bond.bondedRoleType.MARKETS_OPERATOR=ผู้ดำเนินการด้านตลาด # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=ผู้ดำเนินการส่วนสำรวจ BSQ +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=สรุปบันทึกย่อ disputeSummaryWindow.addSummaryNotes=เพิ่มสรุปบันทึกย่อ: disputeSummaryWindow.close.button=ปิดการยื่นคำขอและความช่วยเหลือ -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nNext steps:\nOpen trade and accept or reject suggestion from mediator -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=คุณจำเป็นต้องยุติคำขอความช่วยเหลือคู่ค้าด้วย ! disputeSummaryWindow.close.txDetails.headline=Publish refund transaction disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to publish this transaction? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} กระเป๋าสตางค์ฉุกเฉิน emptyWalletWindow.info=โปรดใช้ในกรณีฉุกเฉินเท่านั้นหากคุณไม่สามารถเข้าถึงเงินจาก UI ได้\n\nโปรดทราบว่าข้อเสนอแบบเปิดทั้งหมดจะถูกปิดโดยอัตโนมัติเมื่อใช้เครื่องมือนี้\n\nก่อนที่คุณจะใช้เครื่องมือนี้โปรดสำรองข้อมูลในสารบบข้อมูลของคุณ คุณสามารถดำเนินการได้ที่ \"บัญชี / การสำรองข้อมูล \" \n\nโปรดรายงานปัญหาของคุณและส่งรายงานข้อบกพร่องเกี่ยวกับ GitHub หรือที่ฟอรัม Bisq เพื่อให้เราสามารถตรวจสอบสิ่งที่เป็นสาเหตุของปัญหาได้ emptyWalletWindow.balance=ยอดในกระเป๋าสตางค์ที่คงเหลือที่มีอยู่ @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=โหนดผลัดเปลี่ยนรา filterWindow.btcNode=โหนด Bitcoin ที่ได้รับการกรองแล้ว (คั่นด้วยเครื่องหมายจุลภาค ที่อยู่ + พอร์ต) filterWindow.preventPublicBtcNetwork=ป้องกันการใช้เครือข่าย Bitcoin สาธารณะ filterWindow.disableDao=Disable DAO +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Min. version required for DAO filterWindow.disableTradeBelowVersion=Min. version required for trading filterWindow.add=เพิ่มตัวกรอง @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=ยืนยัน: รับข้อเสน offerDetailsWindow.creationDate=วันที่สร้าง offerDetailsWindow.makersOnion=ที่อยู่ onion ของผู้สร้าง -qRCodeWindow.headline=QR-Code (คิวอาร์โค้ด) -qRCodeWindow.msg=โปรดใช้ QR-Code (คิวอาร์โค้ด) เพื่อเติมเงินกระเป๋าสตางค์ Bisq จากกระเป๋าสตางค์ ภายนอกของคุณ +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=คำขอชำระเงิน: \n{0} selectDepositTxWindow.headline=เลือกรายการเงินฝากสำหรับกรณีพิพาท @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=ส่งการแจ้งเตือน showWalletDataWindow.walletData=ข้อมูล Wallet  showWalletDataWindow.includePrivKeys=รวมคีย์ส่วนตัว +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=ข้อตกลงการใช้ @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=The deposit transaction of the closed error.closedTradeWithNoDepositTx=The deposit transaction of the closed trade with the trade ID {0} is null.\n\nPlease restart the application to clean up the closed trades list. popup.warning.walletNotInitialized=wallet ยังไม่ได้เริ่มต้น +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=คุณอาจมีเวอร์ชั่น Bisq ไม่เหมาะสำหรับคอมพิวเตอร์นี้\nสถาปัตยกรรมคอมพิวเตอร์ของคุณคือ: {0} .\nเลขฐานสอง Bisq ที่คุณติดตั้งคือ: {1} .\nโปรดปิดตัวลงและติดตั้งรุ่นที่ถูกต้องอีกครั้ง ({2}) popup.warning.incompatibleDB=เราตรวจพบไฟล์ฐานข้อมูลที่ไม่เข้ากัน! \n\nไฟล์ฐานข้อมูลเหล่านี้ไม่สามารถใช้งานได้กับฐานข้อมูลปัจจุบันของเรา:\n{0} \n\nเราได้สำรองข้อมูลไฟล์ที่เสียหายแล้วใช้ค่าเริ่มต้นกับเวอร์ชั่นฐานข้อมูลใหม่\n\nการสำรองข้อมูลอยู่ที่: \n{1} /db/backup_of_corrupted_data.\n\nโปรดตรวจสอบว่าคุณมี Bisq เวอร์ชั่นล่าสุดหรือไม่\nคุณสามารถดาวน์โหลดได้ที่: \nhttps://bisq.network/downloads\n\nโปรดรีสตาร์ทแอ็พพลิเคชั่น popup.warning.startupFailed.twoInstances=Bisq กำลังทำงานอยู่ คุณไม่สามารถเรียกใช้ Bisq พร้อมกันได้ @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=กระเป๋าสตางค์ popup.warning.messageTooLong=ข้อความของคุณเกินขีดจำกัดสูงสุดที่อนุญาต โปรดแบ่งส่งเป็นหลายส่วนหรืออัปโหลดไปยังบริการเช่น https://pastebin.com popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." -popup.warning.nodeBanned=หนึ่งใน {0} โหนดถูกแบน โปรดรีสตาร์ทแอ็พพลิเคชั่นเพื่อให้แน่ใจว่าไม่ได้เชื่อมต่อกับโหนดที่ถูกแบน +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=ราคาผลัดเปลี่ยน popup.warning.seed=รหัสลับเพื่อกู้ข้อมูล popup.warning.mandatoryUpdate.trading=Please update to the latest Bisq version. A mandatory update was released which disables trading for old versions. Please check out the Bisq Forum for more information. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=เพื่อให้แน่ใจว่า popup.info.cashDepositInfo=โปรดตรวจสอบว่าคุณมีสาขาธนาคารในพื้นที่ของคุณเพื่อสามารถฝากเงินได้\nรหัสธนาคาร (BIC / SWIFT) ของธนาคารผู้ขายคือ: {0} popup.info.cashDepositInfo.confirm=ฉันยืนยันว่าฉันสามารถฝากเงินได้ popup.info.shutDownWithOpenOffers=Bisq คือกำลังจะปิดลง แต่ยังคงมีการเปิดขายข้อเสนอปกติ\nข้อเสนอเหล่านี้จะไม่ใข้งานได้บนเครือข่าย P2P network ในขณะที่ Bisq ปิดตัวลง แต่จะมีการเผยแพร่บนเครือข่าย P2P ครั้งถัดไปเมื่อคุณมีการเริ่มใช้งาน Bisq.\n\nในการคงสถานะข้อเสนอแบบออนไลน์ คือเปิดใข้งาน Bisq และทำให้มั่นใจว่าคอมพิวเตอร์เครื่องนี้กำลังออนไลน์อยู่ด้วยเช่นกัน (เช่น ตรวจสอบว่าคอมพิวเตอร์ไม่ได้อยู่ในโหมดแสตนบายด์...หน้าจอแสตนบายด์ไม่มีปัญหา) +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=การแจ้งเตือนส่วนตัวที่สำคัญ! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=โปรดตรวจสอบว่าค่าธรรมเนียมการขุดที่ใช้ในกระเป๋าสตางค์ภายนอกของคุณอย่างน้อย {0} satoshis / byte มิฉะนั้นธุรกรรมการซื้อจะไม่ได้รับการยืนยันและการค้าจะสิ้นสุดลงในข้อพิพาท +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=บัญชีการค้าที่บันทึกไว้ในเส้นทาง: \n{0} guiUtil.accountExport.noAccountSetup=คุณไม่มีบัญชีการซื้อขายที่ตั้งค่าไว้สำหรับการส่งออก @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=บัญชีการชำระเงินที peerInfoIcon.tooltip.unknownAge=อายุบัญชีการชำระเงินที่ไม่รู้จัก tooltip.openPopupForDetails=เปิดป๊อปอัปเพื่ออ่านรายละเอียด +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=เปิดตัวสำรวจ blockchain ภายนอกตามที่อยู่: {0} tooltip.openBlockchainForTx=เปิดตัวสำรวจ blockchain ภายนอกสำหรับธุรกรรม: {0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=การเปิดแอปพลิเค peerInfoIcon.tooltip={0} \nแท็ก: {1} txIdTextField.copyIcon.tooltip=คัดลอก ID ธุรกรรมไปยังคลิปบอร์ด -txIdTextField.blockExplorerIcon.tooltip=เปิดตัวสำรวจ blockchain ด้วย ID ธุรกรรมดังกล่าว +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=เปิดตัวสำรวจ blo navigation.account=\"บัญชี\" navigation.account.walletSeed=\ "บัญชี / รหัสลับป้องกันกระเป๋าสตางค์\" -navigation.funds.availableForWithdrawal=\"เงิน / ส่งเงิน \" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"แฟ้มผลงาน / ข้อเสนอของฉัน \" navigation.portfolio.pending=\"แฟ้มผลงาน / เปิดการซื้อขาย \" navigation.portfolio.closedTrades=\"แฟ้มผลงาน/ประวัติ\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=กระเป๋าสตางค์ของ seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=กระเป๋าสตางค์ได้รับการกู้คืนข้อมูลด้วยรหัสลับเพื่อป้องกันและกู้คืนกระเป๋าสตางค์ด้วยรหัสลับใหม่แล้ว\n\nคุณจำเป็นต้องปิดและรีสตาร์ทแอ็พพลิเคชั่น seed.restore.error=เกิดข้อผิดพลาดขณะกู้คืนกระเป๋าสตางค์ด้วยรหัสลับ {0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=บัญชี payment.account.no=หมายเลขบัญชี payment.account.name=ชื่อบัญชี payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=ชื่อเต็มของเจ้าของบัญชี payment.account.fullName=ชื่อเต็ม (ชื่อจริง, ชื่อกลาง, นามสกุล) payment.account.state=รัฐ / จังหวัด / ภูมิภาค diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 5bf014ceaa4..0f2205fca39 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -35,7 +35,7 @@ shared.no=Không shared.iUnderstand=Tôi hiểu shared.na=Không áp dụng shared.shutDown=Đóng -shared.reportBug=Báo cáo lỗi lên GitHub +shared.reportBug=Report bug on GitHub shared.buyBitcoin=Mua bitcoin shared.sellBitcoin=Bán bitcoin shared.buyCurrency=Mua {0} @@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (min - max) shared.removeOffer=Bỏ chào giá shared.dontRemoveOffer=Không được bỏ chào giá shared.editOffer=Chỉnh sửa chào giá -shared.openLargeQRWindow=Mở cửa sổ lớn Mã QR +shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=Tài khoản giao dịch -shared.faq=Đi đến trang web FAQ +shared.faq=Visit FAQ page shared.yesCancel=Có, hủy shared.nextStep=Bước tiếp theo shared.selectTradingAccount=Chọn tài khoản giao dịch shared.fundFromSavingsWalletButton=Chuyển tiền từ Ví Bisq shared.fundFromExternalWalletButton=Mở ví ngoài để nộp tiền -shared.openDefaultWalletFailed=Mở ứng dụng ví Bitcoin mặc định không thành. Có thể bạn chưa cài đặt? +shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed? shared.distanceInPercent=Chênh lệch % so với giá thị trường shared.belowInPercent=Thấp hơn % so với giá thị trường shared.aboveInPercent=Cao hơn % so với giá thị trường shared.enterPercentageValue=Nhập giá trị % shared.OR=HOẶC -shared.notEnoughFunds=Bạn không đủ tiền trong ví Bisq.\nBạn cần {0} nhưng bạn chỉ có {1} trong ví Bisq.\n\nHãy nộp tiền cho giao dịch này từ ví Bitcoin khác của bạn Hoặc nộp vào ví Bisq của bạn tại \"Số Tiền/Nạp Tiền\". +shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds. shared.waitingForFunds=Đợi nộp tiền... shared.depositTransactionId=ID giao dịch gửi tiền shared.TheBTCBuyer=Người mua BTC @@ -116,22 +116,22 @@ shared.You=Bạn shared.reasonForPayment=Lý do thanh toán shared.sendingConfirmation=Gửi xác nhận... shared.sendingConfirmationAgain=Hãy gửi lại xác nhận -shared.exportCSV=Truy xuất ra csv +shared.exportCSV=Export to CSV shared.exportJSON=Truy xuất ra JSON shared.noDateAvailable=Ngày tháng không hiển thị shared.noDetailsAvailable=Không có thông tin shared.notUsedYet=Chưa được sử dụng shared.date=Ngày shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? -shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n +shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.copyToClipboard=Sao chép đến clipboard shared.language=Ngôn ngữ shared.country=Quốc gia shared.applyAndShutDown=Áp dụng và tắt shared.selectPaymentMethod=Chọn phương thức thanh toán -shared.accountNameAlreadyUsed=Tên tài khoản đã được sử dụng.\nHãy sử dụng tên tài khoản khác. +shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name. shared.askConfirmDeleteAccount=Bạn có thực sự muốn xóa tài khoản được chọn không? -shared.cannotDeleteAccount=Bạn không thể xóa tài khoản này vì đang được dùng ở một chào giá đang hiệu lực hoặc một giao dịch khác. +shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade). shared.noAccountsSetupYet=Chưa có tài khoản nào được thiết lập shared.manageAccounts=Quản lý tài khoản shared.addNewAccount=Thêm tài khoản mới @@ -214,7 +214,8 @@ shared.mediator=Người hòa giải shared.arbitrator=Trọng tài shared.refundAgent=Trọng tài shared.refundAgentForSupportStaff=Refund agent -shared.delayedPayoutTxId=Refund collateral transaction ID +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=Các quốc gia có ngân hàng được ch offerbook.availableOffers=Các chào giá hiện có offerbook.filterByCurrency=Lọc theo tiền tệ offerbook.filterByPaymentMethod=Lọc theo phương thức thanh toán -offerbook.timeSinceSigning=Time since signing +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts offerbook.timeSinceSigning.info.peer=signed by a peer, waiting for limits to be lifted @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=signed by peer and can sign peer accounts offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} ngày offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=Huỷ kích hoạt chào giá không thành cô offerbook.activateOffer.failed=Công bố lệnh không thành công:\n{0} offerbook.withdrawFundsHint=Bạn có thể rút bạn đã thanh toán từ màn hình {0}. -offerbook.warning.noTradingAccountForCurrency.headline=Không có tài khoản giao dịch với đồng tiền được chọn -offerbook.warning.noTradingAccountForCurrency.msg=Bạn không có tài khoản giao dịch với đồng tiền được chọn.\nBạn có muốn tạo lệnh với một trong các tài khoản giao dịch hiện có của bạn? -offerbook.warning.noMatchingAccount.headline=Không có tài khoản giao dịch phù hợp. -offerbook.warning.noMatchingAccount.msg=To take this offer, you will need to set up a payment account using this payment method.\n\nWould you like to do this now? +offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency +offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? +offerbook.warning.noMatchingAccount.headline=No matching payment account. +offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due to counterparty trade restrictions @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=Phương thức thanh toán sử dụng tr offerbook.warning.nodeBlocked=Địa chỉ onion của Thương gia bị chặn bởi các lập trình viên Bisq.\nCó thể có sự cố chưa được xử lý dẫn tới vấn đề khi nhận báo giá từ Thương gia này. offerbook.warning.requireUpdateToNewVersion=Phiên bản Bisq bạn đang sử dụng không còn hỗ trợ giao dịch nữa.\nVui lòng cập nhận phiên bản Bisq mới nhất tại https://bisq.network/downloads. offerbook.warning.tradeLimitNotMatching=Your payment account has been created {0} ago. Your trade limit is based on the account age and is not sufficient for that offer.\n\nYour trade limit is: {1}\nThe min. trade amount of the offer is: {2}.\n\nYou cannot take that offer at the moment. Once your account is older than 2 months this restriction gets removed. - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=Bạn sẽ bán với giá thị trường (cập nhật mỗi phút). offerbook.info.buyAtMarketPrice=Bạn sẽ mua với giá thị trường (cập nhật mỗi phút). @@ -541,7 +543,7 @@ portfolio.tab.history=Lịch sử portfolio.tab.failed=Không thành công portfolio.tab.editOpenOffer=Chỉnh sửa báo giá -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=Đợi xác nhận blockchain portfolio.pending.step2_buyer.startPayment=Bắt đầu thanh toán @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=Đợi đến khi khoản thanh portfolio.pending.step3_seller.confirmPaymentReceived=Xác nhận đã nhận được thanh toán portfolio.pending.step5.completed=Hoàn thành +portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status +portfolio.pending.autoConf=Auto-confirmed +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} +portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} +portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key +portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=Auto-confirm feature is disabled. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=Trade amount exceeds auto-confirm amount limit +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=An error at a service request occurred. No auto-confirm possible. +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=A service returned with a failure. No auto-confirm possible. + portfolio.pending.step1.info=Giao dịch đặt cọc đã được công bố.\n{0} Bạn cần đợi ít nhất một xác nhận blockchain trước khi bắt đầu thanh toán. portfolio.pending.step1.warn=The deposit transaction is still not confirmed. This sometimes happens in rare cases when the funding fee of one trader from an external wallet was too low. portfolio.pending.step1.openForDispute=The deposit transaction is still not confirmed. You can wait longer or contact the mediator for assistance. @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=Hãy truy cập trang web ngân hàng và tha # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Vui lòng liên hệ người bán BTC và cung cấp số liên hệ và sắp xếp cuộc hẹn để thanh toán {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Thanh toán bắt đầu sử dụng {0} +portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=Số tiền chuyển portfolio.pending.step2_buyer.sellersAddress=Địa chỉ của người bán {0} portfolio.pending.step2_buyer.buyerAccount=Tài khoản thanh toán sẽ sử dụng @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might veri portfolio.pending.step2_buyer.confirmStart.headline=Xác nhận rằng bạn đã bắt đầu thanh toán portfolio.pending.step2_buyer.confirmStart.msg=Bạn đã kích hoạt thanh toán {0} cho Đối tác giao dịch của bạn chưa? portfolio.pending.step2_buyer.confirmStart.yes=Có, tôi đã bắt đầu thanh toán - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value +portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway portfolio.pending.step2_seller.waitPayment.headline=Đợi thanh toán portfolio.pending.step2_seller.f2fInfo.headline=Thông tin liên lạc của người mua portfolio.pending.step2_seller.waitPayment.msg=Giao dịch đặt cọc có ít nhất một xác nhận blockchain.\nBạn cần phải đợi cho đến khi người mua BTC bắt đầu thanh toán {0}. @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=Số tiền nhận được portfolio.pending.step3_seller.yourAddress=Địa chỉ {0} của bạn portfolio.pending.step3_seller.buyersAddress=Địa chỉ {0} của người mua portfolio.pending.step3_seller.yourAccount=tài khoản giao dịch của bạn -portfolio.pending.step3_seller.buyersAccount=tài khoản giao dịch của người mua +portfolio.pending.step3_seller.xmrTxHash=ID giao dịch +portfolio.pending.step3_seller.xmrTxKey=Transaction key +portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=Xác nhận đã nhận được thanh toán portfolio.pending.step3_seller.buyerStartedPayment=Người mua BTC đã bắt đầu thanh toán {0}.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Kiểm tra xác nhận blockchain ở ví altcoin của bạn hoặc block explorer và xác nhận thanh toán nếu bạn nhận được đủ xác nhận blockchain. @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=Please use this function only in emergen portfolio.pending.timeLockNotOver=You have to wait until ≈{0} ({1} more blocks) before you can open an arbitration dispute. portfolio.pending.error.depositTxNull=The deposit transaction is null. You cannot open a dispute without a valid deposit transaction. Please go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. -portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. The trade gets moved to the failed trades section. -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=The deposit transaction is not confirmed. You can not open an arbitration dispute with an unconfirmed deposit transaction. Please wait until it is confirmed or go to \"Settings/Network info\" and do a SPV resync.\n\nFor further help please contact the Bisq support channel at the Bisq Keybase team. portfolio.pending.notification=Thông báo @@ -753,9 +791,25 @@ portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model:\nhttps://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=Hoàn thành portfolio.closed.ticketClosed=Arbitrated portfolio.closed.mediationTicketClosed=Mediated @@ -763,8 +817,8 @@ portfolio.closed.canceled=Đã hủy portfolio.failed.Failed=Không thành công portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -857,11 +911,22 @@ support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets support.filter=Search disputes support.filter.prompt=Nhập ID giao dịch, ngày tháng, địa chỉ onion hoặc dữ liệu tài khoản + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=Gửi thông báo riêng tư -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=Thông báo riêng tư +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=Không có đơn hỗ trợ được mở support.sendingMessage=Đang gửi tin nhắn... support.receiverNotOnline=Receiver is not online. Message is saved to their mailbox. @@ -905,6 +970,9 @@ support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nB support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nBisq version: {1} support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} support.mediatorsAddress=Mediator''s node address: {0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -915,10 +983,15 @@ settings.tab.network=Thông tin mạng settings.tab.about=Về setting.preferences.general=Tham khảo chung -setting.preferences.explorer=Trình duyệt Bitcoin block explorer -setting.preferences.explorer.bsq=BSQ block explorer +setting.preferences.explorer=Bitcoin Explorer +setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Sai lệch tối đa so với giá thị trường setting.preferences.avoidStandbyMode=Tránh để chế độ chờ +setting.preferences.autoConfirmXMR=XMR auto-confirm +setting.preferences.autoConfirmEnabled=Enabled +setting.preferences.autoConfirmRequiredConfirmations=Required confirmations +setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames) setting.preferences.deviationToLarge=Giá trị không được phép lớn hơn {0}%. setting.preferences.txFee=phí giao dịch rút (satoshis/byte) setting.preferences.useCustomValue=Sử dụng giá trị thông dụng @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool fo setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN -setting.about.shortcuts.showDisputeStatistics=Show summary of all disputes -setting.about.shortcuts.showDisputeStatistics.value=Navigate to disputes view and press: {0} - setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=Navigate to account and press: setting.about.shortcuts.registerMediator=Register mediator (mediator/arbitrator only) setting.about.shortcuts.registerMediator.value=Navigate to account and press: {0} -setting.about.shortcuts.reOpenDispute=Re-open already closed dispute (mediator/arbitrator only) -setting.about.shortcuts.reOpenDispute.value=Select closed dispute and press: {0} - setting.about.shortcuts.openSignPaymentAccountsWindow=Open window for account age signing (legacy arbitrators only) setting.about.shortcuts.openSignPaymentAccountsWindow.value=Navigate to legacy arbitrator view and press: {0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=Send alert or update message (privileged ac setting.about.shortcuts.sendFilter=Set Filter (privileged activity) setting.about.shortcuts.sendPrivateNotification=Send private notification to peer (privileged activity) -setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar or dispute and press: {0} - +setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0} +setting.info.headline=New XMR auto-confirm Feature +setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki: https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements account.altcoin.popup.wallet.confirm=Tôi hiểu và xác nhận rằng tôi đã biết loại ví mình cần sử dụng. account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. account.altcoin.popup.arq.msg=Trading ARQ on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending ARQ, you need to use either the official ArQmA GUI wallet or ArQmA CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\narqma-wallet-cli (use the command get_tx_key)\narqma-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The ARQ sender is responsible for providing verification of the ARQ transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit ArQmA discord channel (https://discord.gg/s9BQpJT) or the ArQmA forum (https://labs.arqma.com) to find more information. -account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand and fulfill the following requirements:\n\nProve payments: since Monero is a private coin, some transaction details aren't publicly available in the blockchain, and, in case of a dispute, the mediator or arbitrator needs them to check if the transaction was really made. In Bisq, the sender of the XMR transaction is the one responsible for providing this information to the mediator or arbitrator in case of a dispute. In order to do that, you must send XMR using a wallet that provides the information required to prove the payment was made, which includes:\n\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nThis information can be found in the official Monero GUI & CLI wallets, MyMonero, and Exodus (desktop) as well as in Cake Wallet, MyMonero, and Monerujo (mobile), in the following locations:\n\n- Monero GUI: go to Transactions tab\n- Monero CLI: use the command get_tx_key TXID. The flag store-tx-info must be enabled (enabled by default in new versions)\n- Other wallets: go to Transactions history and search for Transaction key (Tx key or Secret key) and the destination address in a sent transaction. Save recipient address option must be enabled in Cake Wallet settings.\n\nIf you are using a wallet different from the mentioned above, please be sure you can access those three pieces of information.Since the transaction key and the destination address are stored in the Monero wallet software, and they cannot be recovered in the Monero blockchain, you should never delete or restore your Monero wallet before a Bisq trade is completed. Failure to provide the above data will result in losing the dispute case.\n\nCheck payments: with those three pieces of information, the verification that a quantity of Monero was sent to a specific address can be accomplished the following way:\n\n- Monero GUI: change wallet to Advanced mode and go to Advanced > Prove/check > Check Transaction\n- Monero CLI: use the command check_tx_key TXID TXKEY ADDRESS\n- XMR checktx tool (https://xmr.llcoins.net/checktx.html)\n- Explore Monero website (https://www.exploremonero.com/receipt)\n\nIf you are still not sure about this process, visit (https://www.getmonero.org/resources/user-guides/prove-payment.html) to find more information or ask a question on the Monero support subreddit (https://www.reddit.com/r/monerosupport/). +account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=Trading MSR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending MSR, you need to use either the official Masari GUI wallet, Masari CLI wallet with the store-tx-info flag enabled (enabled by default) or the Masari web wallet (https://wallet.getmasari.org). Please be sure you can access the tx key as that would be required in case of a dispute.\nmasari-wallet-cli (use the command get_tx_key)\nmasari-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nMasari Web Wallet (goto Account -> transaction history and view details on your sent transaction)\n\nVerification can be accomplished in-wallet.\nmasari-wallet-cli : using command (check_tx_key).\nmasari-wallet-gui : on the Advanced > Prove/Check page.\nVerification can be accomplished in the block explorer \nOpen block explorer (https://explorer.getmasari.org), use the search bar to find your transaction hash.\nOnce transaction is found, scroll to bottom to the 'Prove Sending' area and fill in details as needed.\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The MSR sender is responsible for providing verification of the MSR transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process, ask for help on the Official Masari Discord (https://discord.gg/sMCwMqs). account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW). @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Điều hành giá node # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets API operator +dao.bond.bondedRoleType.MARKETS_OPERATOR=Điều hành thị trường # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Điều hành QSQ explorer +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Điều hành rơ-le thông báo điện thoại # suppress inspection "UnusedProperty" @@ -1985,15 +2053,24 @@ disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled disputeSummaryWindow.summaryNotes=Lưu ý tóm tắt disputeSummaryWindow.addSummaryNotes=Thêm lưu ý tóm tắt disputeSummaryWindow.close.button=Đóng đơn -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\nNext steps:\nOpen trade and accept or reject suggestion from mediator -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=Bạn cũng cần phải đóng Đơn Đối tác giao dịch! disputeSummaryWindow.close.txDetails.headline=Publish refund transaction disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to publish this transaction? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline=Công cụ ví khẩn cấp emptyWalletWindow.info=Vui lòng chỉ sử dụng trong trường hợp khẩn cấp nếu bạn không thể truy cập vốn của bạn từ UI.\n\nLưu ý rằng tất cả Báo giá mở sẽ được tự động đóng khi sử dụng công cụ này.\n\nTrước khi sử dụng công cụ này, vui lòng sao lưu dự phòng thư mục dữ liệu của bạn. Bạn có thể sao lưu tại \"Tài khoản/Sao lưu dự phòng\".\n\nVui lòng báo với chúng tôi vấn đề của bạn và lập báo cáo sự cố trên GitHub hoặc diễn đàn Bisq để chúng tôi có thể điều tra điều gì gây nên vấn đề đó. emptyWalletWindow.balance=Số dư ví hiện tại của bạn @@ -2023,6 +2100,7 @@ filterWindow.priceRelayNode=nút rơle giá đã lọc (địa chỉ onion cách filterWindow.btcNode=nút Bitcoin đã lọc (địa chỉ cách nhau bằng dấu phẩy + cửa) filterWindow.preventPublicBtcNetwork=Ngăn sử dụng mạng Bitcoin công cộng filterWindow.disableDao=Tắt DAO +filterWindow.disableAutoConf=Disable auto-confirm filterWindow.disableDaoBelowVersion=Phiên bản tối thiểu yêu cầu cho DAO filterWindow.disableTradeBelowVersion=Phiên bản tối thiể yêu cầu cho giao dịch filterWindow.add=Thêm bộ lọc @@ -2045,8 +2123,8 @@ offerDetailsWindow.confirm.taker=Xác nhận: Nhận chào giáo cho {0} bitcoin offerDetailsWindow.creationDate=Ngày tạo offerDetailsWindow.makersOnion=Địa chỉ onion của người tạo -qRCodeWindow.headline=Mã QR -qRCodeWindow.msg=Vui lòng sử dụng mã QR để nộp tiền và ví Bisq của bạn từ ví bên ngoài. +qRCodeWindow.headline=QR Code +qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet. qRCodeWindow.request=Yêu cầu thanh toán:\n{0} selectDepositTxWindow.headline=Chọn giao dịch tiền gửi để khiếu nại @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=Gửi thông báo riêng tư showWalletDataWindow.walletData=Dữ liệu ví showWalletDataWindow.includePrivKeys=Bao gồm khóa cá nhân +setXMRTxKeyWindow.headline=Prove sending of XMR +setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=Transaction ID (optional) +setXMRTxKeyWindow.txKey=Transaction key (optional) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=Thỏa thuận người dùng @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=The deposit transaction of the closed error.closedTradeWithNoDepositTx=The deposit transaction of the closed trade with the trade ID {0} is null.\n\nPlease restart the application to clean up the closed trades list. popup.warning.walletNotInitialized=Ví chưa được kích hoạt +popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved. popup.warning.wrongVersion=Có thể máy tính của bạn có phiên bản Bisq không đúng.\nCấu trúc máy tính của bạn là: {0}.\nHệ nhị phân Bisq bạn cài đặt là: {1}.\nVui lòng tắt máy và cài đặt lại phiên bản đúng ({2}). popup.warning.incompatibleDB=Chúng tôi phát hiện ra file dữ liệu không tương thích!\n\nCác file dữ liệu này không tương thích với mã hiện tại:\n{0}\n\nChúng tôi đã sao lưu dự phòng các file bị hỏng và sử dụng giá trị mặc định cho phiên bản dữ liệu mới.\n\nSao lưu dự phòng tại:\n{1}/db/backup_of_corrupted_data.\n\nVui lòng kiểm tra xem bạn đã cài đặt phiên bản mới nhất của Bisq chưa.\nBạn có thể tải về tại:\nhttps://bisq.network/downloads\n\nVui lòng khởi động lại ứng dụng. popup.warning.startupFailed.twoInstances=Bisq đã chạy. Bạn không thể chạy hai chương trình Bisq. @@ -2174,7 +2258,7 @@ popup.warning.noBsqFundsForBtcFeePayment=Ví BSQ của bạn không đủ tiền popup.warning.messageTooLong=Tin nhắn của bạn vượt quá kích cỡ tối đa cho phép. Vui lòng gửi thành nhiều lần hoặc tải lên mạng như https://pastebin.com. popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." -popup.warning.nodeBanned=Một trong {0} Node đã bị chấm. Vui lòng khởi động lại ứng dụng để chắc chắn không kết nối với các Node bị cấm. +popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=rơle giá popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Please update to the latest Bisq version. A mandatory update was released which disables trading for old versions. Please check out the Bisq Forum for more information. @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=Để đảm bảo cả hai người giao dịch popup.info.cashDepositInfo=Chắc chắn rằng khu vực của bạn có chi nhánh ngân hàng có thể gửi tiền mặt.\nID (BIC/SWIFT) ngân hàng của bên bán là: {0}. popup.info.cashDepositInfo.confirm=Tôi xác nhận tôi đã gửi tiền popup.info.shutDownWithOpenOffers=Bisq đang đóng, nhưng vẫn có các chào giá đang mở. \n\nNhững chào giá này sẽ không có tại mạng P2P khi Bisq đang đóng, nhưng chúng sẽ được công bố lại trên mạng P2P vào lần tiếp theo bạn khởi động Bisq.\nĐể giữ các chào giá luôn trực tuyến, vui lòng để Bisq chạy và đảm bảo là máy tính của bạn cũng đang trực tuyến(có nghĩa là đảm bảo là máy tính của bạn không chuyển về chế độ chờ...nếu màn hình về chế độ chờ thì không sao). +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=Thông báo riêng tư quan trọng! @@ -2297,7 +2382,7 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=Hãy chắc chắn phí đào sử dụng tại ví ngoài của bán ít nhất là {0} satoshis/byte. Nếu không các giao dịch giao dịch không thể được xác nhận và giao dịch sẽ kết thúc bằng khiếu nại.\n\nKhông sử dụng tính năng RBF (thay thế bằng phí) cho giao dịch nộp tiền. Ví Bisq không chấp nhận giao dịch BRF chưa xác nhận vì có rủi ro (rủi ro chi trả hai lần). Nếu bạn sử dụng RBF cần phải đợi 1 xác nhận trước khi ví Bisq công nhận giao dịch. +guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. guiUtil.accountExport.savedToPath=tài khoản giao dịch được lưu vào đường dẫn:\n{0} guiUtil.accountExport.noAccountSetup=Bạn không có tài khoản giao dịch được thiết lập để truy xuất. @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=Tài khoản thanh toán được tạo cách đây {0} peerInfoIcon.tooltip.unknownAge=Tuổi tài khoản thanh toán chưa biết. tooltip.openPopupForDetails=Mở cửa sổ để xem chi tiết +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=Mở blockchain explorer ngoài để xem địa chỉ: {0} tooltip.openBlockchainForTx=Mở blockchain explorer ngoài để xem giao dịch: {0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=Mở ứng dụng ví Bitcoin mặc định k peerInfoIcon.tooltip={0}\nTag: {1} txIdTextField.copyIcon.tooltip=Copy ID giao dịch vào clipboard -txIdTextField.blockExplorerIcon.tooltip=Mở một blockchain explorer với ID giao dịch này +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=Mở một blockchain explorer với ID navigation.account=\"Tài khoản\" navigation.account.walletSeed=\"Tài khoản/Khởi tạo ví\" -navigation.funds.availableForWithdrawal=\"Vốn/Gửi vốn\" +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=\"Portfolio/Các Báo giá mở của tôi\" navigation.portfolio.pending=\"Portfolio/Các giao dịch mở\" navigation.portfolio.closedTrades=\"Portfolio/Lịch sử\" @@ -2457,6 +2544,7 @@ seed.warn.notEncryptedAnymore=Ví của bạn đã được mã hóa.\n\nSau khi seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? seed.restore.success=Ví khôi phục thành công với từ khởi tạo mới.\n\nBạn cần phải tắt và khởi động lại ứng dụng. seed.restore.error=Có lỗi xảy ra khi khôi phục ví với Seed words.{0} +seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue? #################################################################### @@ -2467,6 +2555,7 @@ payment.account=Tài khoản payment.account.no=Tài khoản số payment.account.name=Tên tài khoản payment.account.userName=User name +payment.account.phoneNr=Phone number payment.account.owner=Họ tên chủ tài khoản payment.account.fullName=Họ tên (họ, tên lót, tên) payment.account.state=Bang/Tỉnh/Vùng diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 7c08c8015b7..52dd35787e0 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -35,7 +35,7 @@ shared.no=否 shared.iUnderstand=我了解 shared.na=N/A shared.shutDown=完全关闭 -shared.reportBug=报告错误到 Github issues +shared.reportBug=在 Github 报告错误 shared.buyBitcoin=买入比特币 shared.sellBitcoin=卖出比特币 shared.buyCurrency=买入 {0} @@ -63,7 +63,7 @@ shared.priceInCurForCur=1 {1} 的 {0} 价格 shared.fixedPriceInCurForCur=1 {1} 的 {0} 的固定价格 shared.amount=数量 shared.txFee=交易手续费 -shared.tradeFee=Trade Fee +shared.tradeFee=交易手续费 shared.buyerSecurityDeposit=买家保证金 shared.sellerSecurityDeposit=卖家保证金 shared.amountWithCur={0} 数量 @@ -102,13 +102,13 @@ shared.nextStep=下一步 shared.selectTradingAccount=选择交易账户 shared.fundFromSavingsWalletButton=从 Bisq 钱包资金划转 shared.fundFromExternalWalletButton=从您的外部钱包充值 -shared.openDefaultWalletFailed=打开默认的比特币钱包应用程序失败了。或许您没有安装? +shared.openDefaultWalletFailed=打开默认的比特币钱包应用程序失败了。您确定您安装了吗? shared.distanceInPercent=与市场价格的差价 % shared.belowInPercent=低于市场价格 % shared.aboveInPercent=高于市场价格 % shared.enterPercentageValue=输入 % 值 shared.OR=或者 -shared.notEnoughFunds=您的 Bisq 钱包中没有足够的资金。\n您需要 {0} 但是您的 Bisq 钱包只有 {1} 。\n\n请从外部比特币钱包注入资金或在“资金/存款”充值到您的 Bisq 钱包。 +shared.notEnoughFunds=您的 Bisq 钱包中没有足够的资金去支付这一交易 需要{0} 您可用余额为 {1}。\n\n请从外部比特币钱包注入资金或在“资金/存款”充值到您的 Bisq 钱包。 shared.waitingForFunds=等待资金充值... shared.depositTransactionId=存款交易 ID shared.TheBTCBuyer=BTC 买家 @@ -214,8 +214,9 @@ shared.mediator=调解员 shared.arbitrator=仲裁员 shared.refundAgent=仲裁员 shared.refundAgentForSupportStaff=退款助理 -shared.delayedPayoutTxId=退款担保交易 ID -shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to +shared.unconfirmedTransactionsLimitReached=你现在有过多的未确认交易。请稍后尝试 #################################################################### @@ -336,7 +337,7 @@ offerbook.offerersAcceptedBankSeats=接受的银行所在国家(买家):\n offerbook.availableOffers=可用报价 offerbook.filterByCurrency=以货币筛选 offerbook.filterByPaymentMethod=以支付方式筛选 -offerbook.timeSinceSigning=自验证 +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=此账户已验证,{0} offerbook.timeSinceSigning.info.arbitrator=由仲裁员验证,并可以验证伙伴账户 offerbook.timeSinceSigning.info.peer=由对方验证,等待限制被解除 @@ -345,6 +346,7 @@ offerbook.timeSinceSigning.info.signer=由对方验证,并可验证对方账 offerbook.timeSinceSigning.info.banned=账户已被封禁 offerbook.timeSinceSigning.daysSinceSigning={0} 天 offerbook.timeSinceSigning.daysSinceSigning.long=自验证{0} +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=当您成功地完成与拥有已验证付款帐户的伙伴交易时,您的付款帐户已验证。\n{0} 天后,最初的 {1} 的限制解除以及你的账户可以验证其他人的付款账户。 offerbook.timeSinceSigning.notSigned=尚未验证 @@ -354,8 +356,8 @@ shared.notSigned.noNeed=此账户类型不适用验证 offerbook.nrOffers=报价数量:{0} offerbook.volume={0}(最小 - 最大) -offerbook.deposit=Deposit BTC (%) -offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.deposit=BTC 保证金(%) +offerbook.deposit.help=交易双方均已支付保证金确保这个交易正常进行。这会在交易完成时退还。 offerbook.createOfferToBuy=创建新的报价来买入 {0} offerbook.createOfferToSell=创建新的报价来卖出 {0} @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=报价停用失败:\n{0} offerbook.activateOffer.failed=报价发布失败:\n{0} offerbook.withdrawFundsHint=您可以从 {0} 中撤回您支付的资金。 -offerbook.warning.noTradingAccountForCurrency.headline=选择的货币没有交易账户 -offerbook.warning.noTradingAccountForCurrency.msg=您选择的货币没有交易账户。\n您想要用您现有的交易账户创建一个报价吗? -offerbook.warning.noMatchingAccount.headline=没有匹配的交易账户。 -offerbook.warning.noMatchingAccount.msg=为了接受这个报价,你需要用这个付款方式建立一个付款账户。\n\n你现在想做这件事吗? +offerbook.warning.noTradingAccountForCurrency.headline=选择的货币没有支付账户 +offerbook.warning.noTradingAccountForCurrency.msg=您选择的货币还没有建立支付账户。\n\n你想要用其他货币创建一个报价吗? +offerbook.warning.noMatchingAccount.headline=没有匹配的支付账户。 +offerbook.warning.noMatchingAccount.msg=这个报价使用了您未创建过的支付方式。\n\n你现在想要创建一个新的支付账户吗? offerbook.warning.counterpartyTradeRestrictions=由于交易伙伴的交易限制,这个报价不能接受 @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=该报价中使用的付款方式被 Bisq offerbook.warning.nodeBlocked=该交易者的匿名地址被 Bisq 开发人员限制。\n当获取来自该交易者的报价,可能有一个未处理的漏洞导致了问题。 offerbook.warning.requireUpdateToNewVersion=您的 Bisq 版本不再兼容交易。\n请通过 https://bisq.network/downloads 更新到最新的 Bisq 版本。 offerbook.warning.tradeLimitNotMatching=您的付款帐户已于 {0} 前创建。您的交易限额基于账龄,因此不满足该报价的要求。\n您的交易限额是:{1}\n最小交易金额为:{2}\n您现在不能接受这个报价。\n只要您的账龄超过 2 个月,此限制就会被移除。 - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=您会以市场价格进行出售(每分钟更新) offerbook.info.buyAtMarketPrice=您将以市场价格进行购买(每分钟更新)。 @@ -463,7 +465,7 @@ createOffer.tac=发布该报价,我同意与满足该条件的任何交易者 createOffer.currencyForFee=挂单费 createOffer.setDeposit=设置买家的保证金(%) createOffer.setDepositAsBuyer=设置自己作为买家的保证金(%) -createOffer.setDepositForBothTraders=Set both traders' security deposit (%) +createOffer.setDepositForBothTraders=设置双方的保证金比例(%) createOffer.securityDepositInfo=您的买家的保证金将会是 {0} createOffer.securityDepositInfoAsBuyer=您作为买家的保证金将会是 {0} createOffer.minSecurityDepositUsed=已使用最低买家保证金 @@ -514,7 +516,7 @@ takeOffer.failed.offererOffline=您不能下单,因为卖家已经下线。 takeOffer.warning.connectionToPeerLost=您与卖家失去连接。\n因为太多连接,他或许已经下线或者关掉了与您的连接。\n\n如果您还是能在报价列表中看到他的报价,您可以再次尝试下单。 takeOffer.error.noFundsLost=\n\n你的钱包里还没有钱。 \n请尝试重启您的应用程序或者检查您的网络连接。 -takeOffer.error.feePaid=\n\n +takeOffer.error.feePaid=\n!\n takeOffer.error.depositPublished=\n\n您的保证金转账已经发布。\n请尝试重启您的应用程序或者检查您的网络连接。\n如果始终存在问题,请到帮助界面联系开发者。 takeOffer.error.payoutPublished=\n\n您的支付转账已经发布。\n请尝试重启您的应用程序或者检查您的网络连接。\n如果始终存在问题,请到帮助界面联系开发者。 takeOffer.tac=接受该报价,意味着我同意这交易界面中的条件。 @@ -541,7 +543,7 @@ portfolio.tab.history=历史记录 portfolio.tab.failed=失败 portfolio.tab.editOpenOffer=编辑报价 -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=等待区块链确认 portfolio.pending.step2_buyer.startPayment=开始付款 @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=等待直到付款到达 portfolio.pending.step3_seller.confirmPaymentReceived=确定收到付款 portfolio.pending.step5.completed=完成 +portfolio.pending.step3_seller.autoConf.status.label=自动确认状态。 +portfolio.pending.autoConf=自动确认 +portfolio.pending.autoConf.blocks=XMR 确认数:{0} / 需求量:{2} +portfolio.pending.autoConf.state.xmr.txKeyReused=交易密钥已重复使用。请发起纠纷处理。 +portfolio.pending.autoConf.state.confirmations=XMR 确认:{0}/{1} +portfolio.pending.autoConf.state.txNotFound=交易并未在内存池中检索。 +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=无有效交易 ID / 交易密钥 +portfolio.pending.autoConf.state.filterDisabledFeature=由开发者禁用 + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=自动确认功能已禁用。{0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=交易金额超过自动确认金额限制。 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=对等点提供不可用数据。{0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=支付交易已经发布 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=已发起纠纷。该交易的自动确认已被禁用 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=交易证明申请已经开始 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=成功结果:{0}/{1} ;{2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=所有服务都已被证明。 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=您请求的服务发生了错误。没有自动确认。 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=服务返回失败。没有自动确认。 + portfolio.pending.step1.info=存款交易已经发布。\n开始付款之前,{0} 需要等待至少一个区块链确认。 portfolio.pending.step1.warn=保证金交易仍未得到确认。这种情况可能会发生在外部钱包转账时使用的交易手续费用较低造成的。 portfolio.pending.step1.openForDispute=保证金交易仍未得到确认。请联系调解员协助。 @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=请到您的在线银行网页并支付 {0} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=请通过提供的联系人与 BTC 卖家联系,并安排会议支付 {0}。\n\n portfolio.pending.step2_buyer.startPaymentUsing=使用 {0} 开始付款 +portfolio.pending.step2_buyer.recipientsAccountData=接受 {0} portfolio.pending.step2_buyer.amountToTransfer=划转数量 portfolio.pending.step2_buyer.sellersAddress=卖家的 {0} 地址 portfolio.pending.step2_buyer.buyerAccount=您的付款帐户将被使用 @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=有些银行可能会 portfolio.pending.step2_buyer.confirmStart.headline=确定您已经付款 portfolio.pending.step2_buyer.confirmStart.msg=您是否向您的交易伙伴发起 {0} 付款? portfolio.pending.step2_buyer.confirmStart.yes=是的,我已经开始付款 - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=你没有提供任何付款证明 +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=您还没有输入交易 ID 以及交易密钥\n\n如果不提供此数据您的交易伙伴无法在收到 XMR 后使用自动确认功能以快速释放 BTC。\n另外,Bisq 要求 XMR 发送者在发生纠纷的时候能够向调解员和仲裁员提供这些信息。\n更多细节在 Bisq Wiki:https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=输入并不是一个 32 字节的哈希值 +portfolio.pending.step2_buyer.confirmStart.warningButton=忽略并继续 portfolio.pending.step2_seller.waitPayment.headline=等待付款 portfolio.pending.step2_seller.f2fInfo.headline=买家的合同信息 portfolio.pending.step2_seller.waitPayment.msg=存款交易至少有一个区块链确认。\n您需要等到 BTC 买家开始 {0} 付款。 @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=接收数量: portfolio.pending.step3_seller.yourAddress=您的 {0} 地址 portfolio.pending.step3_seller.buyersAddress=卖家的 {0} 地址 portfolio.pending.step3_seller.yourAccount=您的交易账户 -portfolio.pending.step3_seller.buyersAccount=卖家的交易账户 +portfolio.pending.step3_seller.xmrTxHash=交易记录 ID +portfolio.pending.step3_seller.xmrTxKey=交易密钥 +portfolio.pending.step3_seller.buyersAccount=买方账号数据 portfolio.pending.step3_seller.confirmReceipt=确定付款收据 portfolio.pending.step3_seller.buyerStartedPayment=BTC 买家已经开始 {0} 的付款。\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=检查您的数字货币钱包或块浏览器的区块链确认,并确认付款时,您有足够的块链确认。 @@ -682,7 +720,7 @@ portfolio.pending.step5_buyer.refunded=退还保证金 portfolio.pending.step5_buyer.withdrawBTC=提现您的比特币 portfolio.pending.step5_buyer.amount=提现数量 portfolio.pending.step5_buyer.withdrawToAddress=提现地址 -portfolio.pending.step5_buyer.moveToBisqWallet=Keep funds in Bisq wallet +portfolio.pending.step5_buyer.moveToBisqWallet=在 Bisq 钱包中保留资金 portfolio.pending.step5_buyer.withdrawExternal=提现到外部钱包 portfolio.pending.step5_buyer.alreadyWithdrawn=您的资金已经提现。\n请查看交易历史记录。 portfolio.pending.step5_buyer.confirmWithdrawal=确定提现请求 @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=请仅在紧急情况下使用此功能 portfolio.pending.timeLockNotOver=你必须等到≈{0}(还需等待{1}个区块)才能提交纠纷。 portfolio.pending.error.depositTxNull=保证金交易无效。没有有效的保证金交易,你使用创建纠纷。请到“设置/网络信息”进行 SPV 重新同步。\n \n如需更多帮助,请联系 Bisq Keybase 团队的 Support 频道。 -portfolio.pending.mediationResult.error.depositTxNull=保证金交易无效。交易被移至失败交易。 -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=保证金交易未确认。未经确认的存款交易不能发起纠纷或仲裁请求。请耐心等待,直到它被确认或进入“设置/网络信息”进行 SPV 重新同步。\n\n如需更多帮助,请联系 Bisq Keybase 团队的 Support 频道。 portfolio.pending.notification=通知 @@ -724,7 +762,7 @@ portfolio.pending.notification=通知 portfolio.pending.support.headline.getHelp=需要帮助? portfolio.pending.support.text.getHelp=如果您有任何问题,您可以尝试在交易聊天中联系交易伙伴,或在 https://bisq.community 询问 Bisq 社区。如果您的问题仍然没有解决,您可以向调解员取得更多的帮助。 portfolio.pending.support.text.getHelp.arbitrator=如果您有任何问题,您可以尝试在交易聊天中联系交易伙伴,或在 https://bisq.community 询问 Bisq 社区。如果你的问题仍然没有解决,你可以向仲裁员取得更多的帮助。 -portfolio.pending.support.button.getHelp=Open Trader Chat +portfolio.pending.support.button.getHelp=开启交易聊天 portfolio.pending.support.popup.info=如果您与交易的问题仍然没有解决,您可以提交工单来请求调解员的帮助。如果您还没有收到付款,请等到交易结束。\n\n您确定要提交工单吗? portfolio.pending.support.popup.button=创建帮助话题 portfolio.pending.support.headline.halfPeriodOver=确认付款 @@ -753,18 +791,34 @@ portfolio.pending.mediationResult.button=查看建议的解决方案 portfolio.pending.mediationResult.popup.headline=调解员在交易 ID:{0}上的建议 portfolio.pending.mediationResult.popup.headline.peerAccepted=你的伙伴已经接受了调解员的建议 portfolio.pending.mediationResult.popup.info=调解员建议的支出如下:\n你将支付:{0}\n你的交易伙伴将支付:{1}\n\n你可以接受或拒绝这笔调解费支出。\n\n通过接受,你验证了合约的支付交易。如果你的交易伙伴也接受和验证,支付将完成,交易将关闭。\n\n如果你们其中一人或双方都拒绝该建议,你将必须等到(2)({3}区块)与仲裁员展开第二轮纠纷讨论,仲裁员将再次调查该案件,并根据他们的调查结果进行支付。\n\n仲裁员可以收取少量费用(费用上限:交易的保证金)作为其工作的补偿。两个交易者都同意调解员的建议是愉快的路径请求仲裁是针对特殊情况的,比如如果一个交易者确信调解员没有提出公平的赔偿建议(或者如果另一个同伴没有回应)。\n\n关于新的仲裁模型的更多细节:https://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=拒绝并请求仲裁 portfolio.pending.mediationResult.popup.alreadyAccepted=您已经接受了。 +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=完成 portfolio.closed.ticketClosed=已仲裁 portfolio.closed.mediationTicketClosed=已调解 portfolio.closed.canceled=已取消 portfolio.failed.Failed=失败 -portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. -portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.unfail=再继续之前,请保证你有一份根目录的备份!\n您想要将此交易移至未完成的交易吗?\n这是一个解锁卡在失败交易的资金的方法 +portfolio.failed.cantUnfail=目前该交易暂无法移至未完成的交易。\n请在完成交易后重试{0} +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -797,8 +851,8 @@ funds.withdrawal.feeExcluded=不含挖矿费的金额 funds.withdrawal.feeIncluded=包含挖矿费的金额 funds.withdrawal.fromLabel=从源地址提现 funds.withdrawal.toLabel=提现地址 -funds.withdrawal.memoLabel=Withdrawal memo -funds.withdrawal.memo=Optionally fill memo +funds.withdrawal.memoLabel=提现备注 +funds.withdrawal.memo=可选备注 funds.withdrawal.withdrawButton=选定提现 funds.withdrawal.noFundsAvailable=没有可用资金提现 funds.withdrawal.confirmWithdrawalRequest=确定提现请求 @@ -835,7 +889,7 @@ funds.tx.noFundsFromDispute=没有退款的纠纷 funds.tx.receivedFunds=收到的资金: funds.tx.withdrawnFromWallet=从钱包提现 funds.tx.withdrawnFromBSQWallet=BTC 已从 BSQ 钱包中取出 -funds.tx.memo=Memo +funds.tx.memo=备注 funds.tx.noTxAvailable=没有可用交易 funds.tx.revert=还原 funds.tx.txSent=交易成功发送到本地 Bisq 钱包中的新地址。 @@ -855,13 +909,24 @@ support.tab.mediation.support=调解 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=历史仲裁 support.tab.ArbitratorsSupportTickets={0} 的工单 -support.filter=Search disputes +support.filter=查找纠纷 support.filter.prompt=输入 交易 ID、日期、洋葱地址或账户信息 -support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=发送私人通知 -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + +support.reOpenByTrader.prompt=您确定想要重新开启纠纷? +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=私人通知 +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=没有创建的话题 support.sendingMessage=发送消息... support.receiverNotOnline=收件人未在线。消息被保存到他们的邮箱。 @@ -903,8 +968,11 @@ support.youOpenedDisputeForMediation=您创建了一个调解请求。\n\n{0}\n\ support.peerOpenedTicket=对方因技术问题请求获取帮助。\n\n{0}\n\nBisq 版本:{1} support.peerOpenedDispute=对方创建了一个纠纷请求。\n\n{0}\n\nBisq 版本:{1} support.peerOpenedDisputeForMediation=对方创建了一个调解请求。\n\n{0}\n\nBisq 版本:{1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} +support.mediatorsDisputeSummary=系统消息:\n调解纠纷总结:\n{0} support.mediatorsAddress=仲裁员的节点地址:{0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -916,9 +984,14 @@ settings.tab.about=关于我们 setting.preferences.general=通用偏好 setting.preferences.explorer=比特币区块浏览器 -setting.preferences.explorer.bsq=BSQ 区块浏览器 +setting.preferences.explorer.bsq=Bisq 区块浏览器 setting.preferences.deviation=与市场价格最大差价 setting.preferences.avoidStandbyMode=避免待机模式 +setting.preferences.autoConfirmXMR=XMR 自动确认 +setting.preferences.autoConfirmEnabled=启用 +setting.preferences.autoConfirmRequiredConfirmations=已要求确认 +setting.preferences.autoConfirmMaxTradeSize=最大交易量(BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer 链接(使用Tor,但本地主机,LAN IP地址和 *.local 主机名除外) setting.preferences.deviationToLarge=值不允许大于30% setting.preferences.txFee=提现交易费(聪/byte) setting.preferences.useCustomValue=使用自定义值 @@ -949,10 +1022,10 @@ settings.preferences.supportLanguageWarning=如有任何争议,请注意调解 settings.preferences.selectCurrencyNetwork=选择网络 setting.preferences.daoOptions=DAO 选项 setting.preferences.dao.resyncFromGenesis.label=从初始 tx 重构 DAO 状态 -setting.preferences.dao.resyncFromResources.label=Rebuild DAO state from resources -setting.preferences.dao.resyncFromResources.popup=After an application restart the Bisq network governance data will be reloaded from the seed nodes and the BSQ consensus state will be rebuilt from the latest resource files. -setting.preferences.dao.resyncFromGenesis.popup=A resync from genesis transaction can take considerable time and CPU resources. Are you sure you want to do that? Mostly a resync from latest resource files is sufficient and much faster.\n\nIf you proceed, after an application restart the Bisq network governance data will be reloaded from the seed nodes and the BSQ consensus state will be rebuilt from the genesis transaction. -setting.preferences.dao.resyncFromGenesis.resync=Resync from genesis and shutdown +setting.preferences.dao.resyncFromResources.label=从指定资源重新构建 DAO 状态 +setting.preferences.dao.resyncFromResources.popup=应用程序重新启动后,Bisq 网络治理数据将从种子节点重新加载,而 BSQ 同步状态将从创始交易中重新构建。 +setting.preferences.dao.resyncFromGenesis.popup=从创始交易中出现同步会消耗大量时间以及 CPU 资源。您确定要重新同步吗?通常,从最新资源文件进行重新同步就足够了,而且速度更快。\n\n应用程序重新启动后,Bisq 网络治理数据将从种子节点重新加载,而 BSQ 同步状态将从初始交易中重新构建。 +setting.preferences.dao.resyncFromGenesis.resync=从创始区块重新同步并关闭 setting.preferences.dao.isDaoFullNode=以 DAO 全节点运行 Bisq setting.preferences.dao.rpcUser=RPC 用户名 setting.preferences.dao.rpcPw=PRC 密码 @@ -981,8 +1054,8 @@ settings.net.p2PPeersLabel=已连接节点 settings.net.onionAddressColumn=匿名地址 settings.net.creationDateColumn=已建立连接 settings.net.connectionTypeColumn=入/出 -settings.net.sentDataLabel=Sent data statistics -settings.net.receivedDataLabel=Received data statistics +settings.net.sentDataLabel=统计数据已发送 +settings.net.receivedDataLabel=统计数据已接收 settings.net.roundTripTimeColumn=延迟 settings.net.sentBytesColumn=发送 settings.net.receivedBytesColumn=接收 @@ -995,8 +1068,8 @@ settings.net.heightColumn=高度 settings.net.needRestart=您需要重启应用程序以同意这次变更。\n您需要现在重启吗? settings.net.notKnownYet=至今未知... -settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec -settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec +settings.net.sentData=已发送数据 {0},{1} 条消息,{2} 条消息/秒 +settings.net.receivedData=已接收数据 {0},{1} 条消息,{2} 条消息/秒 settings.net.ips=添加逗号分隔的 IP 地址及端口,如使用8333端口可不填写。 settings.net.seedNode=种子节点 settings.net.directPeer=节点(直连) @@ -1018,8 +1091,8 @@ setting.about.support=支持 Bisq setting.about.def=Bisq 不是一个公司,而是一个社区项目,开放参与。如果您想参与或支持 Bisq,请点击下面连接。 setting.about.contribute=贡献 setting.about.providers=数据提供商 -setting.about.apisWithFee=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices, and Bisq Mempool Nodes for mining fee estimation. -setting.about.apis=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices. +setting.about.apisWithFee=Bisq 使用 Bisq 价格指数来表示法币与虚拟货币的市场价格,并使用 Bisq 内存池节点来估算采矿费。 +setting.about.apis=Bisq 使用 Bisq 价格指数来表示法币与数字货币的市场价格。 setting.about.pricesProvided=交易所价格提供商 setting.about.feeEstimation.label=矿工手续费估算提供商 setting.about.versionDetails=版本详情 @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=打开应急 BSQ 钱包工具 setting.about.shortcuts.showTorLogs=在 DEBUG 与 WARN 之间切换 Tor 日志等级 -setting.about.shortcuts.showDisputeStatistics=显示所有纠纷概要 -setting.about.shortcuts.showDisputeStatistics.value=切换至纠纷页面并按下:{0} - setting.about.shortcuts.manualPayoutTxWindow=打开窗口手动支付双重验证存款交易 setting.about.shortcuts.reRepublishAllGovernanceData=重新推送 DAO 众议厅数据(包括提案以及投票) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=切换至账户页面并按下 setting.about.shortcuts.registerMediator=注册调解员(仅限调解员/仲裁员) setting.about.shortcuts.registerMediator.value=切换至账户页面并按下:{0} -setting.about.shortcuts.reOpenDispute=重新打开关闭的纠纷(仅限调解员/仲裁员) -setting.about.shortcuts.reOpenDispute.value=选择关闭的纠纷并按下:{0} - setting.about.shortcuts.openSignPaymentAccountsWindow=打开账龄验证窗口(仅限仲裁员) setting.about.shortcuts.openSignPaymentAccountsWindow.value=切换至仲裁页面并按下:{0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=发送警报或更新消息(需要权限 setting.about.shortcuts.sendFilter=设置过滤器(需要权限) setting.about.shortcuts.sendPrivateNotification=发送私人通知到对等点(需要权限) -setting.about.shortcuts.sendPrivateNotification.value=打开在头像或纠纷中对等点信息并按下:{0} - +setting.about.shortcuts.sendPrivateNotification.value=点击交易伙伴头像并按下:{0} 以显示更多信息 +setting.info.headline=新 XMR 自动确认功能 +setting.info.msg=当你完成 BTC/XMR 交易时,您可以使用自动确认功能来验证是否向您的钱包中发送了正确数量的 XMR,以便 Bisq 可以自动将交易标记为完成,从而使每个人都可以更快地进行交易。\n\n自动确认使用 XMR 发送方提供的交易密钥在至少 2 个 XMR 区块浏览器节点上检查 XMR 交易。在默认情况下,Bisq 使用由 Bisq 贡献者运行的区块浏览器节点,但是我们建议运行您自己的 XMR 区块浏览器节点以最大程度地保护隐私和安全。\n\n您还可以在``设置''中将每笔交易的最大 BTC 数量设置为自动确认以及所需确认的数量。\n\n在 Bisq Wiki 上查看更多详细信息(包括如何设置自己的区块浏览器节点):https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1090,7 +1158,7 @@ setting.about.shortcuts.sendPrivateNotification.value=打开在头像或纠纷 account.tab.arbitratorRegistration=仲裁员注册 account.tab.mediatorRegistration=调解员注册 account.tab.refundAgentRegistration=退款助理注册 -account.tab.signing=Signing +account.tab.signing=验证中 account.tab.account=账户 account.info.headline=欢迎来到 Bisq 账户 account.info.msg=在这里你可以设置交易账户的法定货币及数字货币,选择仲裁员和备份你的钱包及账户数据。\n\n当你开始运行 Bisq 就已经创建了一个空的比特币钱包。\n\n我们建议你在充值之前写下你比特币钱包的还原密钥(在左边的列表)和考虑添加密码。在“资金”选项中管理比特币存入和提现。\n\n隐私 & 安全:\nBisq 是一个去中心化的交易所 – 意味着您的所有数据都保存在您的电脑上,没有服务器,我们无法访问您的个人信息,您的资金,甚至您的 IP 地址。如银行账号、数字货币、比特币地址等数据只分享给与您交易的人,以实现您发起的交易(如果有争议,仲裁员将会看到您的交易数据)。 @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=请确保您按照 {1} 网页上所述使用 {0 account.altcoin.popup.wallet.confirm=我了解并确定我知道我需要哪种钱包。 account.altcoin.popup.upx.msg=在 Bisq 上交易 UPX 需要您了解并满足以下要求:\n\n要发送 UPX ,您需要使用官方的 UPXmA GUI 钱包或启用 store-tx-info 标志的 UPXmA CLI 钱包(在新版本中是默认的)。请确保您可以访问Tx密钥,因为在纠纷状态时需要。\nmonero-wallet-cli(使用get_Tx_key命令)\nmonero-wallet-gui:在高级>证明/检查页面。\n\n在普通的区块链浏览器中,这种交易是不可验证的。\n\n如有纠纷,你须向仲裁员提供下列资料:\n \n- Tx私钥\n- 交易哈希\n- 接收者的公开地址\n\n如未能提供上述资料,或使用不兼容的钱包,将会导致纠纷败诉。如果发生纠纷,UPX 发送方负责向仲裁员提供 UPX 转账的验证。\n\n不需要支付 ID,只需要普通的公共地址。\n \n如果您对该流程不确定,请访问 UPXmA Discord 频道(https://discord.gg/vhdNSrV)或 Telegram 交流群(https://t.me/uplexaOfficial)了解更多信息。\n\n account.altcoin.popup.arq.msg=在 Bisq 上交易 ARQ 需要您了解并满足以下要求:\n\n要发送 ARQ ,您需要使用官方的 ArQmA GUI 钱包或启用 store-tx-info 标志的 ArQmA CLI 钱包(在新版本中是默认的)。请确保您可以访问Tx密钥,因为在纠纷状态时需要。\nmonero-wallet-cli(使用get_Tx_key命令)\nmonero-wallet-gui:在高级>证明/检查页面。\n\n在普通的区块链浏览器中,这种交易是不可验证的。\n\n如有纠纷,你须向调解员或仲裁员提供下列资料:\n\n- Tx私钥\n- 交易哈希\n- 接收者的公开地址\n\n如未能提供上述资料,或使用不兼容的钱包,将会导致纠纷败诉。如果发生纠纷,ARQ 发送方负责向调解员或仲裁员提供 ARQ 转账的验证。\n\n不需要交易 ID,只需要普通的公共地址。\n\n如果您对该流程不确定,请访问 ArQmA Discord 频道(https://discord.gg/s9BQpJT)或 ArQmA 论坛(https://labs.arqma.com)了解更多信息。 -account.altcoin.popup.xmr.msg=在 Bisq 上交易 XMR 需要你理解并满足以下要求:\n\n付款证明:由于 Monero 是一种高隐私性货币,区块链中不公开一些交易细节,而且,在发生争议时,调解员或仲裁员需要他们检查交易是否真的进行了。在 Bisq 中,XMR 交易的发送方负责在发生争议时向调解员或仲裁员提供此信息。为了做到验证付款,您必须使用钱包发送 XMR ,钱包提供所需的信息来证明付款已经完成,其中包括:\n\n- 交易密钥(Tx 公钥、Tx 密钥或 Tx 私钥)\n- 交易ID(Tx ID 或Tx 哈希)\n- 目的地地址(收款人地址)\n\n这些信息可以在官方的 Monero GUI 和 CLI 钱包、MyMonero 和 Exodus (桌面版)以及 Cake Wallet、MyMonero 和 Monerujo(移动版)中找到,地点如下:\n\n- Monero GUI:转到 交易 标签\n- Monero CLI:使用命令 get_tx_key TXID 。必须启用标记 store-tx-info (在新版本中默认启用)\n- 其他钱包:转到交易历史记录,在发送的交易中搜索交易密钥(Tx密钥或Tx秘密)和目标地址。保存收件人地址选项必须在 Cake Wallet 设置中启用。\n\n如果您使用的钱包与上述不同,请确保您可以访问这三个信息。由于交易密钥和目标地址存储在 Monero 钱包软件中,无法在 Monero 区块链中恢复,所以在 Bisq 交易完成之前,您不应该删除或恢复您的 Monero 钱包。如不提供以上资料,将导致纠纷败诉。\n\n检查付款:有了这三条信息,可以通过以下方式来验证是否有一定数量的 Monero 被发送到特定地址:\n\n- Monero GUI:将钱包切换到高级模式,进入高级>验证/检查>检查交易\n- Monero CLI:使用命令 check_tx_key TXID TXKEY ADDRESS\n- XMR Tx 验证工具(https://xmr.llcoins.net/checktx.html)\n- 浏览 Monero 官网(https://www.exploremonero.com/receipt)\n\n如果您仍然对此有疑问,请访问(https://www.getmonero.org/resources/userguides/prove-payment.html)来查找更多信息或在 Monero Support subreddit 上提问(https://www.reddit.com/r/monerosupport/)。 +account.altcoin.popup.xmr.msg=在 Bisq 上交易 XMR 需要你理解并满足以下要求。\n\n如果您出售 XMR,当您在纠纷中您必须要提供下列信息给调解员或仲裁员:\n- 交易密钥(Tx 公钥,Tx密钥,Tx私钥)\n- 交易 ID(Tx ID 或 Tx 哈希)\n- 交易目标地址(接收者地址)\n\n在 wiki 中查看更多关于 Monero 钱包的信息:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\n如未能提供要求的交易数据将在纠纷中直接判负\n\n还要注意,Bisq 现在提供了自动确认 XMR 交易的功能,以使交易更快,但是您需要在设置中启用它。\n\n有关自动确认功能的更多信息,请参见 Wiki:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=区块链浏览器在 Bisq 上交易 XMR 需要您了解并满足以下要求:\n\n发送MSR时,您需要使用官方的 Masari GUI 钱包、启用store-tx-info标记的Masari CLI钱包(默认启用)或Masari 网页钱包(https://wallet.getmasari.org)。请确保您可以访问的 tx 密钥,因为如果发生纠纷这是需要的。\nmonero-wallet-cli(使用get_Tx_key命令)\nmonero-wallet-gui:在高级>证明/检查页面。\n\nMasari 网页钱包(前往 帐户->交易历史和查看您发送的交易细节)\n\n验证可以在钱包中完成。\nmonero-wallet-cli:使用命令(check_tx_key)。\nmonero-wallet-gui:在高级>证明/检查页面\n验证可以在区块浏览器中完成\n打开区块浏览器(https://explorer.getmasari.org),使用搜索栏查找您的事务哈希。\n一旦找到交易,滚动到底部的“证明发送”区域,并填写所需的详细信息。\n如有纠纷,你须向调解员或仲裁员提供下列资料:\n- Tx私钥\n- 交易哈希\n- 接收者的公开地址\n\n不需要交易 ID,只需要正常的公共地址。\n如未能提供上述资料,或使用不兼容的钱包,将会导致纠纷败诉。如果发生纠纷,XMR 发送方负责向调解员或仲裁员提供 XMR 转账的验证。\n\n如果您对该流程不确定,请访问官方的 Masari Discord(https://discord.gg/sMCwMqs)上寻求帮助。 account.altcoin.popup.blur.msg=在 Bisq 上交易 BLUR 需要你了解并满足以下要求:\n\n要发送匿名信息你必须使用匿名网络 CLI 或 GUI 钱包。\n如果您正在使用 CLI 钱包,在传输发送后将显示交易哈希(tx ID)。您必须保存此信息。在发送传输之后,您必须立即使用“get_tx_key”命令来检索交易私钥。如果未能执行此步骤,以后可能无法检索密钥。\n\n如果您使用 Blur Network GUI 钱包,可以在“历史”选项卡中方便地找到交易私钥和交易 ID。发送后立即定位感兴趣的交易。单击包含交易的框的右下角的“?”符号。您必须保存此信息。\n\n如果仲裁是必要的,您必须向调解员或仲裁员提供以下信息:1.)交易ID,2.)交易私钥,3.)收件人地址。调解或仲裁程序将使用 BLUR 事务查看器(https://blur.cash/#tx-viewer)验证 BLUR 转账。\n\n未能向调解员或仲裁员提供必要的信息将导致败诉。在所有争议的情况下,匿名发送方承担100%的责任来向调解员或仲裁员核实交易。\n\n如果你不了解这些要求,不要在 Bisq 上交易。首先,在 Blur Network Discord 中寻求帮助(https://discord.gg/dMWaqVW)。 @@ -1159,7 +1227,7 @@ account.password.info=使用密码保护,您需要在将比特币从钱包中 account.seed.backup.title=备份您的钱包还原密钥 account.seed.info=请写下钱包还原密钥和时间!\n您可以通过还原密钥和时间在任何时候恢复您的钱包。\n还原密钥用于 BTC 和 BSQ 钱包。\n\n您应该在一张纸上写下还原密钥并且不要保存它们在您的电脑上。\n请注意还原密钥并不能代替备份。\n您需要备份完整的应用程序目录在”账户/备份“界面去恢复有效的应用程序状态和数据。 -account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info. +account.seed.backup.warning=请注意种子词不能替代备份。您需要为整个应用目录(在“账户/备份”选项卡中)以恢复应用状态以及数据。\n导入种子词仅在紧急情况时才推荐使用。 如果没有正确备份数据库文件和密钥,该应用程序将无法运行!\n\n在 Bisq wiki 中查看更多信息: https://bisq.wiki/Backing_up_application_data account.seed.warn.noPw.msg=您还没有设置一个可以保护还原密钥显示的钱包密码。\n\n要显示还原密钥吗? account.seed.warn.noPw.yes=是的,不要再问我 account.seed.enterPw=输入密码查看还原密钥 @@ -1498,7 +1566,7 @@ dao.bond.bondedRoleType.FORUM_ADMIN=论坛管理 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter 管理 # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin +dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase 管理员 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube 管理 # suppress inspection "UnusedProperty" @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=价格节点运营者 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=比特币节点运营者 # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=市场 API 运营者 +dao.bond.bondedRoleType.MARKETS_OPERATOR=交易所运营者 # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ 浏览器运营者 +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=浏览器运营者 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=移动通知中继运营者 # suppress inspection "UnusedProperty" @@ -1972,28 +2040,37 @@ disputeSummaryWindow.reason.OTHER=其他 # suppress inspection "UnusedProperty" disputeSummaryWindow.reason.BANK_PROBLEMS=银行 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.OPTION_TRADE=Option trade +disputeSummaryWindow.reason.OPTION_TRADE=可选交易 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=Seller not responding +disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=卖家未回应 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=Wrong sender account +disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=错误的发送者账号 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.PEER_WAS_LATE=Peer was late +disputeSummaryWindow.reason.PEER_WAS_LATE=交易伙伴已超时 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled +disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=交易已稳定 disputeSummaryWindow.summaryNotes=总结说明 disputeSummaryWindow.addSummaryNotes=添加总结说明 disputeSummaryWindow.close.button=关闭话题 -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\n下一个步骤:\n打开未完成交易,接受或拒绝建议的调解员的建议 -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\n下一个步骤:\n不需要您采取进一步的行动。如果仲裁员做出了对你有利的裁决,你将在 资金/交易 页中看到“仲裁退款”交易 + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=你也需要关闭交易对象的话题! disputeSummaryWindow.close.txDetails.headline=发布交易退款 disputeSummaryWindow.close.txDetails.buyer=买方收到{0}在地址:{1} disputeSummaryWindow.close.txDetails.seller=卖方收到{0}在地址:{1} disputeSummaryWindow.close.txDetails=费用:{0}\n{1}{2}交易费:{3}({4}satoshis/byte)\n事务大小:{5} Kb\n\n您确定要发布此事务吗? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} 钱包急救工具 emptyWalletWindow.info=请在紧急情况下使用,如果您无法从 UI 中访问您的资金。\n\n请注意,使用此工具时,所有未结报价将自动关闭。\n\n在使用此工具之前,请备份您的数据目录。您可以在“帐户/备份”中执行此操作。\n\n请报告我们您的问题,并在 Github 或 Bisq 论坛上提交错误报告,以便我们可以调查导致问题的原因。 emptyWalletWindow.balance=您的可用钱包余额 @@ -2013,8 +2090,8 @@ filterWindow.onions=筛选匿名地址(用逗号“,”隔开) filterWindow.accounts=筛选交易账户数据:\n格式:逗号分割的 [付款方式ID|数据字段|值] filterWindow.bannedCurrencies=筛选货币代码(用逗号“,”隔开) filterWindow.bannedPaymentMethods=筛选支付方式 ID(用逗号“,”隔开) -filterWindow.bannedAccountWitnessSignerPubKeys=Filtered account witness signer pub keys (comma sep. hex of pub keys) -filterWindow.bannedPrivilegedDevPubKeys=Filtered privileged dev pub keys (comma sep. hex of pub keys) +filterWindow.bannedAccountWitnessSignerPubKeys=已过滤的帐户证人签名者公钥(逗号分隔十六进制公钥) +filterWindow.bannedPrivilegedDevPubKeys=已过滤的特权开发者公钥(逗号分隔十六进制公钥) filterWindow.arbitrators=筛选后的仲裁人(用逗号“,”隔开的洋葱地址) filterWindow.mediators=筛选后的调解员(用逗号“,”隔开的洋葱地址) filterWindow.refundAgents=筛选后的退款助理(用逗号“,”隔开的洋葱地址) @@ -2023,11 +2100,12 @@ filterWindow.priceRelayNode=筛选后的价格中继节点(用逗号“,”隔 filterWindow.btcNode=筛选后的比特币节点(用逗号“,”隔开的地址+端口) filterWindow.preventPublicBtcNetwork=禁止使用公共比特币网络 filterWindow.disableDao=禁用 DAO +filterWindow.disableAutoConf=禁用自动确认 filterWindow.disableDaoBelowVersion=DAO 最低所需要的版本 filterWindow.disableTradeBelowVersion=交易最低所需要的版本 filterWindow.add=添加筛选 filterWindow.remove=移除筛选 -filterWindow.btcFeeReceiverAddresses=BTC fee receiver addresses +filterWindow.btcFeeReceiverAddresses=比特币手续费接收地址 offerDetailsWindow.minBtcAmount=最小 BTC 数量 offerDetailsWindow.min=(最小 {0}) @@ -2046,7 +2124,7 @@ offerDetailsWindow.creationDate=创建时间 offerDetailsWindow.makersOnion=卖家的匿名地址 qRCodeWindow.headline=二维码 -qRCodeWindow.msg=请使用这二维码从外部钱包来充值您的 Bisq 钱包。 +qRCodeWindow.msg=请使用二维码从外部钱包充值至 Bisq 钱包 qRCodeWindow.request=付款请求:\n{0} selectDepositTxWindow.headline=选择纠纷的存款交易 @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=发送私人通知 showWalletDataWindow.walletData=钱包数据 showWalletDataWindow.includePrivKeys=包含私钥 +setXMRTxKeyWindow.headline=证明已发送 XMR +setXMRTxKeyWindow.note=在下面添加 tx 信息可以更快的自动确认交易。更多信息::https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=交易 ID (可选) +setXMRTxKeyWindow.txKey=交易密钥 (可选) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=用户协议 @@ -2085,7 +2168,7 @@ tradeDetailsWindow.disputedPayoutTxId=纠纷支付交易 ID: tradeDetailsWindow.tradeDate=交易时间 tradeDetailsWindow.txFee=矿工手续费 tradeDetailsWindow.tradingPeersOnion=交易伙伴匿名地址 -tradeDetailsWindow.tradingPeersPubKeyHash=Trading peers pubkey hash +tradeDetailsWindow.tradingPeersPubKeyHash=交易伙伴公钥哈希值 tradeDetailsWindow.tradeState=交易状态 tradeDetailsWindow.agentAddresses=仲裁员/调解员 @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=交易 ID 为 {0} 的已关闭交易 error.closedTradeWithNoDepositTx=交易 ID 为 {0} 的保证金交易已被确认。\n\n请重新启动应用程序来清理已关闭的交易列表。 popup.warning.walletNotInitialized=钱包至今未初始化 +popup.warning.osxKeyLoggerWarning=由于 MacOS 10.14 及更高版本中的安全措施更加严格,因此启动 Java 应用程序(Bisq 使用Java)会在 MacOS 中引发弹出警告(``Bisq 希望从任何应用程序接收击键'').\n\n为了避免该问题,请打开“ MacOS 设置”,然后转到“安全和隐私”->“隐私”->“输入监视”,然后从右侧列表中删除“ Bisq”。\n\n一旦解决了技术限制(所需的 Java 版本的 Java 打包程序尚未交付),Bisq将升级到新的 Java 版本,以避免该问题。 popup.warning.wrongVersion=您这台电脑上可能有错误的 Bisq 版本。\n您的电脑的架构是:{0}\n您安装的 Bisq 二进制文件是:{1}\n请关闭并重新安装正确的版本({2})。 popup.warning.incompatibleDB=我们检测到不兼容的数据库文件!\n\n那些数据库文件与我们当前的代码库不兼容:\n{0}\n\n我们对损坏的文件进行了备份,并将默认值应用于新的数据库版本。\n\n备份位于:\n{1}/db/backup_of_corrupted_data。\n\n请检查您是否安装了最新版本的 Bisq\n您可以下载:\nhttps://bisq.network/downloads\n\n请重新启动应用程序。 popup.warning.startupFailed.twoInstances=Bisq 已经在运行。 您不能运行两个 Bisq 实例。 @@ -2169,12 +2253,12 @@ popup.warning.insufficientBtcFundsForBsqTx=你没有足够的 BTC 资金支付 popup.warning.bsqChangeBelowDustException=该交易产生的 BSQ 变化输出低于零头限制(5.46 BSQ),将被比特币网络拒绝。\n\n您需要发送更高的金额以避免更改输出(例如,通过在您的发送金额中添加零头),或者向您的钱包中添加更多的 BSQ 资金,以避免生成零头输出。\n\n零头输出为 {0}。 popup.warning.btcChangeBelowDustException=该交易创建的更改输出低于零头限制(546 聪),将被比特币网络拒绝。\n\n您需要将零头添加到发送量中,以避免生成零头输出。\n\n零头输出为{0}。 -popup.warning.insufficientBsqFundsForBtcFeePayment=You''ll need more BSQ to do this transaction—the last 5.46 BSQ in your wallet cannot be used to pay trade fees because of dust limits in the Bitcoin protocol.\n\nYou can either buy more BSQ or pay trade fees with BTC.\n\nMissing funds: {0} +popup.warning.insufficientBsqFundsForBtcFeePayment=您需要更多的 BSQ 去完成这笔交易 - 钱包中最后剩余 5.46 BSQ 将无法用于支付交易手续费因为 BTC 协议中的零头限制。\n\n你可以购买更多的 BSQ 或用 BTC支付交易手续费\n\n缺少 BSQ 资金:{0} popup.warning.noBsqFundsForBtcFeePayment=您的 BSQ 钱包没有足够的资金支付 BSQ 的交易费用。 popup.warning.messageTooLong=您的信息超过最大允许的大小。请将其分成多个部分发送,或将其上传到 https://pastebin.com 之类的服务器。 popup.warning.lockedUpFunds=你已经从一个失败的交易中冻结了资金。\n冻结余额:{0}\n存款tx地址:{1}\n交易单号:{2}\n\n请通过选择待处理交易界面中的交易并点击“alt + o”或“option+ o”打开帮助话题。 -popup.warning.nodeBanned=其中一个 {0} 节点被禁用。请重新启动您的应用程序,以确保没有连接到禁止节点。 +popup.warning.nodeBanned=其中一个 {0} 节点已被禁用 popup.warning.priceRelay=价格传递 popup.warning.seed=种子 popup.warning.mandatoryUpdate.trading=请更新到最新的 Bisq 版本。强制更新禁止了旧版本进行交易。更多信息请访问 Bisq 论坛。 @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=为了确保双方都遵守交易协议,双方 popup.info.cashDepositInfo=请确保您在您的地区有一个银行分行,以便能够进行现金存款。\n卖方银行的银行 ID(BIC/SWIFT)为:{0}。 popup.info.cashDepositInfo.confirm=我确认我可以支付保证金 popup.info.shutDownWithOpenOffers=Bisq 正在被关闭,但仍有公开的报价。\n\n当 Bisq 关闭时,这些提供将不能在 P2P 网络上使用,但是它们将在您下次启动 Bisq 时重新发布到 P2P 网络上。\n\n为了让您的报价在线,保持 Bisq 运行,并确保这台计算机也在线(即,确保它不会进入待机模式…显示器待机不是问题)。 +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=重要私人通知! @@ -2245,22 +2330,22 @@ popup.accountSigning.signedByPeer=您的一个付款帐户已经被交易伙伴 popup.accountSigning.peerLimitLifted=您其中一个帐户的初始限额已被取消。\n\n{0} popup.accountSigning.peerSigner=您的一个帐户已足够成熟,可以验证其他付款帐户,您的一个帐户的初始限额已被取消。\n\n{0} -popup.accountSigning.singleAccountSelect.headline=Select account age witness -popup.accountSigning.singleAccountSelect.description=Search for account age witness. -popup.accountSigning.singleAccountSelect.datePicker=Select point of time for signing -popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age witness -popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash -popup.accountSigning.confirmSingleAccount.button=Sign account age witness -popup.accountSigning.successSingleAccount.description=Witness {0} was signed -popup.accountSigning.successSingleAccount.success.headline=Success -popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0} - -popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys -popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys -popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed -popup.accountSigning.unsignedPubKeys.result.headline=Signing completed -popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys -popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.accountSigning.singleAccountSelect.headline=选择账龄证据 +popup.accountSigning.singleAccountSelect.description=寻找账龄证据 +popup.accountSigning.singleAccountSelect.datePicker=选择要验证的帐户的时间点 +popup.accountSigning.confirmSingleAccount.headline=确认所选账龄证据 +popup.accountSigning.confirmSingleAccount.selectedHash=已选择证据哈希值 +popup.accountSigning.confirmSingleAccount.button=验证账龄证据 +popup.accountSigning.successSingleAccount.description=证据 {0} 已被验证 +popup.accountSigning.successSingleAccount.success.headline=成功 +popup.accountSigning.successSingleAccount.signError=未能成功验证证据,{0} + +popup.accountSigning.unsignedPubKeys.headline=未验证公钥 +popup.accountSigning.unsignedPubKeys.sign=验证公钥 +popup.accountSigning.unsignedPubKeys.signed=公钥已被验证 +popup.accountSigning.unsignedPubKeys.result.headline=验证已完成 +popup.accountSigning.unsignedPubKeys.result.signed=已验证公钥 +popup.accountSigning.unsignedPubKeys.result.failed=未能验证公钥 #################################################################### # Notifications @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=支付账户在 {0} 前创建。 peerInfoIcon.tooltip.unknownAge=支付账户账龄未知。 tooltip.openPopupForDetails=打开弹出窗口的详细信息 +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=使用外部区块链浏览器打开地址:{0} tooltip.openBlockchainForTx=使用外部区块链浏览器打开交易:{0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=打开默认的比特币钱包应用程序失 peerInfoIcon.tooltip={0}\n标识:{1} txIdTextField.copyIcon.tooltip=复制交易 ID 到剪贴板 -txIdTextField.blockExplorerIcon.tooltip=使用外部区块链浏览器打开这个交易 ID +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=使用外部区块链浏览器打开这 navigation.account=“账户” navigation.account.walletSeed=“账户/钱包密钥” -navigation.funds.availableForWithdrawal=“资金/提现” +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=“资料/未完成报价” navigation.portfolio.pending=“业务/未完成交易” navigation.portfolio.closedTrades=“资料/历史” @@ -2454,9 +2541,10 @@ seed.warn.walletNotEmpty.msg=你的比特币钱包不是空的。\n\n在尝试 seed.warn.walletNotEmpty.restore=无论如何我要恢复 seed.warn.walletNotEmpty.emptyWallet=我先清空我的钱包 seed.warn.notEncryptedAnymore=你的钱包被加密了。\n\n恢复后,钱包将不再加密,您必须设置新的密码。\n\n你要继续吗? -seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? +seed.warn.walletDateEmpty=由于您尚未指定钱包日期,因此 Bisq 将必须扫描 2013.10.09(BIP39创始日期)之后的区块链。\n\nBIP39 钱包于 Bisq 于 2017.06.28 首次发布(版本 v0.5)。因此,您可以使用该日期来节省时间。\n\n理想情况下,您应指定创建钱包种子的日期。\n\n\n您确定要继续而不指定钱包日期吗? seed.restore.success=新的还原密钥成功地恢复了钱包。\n\n您需要关闭并重新启动应用程序。 seed.restore.error=使用还原密钥恢复钱包时出现错误。{0} +seed.restore.openOffers.warn=您有公开报价,如果您从种子词恢复,则这些报价将被删除。\n您确定要继续吗? #################################################################### @@ -2466,7 +2554,8 @@ seed.restore.error=使用还原密钥恢复钱包时出现错误。{0} payment.account=账户 payment.account.no=账户编号 payment.account.name=账户名称 -payment.account.userName=User name +payment.account.userName=用户昵称 +payment.account.phoneNr=电话号码 payment.account.owner=账户拥有者姓名: payment.account.fullName=全称(名,中间名,姓) payment.account.state=州/省/地区 @@ -2536,21 +2625,21 @@ payment.accountType=账户类型 payment.checking=检查 payment.savings=保存 payment.personalId=个人 ID -payment.clearXchange.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle:\nhttps://www.zellepay.com/get-started\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Bisq account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Bisq. +payment.clearXchange.info=Zelle是一项转账服务,转账到其他银行做的很好。\n\n1.检查此页面以查看您的银行是否(以及如何)与 Zelle 合作:\nhttps://www.zellepay.com/get-started\n\n2.特别注意您的转账限额-汇款限额因银行而异,银行通常分别指定每日,每周和每月的限额。\n\n3.如果您的银行不能使用 Zelle,您仍然可以通过 Zelle 移动应用程序使用它,但是您的转账限额会低得多。\n\n4.您的 Bisq 帐户上指定的名称必须与 Zelle/银行帐户上的名称匹配。 \n\n如果您无法按照贸易合同中的规定完成 Zelle 交易,则可能会损失部分(或全部)保证金。\n\n由于 Zelle 的拒付风险较高,因此建议卖家通过电子邮件或 SMS 与未签名的买家联系,以确认买家确实拥有 Bisq 中指定的 Zelle 帐户。 payment.fasterPayments.newRequirements.info=有些银行已经开始核实快捷支付收款人的全名。您当前的快捷支付帐户没有填写全名。\n\n请考虑在 Bisq 中重新创建您的快捷支付帐户,为将来的 {0} 买家提供一个完整的姓名。\n\n重新创建帐户时,请确保将银行区号、帐户编号和帐龄验证盐值从旧帐户复制到新帐户。这将确保您现有的帐龄和签名状态得到保留。 payment.moneyGram.info=使用 MoneyGram 时,BTC 买方必须将授权号码和收据的照片通过电子邮件发送给 BTC 卖方。收据必须清楚地显示卖方的全名、国家或地区、州和金额。买方将在交易过程中显示卖方的电子邮件。 payment.westernUnion.info=使用 Western Union 时,BTC 买方必须通过电子邮件将 MTCN(运单号)和收据照片发送给 BTC 卖方。收据上必须清楚地显示卖方的全名、城市、国家或地区和金额。买方将在交易过程中显示卖方的电子邮件。 payment.halCash.info=使用 HalCash 时,BTC 买方需要通过手机短信向 BTC 卖方发送 HalCash 代码。\n\n请确保不要超过银行允许您用半现金汇款的最高金额。每次取款的最低金额是 10 欧元,最高金额是 10 欧元。金额是 600 欧元。对于重复取款,每天每个接收者 3000 欧元,每月每个接收者 6000 欧元。请与您的银行核对这些限额,以确保它们使用与此处所述相同的限额。\n\n提现金额必须是 10 欧元的倍数,因为您不能从 ATM 机提取其他金额。 创建报价和下单屏幕中的 UI 将调整 BTC 金额,使 EUR 金额正确。你不能使用基于市场的价格,因为欧元的数量会随着价格的变化而变化。\n -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk.\n\nTo mitigate this risk, Bisq sets per-trade limits based on two factors:\n\n1. The estimated level of chargeback risk for the payment method used\n2. The age of your account for that payment method\n\nThe account you are creating now is new and its age is zero. As your account ages, your per-trade limits will grow:\n\n● During the 1st month, your per-trade limit will be {0}\n● During the 2nd month, your per-trade limit will be {1}\n● After the 2nd month, your per-trade limit will be {2}\n\nPlease note: limits only apply to trade size. You can place as many trades as you like. -payment.limits.info.withSigning=To limit chargeback risk, Bisq sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing, and increase with account age.\n\nSee more:\nhttps://bisq.wiki/Account_limits\n\nPlease note: limits only apply to trade size. You can place as many trades as you like. +payment.limits.info=请注意,所有银行转账都有一定的退款风险。\n\n为了降低这一风险,Bisq 基于两个因素对每笔交易设置了限制:\n\n1. 使用的付款方法的预估退款风险水平\n2. 您的付款方式的账龄\n\n您现在创建的帐户是新的,它的账龄为零。随着你的账龄增长,你的每笔交易限额也会随之增长:\n\n●在第一个月,您的每笔交易限额为 {0}\n●在第二个月,您的每笔交易限额将为 {1}\n●第二个月后,您的每笔交易限额为 {2}\n\n请注意:限制只应用在单笔交易,你可以尽可能多的进行交易。 +payment.limits.info.withSigning=为了降低这一风险,Bisq 基于两个因素对该付款方式每笔交易设置了限制:\n\n1. 使用的付款方法的预估退款风险水平\n2. 您的付款方式的账龄\n\n这个付款账户还没有被验证,所以他每个交易最多购买{0}。在验证之后,购买限制会以以下规则逐渐增加:\n\n●签署前,以及签署后30天内,您的每笔最大交易将限制为{0}\n●签署后30天,每笔最大交易将限制为{1}\n●签署后60天,每笔最大交易将限制为{2}\n\n出售限制不会被账户验证状态限制,会与随着您的账龄增加\n\n查看更多:\nhttps://bisq.wiki/Account_limits\n\n请注意:限制只应用在单笔交易,你可以尽可能多的进行交易。 payment.cashDeposit.info=请确认您的银行允许您将现金存款汇入他人账户。例如,美国银行和富国银行不再允许此类存款。 -payment.revolut.info=Revolut requires the 'User name' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not has set the ''User name''.\nPlease enter your Revolut ''User name'' to update your account data.\nThis will not affect your account age signing status. -payment.revolut.addUserNameInfo.headLine=Update Revolut account +payment.revolut.info=Revolut 要求使用“用户名”作为帐户 ID,而不是像以往的电话号码或电子邮件。 +payment.account.revolut.addUserNameInfo={0}\n您现有的 Revolut 帐户({1})尚未设置“用户名”。\n请输入您的 Revolut ``用户名''以更新您的帐户数据。\n这不会影响您的账龄验证状态。 +payment.revolut.addUserNameInfo.headLine=更新 Revolut 账户 -payment.usPostalMoneyOrder.info=Trading using US Postal Money Orders (USPMO) on Bisq requires that you understand the following:\n\n- BTC buyers must write the BTC Seller’s name in both the Payer and the Payee’s fields & take a high-resolution photo of the USPMO and envelope with proof of tracking before sending.\n- BTC buyers must send the USPMO to the BTC seller with Delivery Confirmation.\n\nIn the event mediation is necessary, or if there is a trade dispute, you will be required to send the photos to the Bisq mediator or refund agent, together with the USPMO Serial Number, Post Office Number, and dollar amount, so they can verify the details on the US Post Office website.\n\nFailure to provide the required information to the Mediator or Arbitrator will result in losing the dispute case.\n\nIn all dispute cases, the USPMO sender bears 100% of the burden of responsibility in providing evidence/proof to the Mediator or Arbitrator.\n\nIf you do not understand these requirements, do not trade using USPMO on Bisq. +payment.usPostalMoneyOrder.info=在 Bisq 上交易 US Postal Money Orders (USPMO)您必须理解下述条款:\n\n- BTC 买方必须在发送方和收款人字段中都写上 BTC 卖方的名称,并在发送之前对 USPMO 和信封进行高分辨率照片拍照,并带有跟踪证明。\n- BTC 买方必须将 USPMO 连同交货确认书一起发送给 BTC 卖方。\n\n如果需要调解,或有交易纠纷,您将需要将照片连同 USPMO 编号,邮局编号和交易金额一起发送给 Bisq 调解员或退款代理,以便他们进行验证美国邮局网站上的详细信息。\n\n如未能提供要求的交易数据将在纠纷中直接判负\n\n在所有争议案件中,USPMO 发送方在向调解人或仲裁员提供证据/证明时承担 100% 的责任。\n\n如果您不理解这些要求,请不要在 Bisq 上使用 USPMO 进行交易。 payment.f2f.contact=联系方式 payment.f2f.contact.prompt=您希望如何与交易伙伴联系?(电子邮箱、电话号码、…) @@ -2758,7 +2847,7 @@ validation.interacETransfer.invalidAnswer=必须是一个单词,只包含字 validation.inputTooLarge=输入不能大于 {0} validation.inputTooSmall=输入必须大于 {0} validation.inputToBeAtLeast=输入必须至少为 {0} -validation.amountBelowDust=The amount below the dust limit of {0} satoshi is not allowed. +validation.amountBelowDust=不允许低于 {0} 聪的零头限制。 validation.length=长度必须在 {0} 和 {1} 之间 validation.pattern=输入格式必须为:{0} validation.noHexString=输入不是十六进制格式。 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index b7f3b410d8e..fad923f2c7a 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -35,7 +35,7 @@ shared.no=否 shared.iUnderstand=我瞭解 shared.na=N/A shared.shutDown=完全關閉 -shared.reportBug=報告錯誤到 Github issues +shared.reportBug=在 Github 報告錯誤 shared.buyBitcoin=買入比特幣 shared.sellBitcoin=賣出比特幣 shared.buyCurrency=買入 {0} @@ -63,7 +63,7 @@ shared.priceInCurForCur=1 {1} 的 {0} 價格 shared.fixedPriceInCurForCur=1 {1} 的 {0} 的固定價格 shared.amount=數量 shared.txFee=交易手續費 -shared.tradeFee=Trade Fee +shared.tradeFee=交易手續費 shared.buyerSecurityDeposit=買家保證金 shared.sellerSecurityDeposit=賣家保證金 shared.amountWithCur={0} 數量 @@ -102,13 +102,13 @@ shared.nextStep=下一步 shared.selectTradingAccount=選擇交易賬戶 shared.fundFromSavingsWalletButton=從 Bisq 錢包資金劃轉 shared.fundFromExternalWalletButton=從您的外部錢包充值 -shared.openDefaultWalletFailed=開啟預設的比特幣錢包應用程式失敗了。或許您沒有安裝? +shared.openDefaultWalletFailed=開啟預設的比特幣錢包應用程式失敗了。您確定您安裝了嗎? shared.distanceInPercent=與市場價格的差價 % shared.belowInPercent=低於市場價格 % shared.aboveInPercent=高於市場價格 % shared.enterPercentageValue=輸入 % 值 shared.OR=或者 -shared.notEnoughFunds=您的 Bisq 錢包中沒有足夠的資金。\n您需要 {0} 但是您的 Bisq 錢包只有 {1} 。\n\n請從外部比特幣錢包注入資金或在“資金/存款”充值到您的 Bisq 錢包。 +shared.notEnoughFunds=您的 Bisq 錢包中沒有足夠的資金去支付這一交易 需要{0} 您可用餘額為 {1}。\n\n請從外部比特幣錢包注入資金或在“資金/存款”充值到您的 Bisq 錢包。 shared.waitingForFunds=等待資金充值... shared.depositTransactionId=存款交易 ID shared.TheBTCBuyer=BTC 買家 @@ -123,7 +123,7 @@ shared.noDetailsAvailable=沒有可用詳細 shared.notUsedYet=尚未使用 shared.date=日期 shared.sendFundsDetailsWithFee=傳送:{0}\n來自:{1}\n接收地址:{2}\n要求的最低交易費:{3}({4} 聰/byte)\n交易大小:{5} Kb\n\n收款方將收到:{6}\n\n您確定您想要提現嗎? -shared.sendFundsDetailsDust=Bisq 檢測到,該交易將產生一個低於最低零頭閾值的輸出(不被比特幣共識規則所允許)。相反,這些零頭({0}satoshi{1})將被新增到挖礦手續費中。\n\n\n +shared.sendFundsDetailsDust=Bisq 檢測到,該交易將產生一個低於最低零頭閾值的輸出(不被比特幣共識規則所允許)。相反,這些零頭({0}satoshi{1})將被新增到挖礦手續費中。 shared.copyToClipboard=複製到剪貼簿 shared.language=語言 shared.country=國家或地區 @@ -214,8 +214,9 @@ shared.mediator=調解員 shared.arbitrator=仲裁員 shared.refundAgent=仲裁員 shared.refundAgentForSupportStaff=退款助理 -shared.delayedPayoutTxId=退款擔保交易 ID -shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. +shared.delayedPayoutTxId=Delayed payout transaction ID +shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to +shared.unconfirmedTransactionsLimitReached=你現在有過多的未確認交易。請稍後嘗試 #################################################################### @@ -336,15 +337,16 @@ offerbook.offerersAcceptedBankSeats=接受的銀行所在國家(買家):\n offerbook.availableOffers=可用報價 offerbook.filterByCurrency=以貨幣篩選 offerbook.filterByPaymentMethod=以支付方式篩選 -offerbook.timeSinceSigning=自驗證 +offerbook.timeSinceSigning=Signed since offerbook.timeSinceSigning.info=此賬戶已驗證,{0} offerbook.timeSinceSigning.info.arbitrator=由仲裁員驗證,並可以驗證夥伴賬戶 offerbook.timeSinceSigning.info.peer=由對方驗證,等待限制被解除 offerbook.timeSinceSigning.info.peerLimitLifted=由對方驗證,限制被取消 offerbook.timeSinceSigning.info.signer=由對方驗證,並可驗證對方賬戶(限制已取消) -offerbook.timeSinceSigning.info.banned=account was banned +offerbook.timeSinceSigning.info.banned=帳號已被封禁 offerbook.timeSinceSigning.daysSinceSigning={0} 天 offerbook.timeSinceSigning.daysSinceSigning.long=自驗證{0} +offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=當您成功地完成與擁有已驗證付款帳戶的夥伴交易時,您的付款帳戶已驗證。\n{0} 天后,最初的 {1} 的限制解除以及你的賬戶可以驗證其他人的付款賬戶。 offerbook.timeSinceSigning.notSigned=尚未驗證 @@ -354,8 +356,8 @@ shared.notSigned.noNeed=此賬戶類型不適用驗證 offerbook.nrOffers=報價數量:{0} offerbook.volume={0}(最小 - 最大) -offerbook.deposit=Deposit BTC (%) -offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.deposit=BTC 保證金(%) +offerbook.deposit.help=交易雙方均已支付保證金確保這個交易正常進行。這會在交易完成時退還。 offerbook.createOfferToBuy=建立新的報價來買入 {0} offerbook.createOfferToSell=建立新的報價來賣出 {0} @@ -373,10 +375,10 @@ offerbook.deactivateOffer.failed=報價停用失敗:\n{0} offerbook.activateOffer.failed=報價釋出失敗:\n{0} offerbook.withdrawFundsHint=您可以從 {0} 中撤回您支付的資金。 -offerbook.warning.noTradingAccountForCurrency.headline=選擇的貨幣沒有交易賬戶 -offerbook.warning.noTradingAccountForCurrency.msg=您選擇的貨幣沒有交易賬戶。\n您想要用您現有的交易賬戶建立一個報價嗎? -offerbook.warning.noMatchingAccount.headline=沒有匹配的交易賬戶。 -offerbook.warning.noMatchingAccount.msg=為了接受這個報價,你需要用這個付款方式建立一個付款賬戶。\n\n你現在想做這件事嗎? +offerbook.warning.noTradingAccountForCurrency.headline=選擇的貨幣沒有支付賬戶 +offerbook.warning.noTradingAccountForCurrency.msg=您選擇的貨幣還沒有建立支付賬戶。\n\n你想要用其他貨幣建立一個報價嗎? +offerbook.warning.noMatchingAccount.headline=沒有匹配的支付賬戶。 +offerbook.warning.noMatchingAccount.msg=這個報價使用了您未建立過的支付方式。\n\n你現在想要建立一個新的支付賬戶嗎? offerbook.warning.counterpartyTradeRestrictions=由於交易夥伴的交易限制,這個報價不能接受 @@ -393,7 +395,7 @@ offerbook.warning.paymentMethodBanned=該報價中使用的付款方式被 Bisq offerbook.warning.nodeBlocked=該交易者的匿名地址被 Bisq 開發人員限制。\n當獲取來自該交易者的報價,可能有一個未處理的漏洞導致了問題。 offerbook.warning.requireUpdateToNewVersion=您的 Bisq 版本不再相容交易。\n請通過 https://bisq.network/downloads 更新到最新的 Bisq 版本。 offerbook.warning.tradeLimitNotMatching=您的付款帳戶已於 {0} 前建立。您的交易限額基於賬齡,因此不滿足該報價的要求。\n您的交易限額是:{1}\n最小交易金額為:{2}\n您現在不能接受這個報價。\n只要您的賬齡超過 2 個月,此限制就會被移除。 - +offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade. offerbook.info.sellAtMarketPrice=您會以市場價格進行出售(每分鐘更新) offerbook.info.buyAtMarketPrice=您將以市場價格進行購買(每分鐘更新)。 @@ -463,7 +465,7 @@ createOffer.tac=釋出該報價,我同意與滿足該條件的任何交易者 createOffer.currencyForFee=掛單費 createOffer.setDeposit=設定買家的保證金(%) createOffer.setDepositAsBuyer=設定自己作為買家的保證金(%) -createOffer.setDepositForBothTraders=Set both traders' security deposit (%) +createOffer.setDepositForBothTraders=設定雙方的保證金比例(%) createOffer.securityDepositInfo=您的買家的保證金將會是 {0} createOffer.securityDepositInfoAsBuyer=您作為買家的保證金將會是 {0} createOffer.minSecurityDepositUsed=已使用最低買家保證金 @@ -514,7 +516,7 @@ takeOffer.failed.offererOffline=您不能下單,因為賣家已經下線。 takeOffer.warning.connectionToPeerLost=您與賣家失去連線。\n因為太多連線,他或許已經下線或者關掉了與您的連線。\n\n如果您還是能在報價列表中看到他的報價,您可以再次嘗試下單。 takeOffer.error.noFundsLost=\n\n你的錢包裡還沒有錢。 \n請嘗試重啟您的應用程式或者檢查您的網路連線。 -takeOffer.error.feePaid=\n\n +takeOffer.error.feePaid=\n!\n takeOffer.error.depositPublished=\n\n您的保證金轉賬已經發布。\n請嘗試重啟您的應用程式或者檢查您的網路連線。\n如果始終存在問題,請到幫助介面聯絡開發者。 takeOffer.error.payoutPublished=\n\n您的支付轉賬已經發布。\n請嘗試重啟您的應用程式或者檢查您的網路連線。\n如果始終存在問題,請到幫助介面聯絡開發者。 takeOffer.tac=接受該報價,意味著我同意這交易介面中的條件。 @@ -541,7 +543,7 @@ portfolio.tab.history=歷史記錄 portfolio.tab.failed=失敗 portfolio.tab.editOpenOffer=編輯報價 -portfolio.pending.invalidDelayedPayoutTx=Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or the Keybase channel.\n\n{0} +portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase (https://keybase.io/team/bisq) or on the forum (https://bisq.community) for further assistance.\n\nError message: {0} portfolio.pending.step1.waitForConf=等待區塊鏈確認 portfolio.pending.step2_buyer.startPayment=開始付款 @@ -550,6 +552,36 @@ portfolio.pending.step3_buyer.waitPaymentArrived=等待直到付款到達 portfolio.pending.step3_seller.confirmPaymentReceived=確定收到付款 portfolio.pending.step5.completed=完成 +portfolio.pending.step3_seller.autoConf.status.label=自動確認狀態。 +portfolio.pending.autoConf=自動確認 +portfolio.pending.autoConf.blocks=XMR 確認數:{0} / 需求量:{2} +portfolio.pending.autoConf.state.xmr.txKeyReused=交易金鑰已重複使用。請發起糾紛處理。 +portfolio.pending.autoConf.state.confirmations=XMR 確認:{0}/{1} +portfolio.pending.autoConf.state.txNotFound=交易並未在記憶體池中檢索。 +portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=無有效交易 ID / 交易金鑰 +portfolio.pending.autoConf.state.filterDisabledFeature=由開發者禁用 + +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FEATURE_DISABLED=自動確認功能已禁用。{0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.TRADE_LIMIT_EXCEEDED=交易金額超過自動確認金額限制。 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.INVALID_DATA=對等點提供不可用資料。{0} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=支付交易已經發布 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=已發起糾紛。該交易的自動確認已被禁用 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.REQUESTS_STARTED=交易證明申請已經開始 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.PENDING=成功結果:{0}/{1} ;{2} +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.COMPLETED=所有服務都已被證明。 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.ERROR=您請求的服務發生了錯誤。沒有自動確認。 +# suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.FAILED=服務返回失敗。沒有自動確認。 + portfolio.pending.step1.info=存款交易已經發布。\n開始付款之前,{0} 需要等待至少一個區塊鏈確認。 portfolio.pending.step1.warn=保證金交易仍未得到確認。這種情況可能會發生在外部錢包轉賬時使用的交易手續費用較低造成的。 portfolio.pending.step1.openForDispute=保證金交易仍未得到確認。請聯絡調解員協助。 @@ -584,6 +616,7 @@ portfolio.pending.step2_buyer.bank=請到您的線上銀行網頁並支付 {0} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=請通過提供的聯絡人與 BTC 賣家聯絡,並安排會議支付 {0}。\n\n portfolio.pending.step2_buyer.startPaymentUsing=使用 {0} 開始付款 +portfolio.pending.step2_buyer.recipientsAccountData=接受 {0} portfolio.pending.step2_buyer.amountToTransfer=劃轉數量 portfolio.pending.step2_buyer.sellersAddress=賣家的 {0} 地址 portfolio.pending.step2_buyer.buyerAccount=您的付款帳戶將被使用 @@ -602,7 +635,10 @@ portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=有些銀行可能會 portfolio.pending.step2_buyer.confirmStart.headline=確定您已經付款 portfolio.pending.step2_buyer.confirmStart.msg=您是否向您的交易夥伴發起 {0} 付款? portfolio.pending.step2_buyer.confirmStart.yes=是的,我已經開始付款 - +portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=你沒有提供任何付款證明 +portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=您還沒有輸入交易 ID 以及交易金鑰\n\n如果不提供此資料您的交易夥伴無法在收到 XMR 後使用自動確認功能以快速釋放 BTC。\n另外,Bisq 要求 XMR 傳送者在發生糾紛的時候能夠向調解員和仲裁員提供這些資訊。\n更多細節在 Bisq Wiki:https://bisq.wiki/Trading_Monero#Auto-confirming_trades +portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=輸入並不是一個 32 位元組的雜湊值 +portfolio.pending.step2_buyer.confirmStart.warningButton=忽略並繼續 portfolio.pending.step2_seller.waitPayment.headline=等待付款 portfolio.pending.step2_seller.f2fInfo.headline=買家的合同資訊 portfolio.pending.step2_seller.waitPayment.msg=存款交易至少有一個區塊鏈確認。\n您需要等到 BTC 買家開始 {0} 付款。 @@ -654,7 +690,9 @@ portfolio.pending.step3_seller.amountToReceive=接收數量: portfolio.pending.step3_seller.yourAddress=您的 {0} 地址 portfolio.pending.step3_seller.buyersAddress=賣家的 {0} 地址 portfolio.pending.step3_seller.yourAccount=您的交易賬戶 -portfolio.pending.step3_seller.buyersAccount=賣家的交易賬戶 +portfolio.pending.step3_seller.xmrTxHash=交易記錄 ID +portfolio.pending.step3_seller.xmrTxKey=交易金鑰 +portfolio.pending.step3_seller.buyersAccount=買方賬號資料 portfolio.pending.step3_seller.confirmReceipt=確定付款收據 portfolio.pending.step3_seller.buyerStartedPayment=BTC 買家已經開始 {0} 的付款。\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=檢查您的數字貨幣錢包或塊瀏覽器的區塊鏈確認,並確認付款時,您有足夠的塊鏈確認。 @@ -682,7 +720,7 @@ portfolio.pending.step5_buyer.refunded=退還保證金 portfolio.pending.step5_buyer.withdrawBTC=提現您的比特幣 portfolio.pending.step5_buyer.amount=提現數量 portfolio.pending.step5_buyer.withdrawToAddress=提現地址 -portfolio.pending.step5_buyer.moveToBisqWallet=Keep funds in Bisq wallet +portfolio.pending.step5_buyer.moveToBisqWallet=在 Bisq 錢包中保留資金 portfolio.pending.step5_buyer.withdrawExternal=提現到外部錢包 portfolio.pending.step5_buyer.alreadyWithdrawn=您的資金已經提現。\n請檢視交易歷史記錄。 portfolio.pending.step5_buyer.confirmWithdrawal=確定提現請求 @@ -715,8 +753,8 @@ portfolio.pending.openSupportTicket.msg=請僅在緊急情況下使用此功能 portfolio.pending.timeLockNotOver=你必須等到≈{0}(還需等待{1}個區塊)才能提交糾紛。 portfolio.pending.error.depositTxNull=保證金交易無效。沒有有效的保證金交易,你使用建立糾紛。請到“設定/網路資訊”進行 SPV 重新同步。\n \n如需更多幫助,請聯絡 Bisq Keybase 團隊的 Support 頻道。 -portfolio.pending.mediationResult.error.depositTxNull=保證金交易無效。交易被移至失敗交易。 -portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. The trade gets moved to the failed trades section. +portfolio.pending.mediationResult.error.depositTxNull=The deposit transaction is null. You can move the trade to failed trades. +portfolio.pending.mediationResult.error.delayedPayoutTxNull=The delayed payout transaction is null. You can move the trade to failed trades. portfolio.pending.error.depositTxNotConfirmed=保證金交易未確認。未經確認的存款交易不能發起糾紛或仲裁請求。請耐心等待,直到它被確認或進入“設定/網路資訊”進行 SPV 重新同步。\n\n如需更多幫助,請聯絡 Bisq Keybase 團隊的 Support 頻道。 portfolio.pending.notification=通知 @@ -724,7 +762,7 @@ portfolio.pending.notification=通知 portfolio.pending.support.headline.getHelp=需要幫助? portfolio.pending.support.text.getHelp=如果您有任何問題,您可以嘗試在交易聊天中聯絡交易夥伴,或在 https://bisq.community 詢問 Bisq 社群。如果您的問題仍然沒有解決,您可以向調解員取得更多的幫助。 portfolio.pending.support.text.getHelp.arbitrator=如果您有任何問題,您可以嘗試在交易聊天中聯絡交易夥伴,或在 https://bisq.community 詢問 Bisq 社群。如果你的問題仍然沒有解決,你可以向仲裁員取得更多的幫助。 -portfolio.pending.support.button.getHelp=Open Trader Chat +portfolio.pending.support.button.getHelp=開啟交易聊天 portfolio.pending.support.popup.info=如果您與交易的問題仍然沒有解決,您可以提交工單來請求調解員的幫助。如果您還沒有收到付款,請等到交易結束。\n\n您確定要提交工單嗎? portfolio.pending.support.popup.button=建立幫助話題 portfolio.pending.support.headline.halfPeriodOver=確認付款 @@ -753,18 +791,34 @@ portfolio.pending.mediationResult.button=檢視建議的解決方案 portfolio.pending.mediationResult.popup.headline=調解員在交易 ID:{0}上的建議 portfolio.pending.mediationResult.popup.headline.peerAccepted=你的夥伴已經接受了調解員的建議 portfolio.pending.mediationResult.popup.info=調解員建議的支出如下:\n你將支付:{0}\n你的交易夥伴將支付:{1}\n\n你可以接受或拒絕這筆調解費支出。\n\n通過接受,你驗證了合約的支付交易。如果你的交易夥伴也接受和驗證,支付將完成,交易將關閉。\n\n如果你們其中一人或雙方都拒絕該建議,你將必須等到(2)({3}區塊)與仲裁員展開第二輪糾紛討論,仲裁員將再次調查該案件,並根據他們的調查結果進行支付。\n\n仲裁員可以收取少量費用(費用上限:交易的保證金)作為其工作的補償。兩個交易者都同意調解員的建議是愉快的路徑請求仲裁是針對特殊情況的,比如如果一個交易者確信調解員沒有提出公平的賠償建議(或者如果另一個同伴沒有迴應)。\n\n關於新的仲裁模型的更多細節:https://docs.bisq.network/trading-rules.html#arbitration +portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nThe lock time is since {0} (block {1}) over and you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:\nhttps://docs.bisq.network/trading-rules.html#arbitration portfolio.pending.mediationResult.popup.openArbitration=拒絕並請求仲裁 portfolio.pending.mediationResult.popup.alreadyAccepted=您已經接受了。 +portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. +portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: https://github.com/bisq-network/support/issues. \n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: https://github.com/bisq-network/support/issues. +portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0} +portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time. +portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades +portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade +portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades? +portfolio.failed.revertToPending=Move trade to open trades + portfolio.closed.completed=完成 portfolio.closed.ticketClosed=已仲裁 portfolio.closed.mediationTicketClosed=已調解 portfolio.closed.canceled=已取消 portfolio.failed.Failed=失敗 -portfolio.failed.unfail=Before proceeding, make sure you have a backup of your data directory!\nDo you want to move this trade back to open trades?\nThis is a way to unlock funds stuck in a failed trade. -portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at the moment. \nTry again after completion of trade(s) {0} -portfolio.failed.depositTxNull=The trade cannot be completed. Deposit transaction is null -portfolio.failed.delayedPayoutTxNull=The trade cannot be completed. Delayed payout transaction is null +portfolio.failed.unfail=再繼續之前,請保證你有一份根目錄的備份!\n您想要將此交易移至未完成的交易嗎?\n這是一個解鎖卡在失敗交易的資金的方法 +portfolio.failed.cantUnfail=目前該交易暫無法移至未完成的交易。\n請在完成交易後重試{0} +portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. +portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. #################################################################### @@ -797,8 +851,8 @@ funds.withdrawal.feeExcluded=不含挖礦費的金額 funds.withdrawal.feeIncluded=包含挖礦費的金額 funds.withdrawal.fromLabel=從源地址提現 funds.withdrawal.toLabel=提現地址 -funds.withdrawal.memoLabel=Withdrawal memo -funds.withdrawal.memo=Optionally fill memo +funds.withdrawal.memoLabel=提現備註 +funds.withdrawal.memo=可選備註 funds.withdrawal.withdrawButton=選定提現 funds.withdrawal.noFundsAvailable=沒有可用資金提現 funds.withdrawal.confirmWithdrawalRequest=確定提現請求 @@ -835,7 +889,7 @@ funds.tx.noFundsFromDispute=沒有退款的糾紛 funds.tx.receivedFunds=收到的資金: funds.tx.withdrawnFromWallet=從錢包提現 funds.tx.withdrawnFromBSQWallet=BTC 已從 BSQ 錢包中取出 -funds.tx.memo=Memo +funds.tx.memo=備註 funds.tx.noTxAvailable=沒有可用交易 funds.tx.revert=還原 funds.tx.txSent=交易成功傳送到本地 Bisq 錢包中的新地址。 @@ -855,13 +909,24 @@ support.tab.mediation.support=調解 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=歷史仲裁 support.tab.ArbitratorsSupportTickets={0} 的工單 -support.filter=Search disputes +support.filter=查詢糾紛 support.filter.prompt=輸入 交易 ID、日期、洋蔥地址或賬戶資訊 -support.reOpenByTrader.prompt=Are you sure you want to re-open the dispute? -support.reOpenButton.label=Re-open dispute -support.sendNotificationButton.label=傳送私人通知 -support.reportButton.label=Generate report -support.fullReportButton.label=Get text dump of all disputes + +support.sigCheck.button=Verify result +support.sigCheck.popup.info=In case of a reimbursement request to the DAO you need to paste the summary message of the mediation and arbitration process in your reimbursement request on Github. To make this statement verifiable any user can check with this tool if the signature of the mediator or arbitrator matches the summary message. +support.sigCheck.popup.header=Verify dispute result signature +support.sigCheck.popup.msg.label=Summary message +support.sigCheck.popup.msg.prompt=Copy & paste summary message from dispute +support.sigCheck.popup.result=Validation result +support.sigCheck.popup.success=Signature is valid +support.sigCheck.popup.failed=Signature verification failed +support.sigCheck.popup.invalidFormat=Message is not of expected format. Copy & paste summary message from dispute. + +support.reOpenByTrader.prompt=您確定想要重新開啟糾紛? +support.reOpenButton.label=Re-open +support.sendNotificationButton.label=私人通知 +support.reportButton.label=Report +support.fullReportButton.label=All disputes support.noTickets=沒有建立的話題 support.sendingMessage=傳送訊息... support.receiverNotOnline=收件人未線上。訊息被儲存到他們的郵箱。 @@ -903,8 +968,11 @@ support.youOpenedDisputeForMediation=您建立了一個調解請求。\n\n{0}\n\ support.peerOpenedTicket=對方因技術問題請求獲取幫助。\n\n{0}\n\nBisq 版本:{1} support.peerOpenedDispute=對方建立了一個糾紛請求。\n\n{0}\n\nBisq 版本:{1} support.peerOpenedDisputeForMediation=對方建立了一個調解請求。\n\n{0}\n\nBisq 版本:{1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} +support.mediatorsDisputeSummary=系統訊息:\n調解糾紛總結:\n{0} support.mediatorsAddress=仲裁員的節點地址:{0} +support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} +support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? +support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. #################################################################### @@ -916,9 +984,14 @@ settings.tab.about=關於我們 setting.preferences.general=通用偏好 setting.preferences.explorer=比特幣區塊瀏覽器 -setting.preferences.explorer.bsq=BSQ 區塊瀏覽器 +setting.preferences.explorer.bsq=Bisq 區塊瀏覽器 setting.preferences.deviation=與市場價格最大差價 setting.preferences.avoidStandbyMode=避免待機模式 +setting.preferences.autoConfirmXMR=XMR 自動確認 +setting.preferences.autoConfirmEnabled=啟用 +setting.preferences.autoConfirmRequiredConfirmations=已要求確認 +setting.preferences.autoConfirmMaxTradeSize=最大交易量(BTC) +setting.preferences.autoConfirmServiceAddresses=Monero Explorer 連結(使用Tor,但本地主機,LAN IP地址和 *.local 主機名除外) setting.preferences.deviationToLarge=值不允許大於30% setting.preferences.txFee=提現交易費(聰/byte) setting.preferences.useCustomValue=使用自定義值 @@ -949,10 +1022,10 @@ settings.preferences.supportLanguageWarning=如有任何爭議,請注意調解 settings.preferences.selectCurrencyNetwork=選擇網路 setting.preferences.daoOptions=DAO 選項 setting.preferences.dao.resyncFromGenesis.label=從初始 tx 重構 DAO 狀態 -setting.preferences.dao.resyncFromResources.label=Rebuild DAO state from resources -setting.preferences.dao.resyncFromResources.popup=After an application restart the Bisq network governance data will be reloaded from the seed nodes and the BSQ consensus state will be rebuilt from the latest resource files. -setting.preferences.dao.resyncFromGenesis.popup=A resync from genesis transaction can take considerable time and CPU resources. Are you sure you want to do that? Mostly a resync from latest resource files is sufficient and much faster.\n\nIf you proceed, after an application restart the Bisq network governance data will be reloaded from the seed nodes and the BSQ consensus state will be rebuilt from the genesis transaction. -setting.preferences.dao.resyncFromGenesis.resync=Resync from genesis and shutdown +setting.preferences.dao.resyncFromResources.label=從指定資源重新構建 DAO 狀態 +setting.preferences.dao.resyncFromResources.popup=應用程式重新啟動後,Bisq 網路治理資料將從種子節點重新載入,而 BSQ 同步狀態將從創始交易中重新構建。 +setting.preferences.dao.resyncFromGenesis.popup=從創始交易中出現同步會消耗大量時間以及 CPU 資源。您確定要重新同步嗎?通常,從最新資原始檔進行重新同步就足夠了,而且速度更快。\n\n應用程式重新啟動後,Bisq 網路治理資料將從種子節點重新載入,而 BSQ 同步狀態將從初始交易中重新構建。 +setting.preferences.dao.resyncFromGenesis.resync=從創始區塊重新同步並關閉 setting.preferences.dao.isDaoFullNode=以 DAO 全節點執行 Bisq setting.preferences.dao.rpcUser=RPC 使用者名稱 setting.preferences.dao.rpcPw=PRC 密碼 @@ -981,8 +1054,8 @@ settings.net.p2PPeersLabel=已連線節點 settings.net.onionAddressColumn=匿名地址 settings.net.creationDateColumn=已建立連線 settings.net.connectionTypeColumn=入/出 -settings.net.sentDataLabel=Sent data statistics -settings.net.receivedDataLabel=Received data statistics +settings.net.sentDataLabel=統計資料已傳送 +settings.net.receivedDataLabel=統計資料已接收 settings.net.roundTripTimeColumn=延遲 settings.net.sentBytesColumn=傳送 settings.net.receivedBytesColumn=接收 @@ -995,8 +1068,8 @@ settings.net.heightColumn=高度 settings.net.needRestart=您需要重啟應用程式以同意這次變更。\n您需要現在重啟嗎? settings.net.notKnownYet=至今未知... -settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec -settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec +settings.net.sentData=已傳送資料 {0},{1} 條訊息,{2} 條訊息/秒 +settings.net.receivedData=已接收資料 {0},{1} 條訊息,{2} 條訊息/秒 settings.net.ips=新增逗號分隔的 IP 地址及埠,如使用8333埠可不填寫。 settings.net.seedNode=種子節點 settings.net.directPeer=節點(直連) @@ -1018,8 +1091,8 @@ setting.about.support=支援 Bisq setting.about.def=Bisq 不是一個公司,而是一個社群項目,開放參與。如果您想參與或支援 Bisq,請點選下面連線。 setting.about.contribute=貢獻 setting.about.providers=資料提供商 -setting.about.apisWithFee=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices, and Bisq Mempool Nodes for mining fee estimation. -setting.about.apis=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices. +setting.about.apisWithFee=Bisq 使用 Bisq 價格指數來表示法幣與虛擬貨幣的市場價格,並使用 Bisq 記憶體池節點來估算採礦費。 +setting.about.apis=Bisq 使用 Bisq 價格指數來表示法幣與數字貨幣的市場價格。 setting.about.pricesProvided=交易所價格提供商 setting.about.feeEstimation.label=礦工手續費估算提供商 setting.about.versionDetails=版本詳情 @@ -1053,9 +1126,6 @@ setting.about.shortcuts.openEmergencyBsqWalletTool=開啟應急 BSQ 錢包工具 setting.about.shortcuts.showTorLogs=在 DEBUG 與 WARN 之間切換 Tor 日誌等級 -setting.about.shortcuts.showDisputeStatistics=顯示所有糾紛概要 -setting.about.shortcuts.showDisputeStatistics.value=切換至糾紛頁面並按下:{0} - setting.about.shortcuts.manualPayoutTxWindow=開啟視窗手動支付雙重驗證存款交易 setting.about.shortcuts.reRepublishAllGovernanceData=重新推送 DAO 眾議廳資料(包括提案以及投票) @@ -1069,9 +1139,6 @@ setting.about.shortcuts.registerArbitrator.value=切換至賬戶頁面並按下 setting.about.shortcuts.registerMediator=註冊調解員(僅限調解員/仲裁員) setting.about.shortcuts.registerMediator.value=切換至賬戶頁面並按下:{0} -setting.about.shortcuts.reOpenDispute=重新開啟關閉的糾紛(僅限調解員/仲裁員) -setting.about.shortcuts.reOpenDispute.value=選擇關閉的糾紛並按下:{0} - setting.about.shortcuts.openSignPaymentAccountsWindow=開啟賬齡驗證視窗(僅限仲裁員) setting.about.shortcuts.openSignPaymentAccountsWindow.value=切換至仲裁頁面並按下:{0} @@ -1080,9 +1147,10 @@ setting.about.shortcuts.sendAlertMsg=傳送警報或更新訊息(需要許可 setting.about.shortcuts.sendFilter=設定過濾器(需要許可權) setting.about.shortcuts.sendPrivateNotification=傳送私人通知到對等點(需要許可權) -setting.about.shortcuts.sendPrivateNotification.value=開啟在頭像或糾紛中對等點資訊並按下:{0} - +setting.about.shortcuts.sendPrivateNotification.value=點選交易夥伴頭像並按下:{0} 以顯示更多資訊 +setting.info.headline=新 XMR 自動確認功能 +setting.info.msg=當你完成 BTC/XMR 交易時,您可以使用自動確認功能來驗證是否向您的錢包中傳送了正確數量的 XMR,以便 Bisq 可以自動將交易標記為完成,從而使每個人都可以更快地進行交易。\n\n自動確認使用 XMR 傳送方提供的交易金鑰在至少 2 個 XMR 區塊瀏覽器節點上檢查 XMR 交易。在預設情況下,Bisq 使用由 Bisq 貢獻者執行的區塊瀏覽器節點,但是我們建議執行您自己的 XMR 區塊瀏覽器節點以最大程度地保護隱私和安全。\n\n您還可以在``設定''中將每筆交易的最大 BTC 數量設定為自動確認以及所需確認的數量。\n\n在 Bisq Wiki 上檢視更多詳細資訊(包括如何設定自己的區塊瀏覽器節點):https://bisq.wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1090,7 +1158,7 @@ setting.about.shortcuts.sendPrivateNotification.value=開啟在頭像或糾紛 account.tab.arbitratorRegistration=仲裁員註冊 account.tab.mediatorRegistration=調解員註冊 account.tab.refundAgentRegistration=退款助理註冊 -account.tab.signing=Signing +account.tab.signing=驗證中 account.tab.account=賬戶 account.info.headline=歡迎來到 Bisq 賬戶 account.info.msg=在這裡你可以設定交易賬戶的法定貨幣及數字貨幣,選擇仲裁員和備份你的錢包及賬戶資料。\n\n當你開始執行 Bisq 就已經建立了一個空的比特幣錢包。\n\n我們建議你在充值之前寫下你比特幣錢包的還原金鑰(在左邊的列表)和考慮新增密碼。在“資金”選項中管理比特幣存入和提現。\n\n隱私 & 安全:\nBisq 是一個去中心化的交易所 – 意味著您的所有資料都儲存在您的電腦上,沒有伺服器,我們無法訪問您的個人資訊,您的資金,甚至您的 IP 地址。如銀行賬號、數字貨幣、比特幣地址等資料只分享給與您交易的人,以實現您發起的交易(如果有爭議,仲裁員將會看到您的交易資料)。 @@ -1120,7 +1188,7 @@ account.altcoin.popup.wallet.msg=請確保您按照 {1} 網頁上所述使用 {0 account.altcoin.popup.wallet.confirm=我瞭解並確定我知道我需要哪種錢包。 account.altcoin.popup.upx.msg=在 Bisq 上交易 UPX 需要您瞭解並滿足以下要求:\n\n要傳送 UPX ,您需要使用官方的 UPXmA GUI 錢包或啟用 store-tx-info 標誌的 UPXmA CLI 錢包(在新版本中是預設的)。請確保您可以訪問Tx金鑰,因為在糾紛狀態時需要。\nmonero-wallet-cli(使用get_Tx_key命令)\nmonero-wallet-gui:在高階>證明/檢查頁面。\n\n在普通的區塊鏈瀏覽器中,這種交易是不可驗證的。\n\n如有糾紛,你須向仲裁員提供下列資料:\n \n- Tx私鑰\n- 交易雜湊\n- 接收者的公開地址\n\n如未能提供上述資料,或使用不相容的錢包,將會導致糾紛敗訴。如果發生糾紛,UPX 傳送方負責向仲裁員提供 UPX 轉賬的驗證。\n\n不需要支付 ID,只需要普通的公共地址。\n \n如果您對該流程不確定,請訪問 UPXmA Discord 頻道(https://discord.gg/vhdNSrV)或 Telegram 交流群(https://t.me/uplexaOfficial)瞭解更多資訊。\n\n account.altcoin.popup.arq.msg=在 Bisq 上交易 ARQ 需要您瞭解並滿足以下要求:\n\n要傳送 ARQ ,您需要使用官方的 ArQmA GUI 錢包或啟用 store-tx-info 標誌的 ArQmA CLI 錢包(在新版本中是預設的)。請確保您可以訪問Tx金鑰,因為在糾紛狀態時需要。\nmonero-wallet-cli(使用get_Tx_key命令)\nmonero-wallet-gui:在高階>證明/檢查頁面。\n\n在普通的區塊鏈瀏覽器中,這種交易是不可驗證的。\n\n如有糾紛,你須向調解員或仲裁員提供下列資料:\n\n- Tx私鑰\n- 交易雜湊\n- 接收者的公開地址\n\n如未能提供上述資料,或使用不相容的錢包,將會導致糾紛敗訴。如果發生糾紛,ARQ 傳送方負責向調解員或仲裁員提供 ARQ 轉賬的驗證。\n\n不需要交易 ID,只需要普通的公共地址。\n\n如果您對該流程不確定,請訪問 ArQmA Discord 頻道(https://discord.gg/s9BQpJT)或 ArQmA 論壇(https://labs.arqma.com)瞭解更多資訊。 -account.altcoin.popup.xmr.msg=在 Bisq 上交易 XMR 需要你理解並滿足以下要求:\n\n付款證明:由於 Monero 是一種高隱私性貨幣,區塊鏈中不公開一些交易細節,而且,在發生爭議時,調解員或仲裁員需要他們檢查交易是否真的進行了。在 Bisq 中,XMR 交易的傳送方負責在發生爭議時向調解員或仲裁員提供此資訊。為了做到驗證付款,您必須使用錢包傳送 XMR ,錢包提供所需的資訊來證明付款已經完成,其中包括:\n\n- 交易金鑰(Tx 公鑰、Tx 金鑰或 Tx 私鑰)\n- 交易ID(Tx ID 或Tx 雜湊)\n- 目的地地址(收款人地址)\n\n這些資訊可以在官方的 Monero GUI 和 CLI 錢包、MyMonero 和 Exodus (桌面版)以及 Cake Wallet、MyMonero 和 Monerujo(移動版)中找到,地點如下:\n\n- Monero GUI:轉到 交易 標籤\n- Monero CLI:使用命令 get_tx_key TXID 。必須啟用標記 store-tx-info (在新版本中預設啟用)\n- 其他錢包:轉到交易歷史記錄,在傳送的交易中搜索交易金鑰(Tx金鑰或Tx祕密)和目標地址。儲存收件人地址選項必須在 Cake Wallet 設定中啟用。\n\n如果您使用的錢包與上述不同,請確保您可以訪問這三個資訊。由於交易金鑰和目標地址儲存在 Monero 錢包軟體中,無法在 Monero 區塊鏈中恢復,所以在 Bisq 交易完成之前,您不應該刪除或恢復您的 Monero 錢包。如不提供以上資料,將導致糾紛敗訴。\n\n檢查付款:有了這三條資訊,可以通過以下方式來驗證是否有一定數量的 Monero 被髮送到特定地址:\n\n- Monero GUI:將錢包切換到高階模式,進入高階>驗證/檢查>檢查交易\n- Monero CLI:使用命令 check_tx_key TXID TXKEY ADDRESS\n- XMR Tx 驗證工具(https://xmr.llcoins.net/checktx.html)\n- 瀏覽 Monero 官網(https://www.exploremonero.com/receipt)\n\n如果您仍然對此有疑問,請訪問(https://www.getmonero.org/resources/userguides/prove-payment.html)來查詢更多資訊或在 Monero Support subreddit 上提問(https://www.reddit.com/r/monerosupport/)。 +account.altcoin.popup.xmr.msg=在 Bisq 上交易 XMR 需要你理解並滿足以下要求。\n\n如果您出售 XMR,當您在糾紛中您必須要提供下列資訊給調解員或仲裁員:\n- 交易金鑰(Tx 公鑰,Tx金鑰,Tx私鑰)\n- 交易 ID(Tx ID 或 Tx 雜湊)\n- 交易目標地址(接收者地址)\n\n在 wiki 中檢視更多關於 Monero 錢包的資訊:\nhttps://bisq.wiki/Trading_Monero#Proving_payments\n\n如未能提供要求的交易資料將在糾紛中直接判負\n\n還要注意,Bisq 現在提供了自動確認 XMR 交易的功能,以使交易更快,但是您需要在設定中啟用它。\n\n有關自動確認功能的更多資訊,請參見 Wiki:\nhttps://bisq.wiki/Trading_Monero#Auto-confirming_trades # suppress inspection "TrailingSpacesInProperty" account.altcoin.popup.msr.msg=區塊鏈瀏覽器在 Bisq 上交易 XMR 需要您瞭解並滿足以下要求:\n\n傳送MSR時,您需要使用官方的 Masari GUI 錢包、啟用store-tx-info標記的Masari CLI錢包(預設啟用)或Masari 網頁錢包(https://wallet.getmasari.org)。請確保您可以訪問的 tx 金鑰,因為如果發生糾紛這是需要的。\nmonero-wallet-cli(使用get_Tx_key命令)\nmonero-wallet-gui:在高階>證明/檢查頁面。\n\nMasari 網頁錢包(前往 帳戶->交易歷史和檢視您傳送的交易細節)\n\n驗證可以在錢包中完成。\nmonero-wallet-cli:使用命令(check_tx_key)。\nmonero-wallet-gui:在高階>證明/檢查頁面\n驗證可以在區塊瀏覽器中完成\n開啟區塊瀏覽器(https://explorer.getmasari.org),使用搜索欄查詢您的事務雜湊。\n一旦找到交易,滾動到底部的“證明發送”區域,並填寫所需的詳細資訊。\n如有糾紛,你須向調解員或仲裁員提供下列資料:\n- Tx私鑰\n- 交易雜湊\n- 接收者的公開地址\n\n不需要交易 ID,只需要正常的公共地址。\n如未能提供上述資料,或使用不相容的錢包,將會導致糾紛敗訴。如果發生糾紛,XMR 傳送方負責向調解員或仲裁員提供 XMR 轉賬的驗證。\n\n如果您對該流程不確定,請訪問官方的 Masari Discord(https://discord.gg/sMCwMqs)上尋求幫助。 account.altcoin.popup.blur.msg=在 Bisq 上交易 BLUR 需要你瞭解並滿足以下要求:\n\n要傳送匿名資訊你必須使用匿名網路 CLI 或 GUI 錢包。\n如果您正在使用 CLI 錢包,在傳輸傳送後將顯示交易雜湊(tx ID)。您必須儲存此資訊。在傳送傳輸之後,您必須立即使用“get_tx_key”命令來檢索交易私鑰。如果未能執行此步驟,以後可能無法檢索金鑰。\n\n如果您使用 Blur Network GUI 錢包,可以在“歷史”選項卡中方便地找到交易私鑰和交易 ID。傳送後立即定位感興趣的交易。單擊包含交易的框的右下角的“?”符號。您必須儲存此資訊。\n\n如果仲裁是必要的,您必須向調解員或仲裁員提供以下資訊:1.)交易ID,2.)交易私鑰,3.)收件人地址。調解或仲裁程式將使用 BLUR 事務檢視器(https://blur.cash/#tx-viewer)驗證 BLUR 轉賬。\n\n未能向調解員或仲裁員提供必要的資訊將導致敗訴。在所有爭議的情況下,匿名傳送方承擔100%的責任來向調解員或仲裁員核實交易。\n\n如果你不瞭解這些要求,不要在 Bisq 上交易。首先,在 Blur Network Discord 中尋求幫助(https://discord.gg/dMWaqVW)。 @@ -1159,7 +1227,7 @@ account.password.info=使用密碼保護,您需要在將比特幣從錢包中 account.seed.backup.title=備份您的錢包還原金鑰 account.seed.info=請寫下錢包還原金鑰和時間!\n您可以通過還原金鑰和時間在任何時候恢復您的錢包。\n還原金鑰用於 BTC 和 BSQ 錢包。\n\n您應該在一張紙上寫下還原金鑰並且不要儲存它們在您的電腦上。\n請注意還原金鑰並不能代替備份。\n您需要備份完整的應用程式目錄在”賬戶/備份“介面去恢復有效的應用程式狀態和資料。 -account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info. +account.seed.backup.warning=請注意種子詞不能替代備份。您需要為整個應用目錄(在“賬戶/備份”選項卡中)以恢復應用狀態以及資料。\n匯入種子詞僅在緊急情況時才推薦使用。 如果沒有正確備份資料庫檔案和金鑰,該應用程式將無法執行!\n\n在 Bisq wiki 中檢視更多資訊: https://bisq.wiki/Backing_up_application_data account.seed.warn.noPw.msg=您還沒有設定一個可以保護還原金鑰顯示的錢包密碼。\n\n要顯示還原金鑰嗎? account.seed.warn.noPw.yes=是的,不要再問我 account.seed.enterPw=輸入密碼檢視還原金鑰 @@ -1498,7 +1566,7 @@ dao.bond.bondedRoleType.FORUM_ADMIN=論壇管理 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter 管理 # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin +dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase 管理員 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube 管理 # suppress inspection "UnusedProperty" @@ -1518,9 +1586,9 @@ dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=價格節點運營者 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=比特幣節點運營者 # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=市場 API 運營者 +dao.bond.bondedRoleType.MARKETS_OPERATOR=交易所運營者 # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ 瀏覽器運營者 +dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=瀏覽器運營者 # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=移動通知中繼運營者 # suppress inspection "UnusedProperty" @@ -1972,28 +2040,37 @@ disputeSummaryWindow.reason.OTHER=其他 # suppress inspection "UnusedProperty" disputeSummaryWindow.reason.BANK_PROBLEMS=銀行 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.OPTION_TRADE=Option trade +disputeSummaryWindow.reason.OPTION_TRADE=可選交易 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=Seller not responding +disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=賣家未迴應 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=Wrong sender account +disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=錯誤的傳送者賬號 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.PEER_WAS_LATE=Peer was late +disputeSummaryWindow.reason.PEER_WAS_LATE=交易夥伴已超時 # suppress inspection "UnusedProperty" -disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled +disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=交易已穩定 disputeSummaryWindow.summaryNotes=總結說明 disputeSummaryWindow.addSummaryNotes=新增總結說明 disputeSummaryWindow.close.button=關閉話題 -disputeSummaryWindow.close.msg=Ticket closed on {0}\n\nSummary:\nPayout amount for BTC buyer: {1}\nPayout amount for BTC seller: {2}\n\nReason for dispute: {3}\n\nSummary notes:\n{4} -disputeSummaryWindow.close.nextStepsForMediation=\n\n下一個步驟:\n開啟未完成交易,接受或拒絕建議的調解員的建議 -disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\n下一個步驟:\n不需要您採取進一步的行動。如果仲裁員做出了對你有利的裁決,你將在 資金/交易 頁中看到“仲裁退款”交易 + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n + +# Do no change any line break or order of tokens as the structure is used for signature verification +disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3} + +disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator +disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions disputeSummaryWindow.close.closePeer=你也需要關閉交易物件的話題! disputeSummaryWindow.close.txDetails.headline=釋出交易退款 disputeSummaryWindow.close.txDetails.buyer=買方收到{0}在地址:{1} disputeSummaryWindow.close.txDetails.seller=賣方收到{0}在地址:{1} disputeSummaryWindow.close.txDetails=費用:{0}\n{1}{2}交易費:{3}({4}satoshis/byte)\n事務大小:{5} Kb\n\n您確定要釋出此事務嗎? +disputeSummaryWindow.close.noPayout.headline=Close without any payout +disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? + emptyWalletWindow.headline={0} 錢包急救工具 emptyWalletWindow.info=請在緊急情況下使用,如果您無法從 UI 中訪問您的資金。\n\n請注意,使用此工具時,所有未結報價將自動關閉。\n\n在使用此工具之前,請備份您的資料目錄。您可以在“帳戶/備份”中執行此操作。\n\n請報告我們您的問題,並在 Github 或 Bisq 論壇上提交錯誤報告,以便我們可以調查導致問題的原因。 emptyWalletWindow.balance=您的可用錢包餘額 @@ -2013,8 +2090,8 @@ filterWindow.onions=篩選匿名地址(用逗號“,”隔開) filterWindow.accounts=篩選交易賬戶資料:\n格式:逗號分割的 [付款方式ID|資料欄位|值] filterWindow.bannedCurrencies=篩選貨幣程式碼(用逗號“,”隔開) filterWindow.bannedPaymentMethods=篩選支付方式 ID(用逗號“,”隔開) -filterWindow.bannedAccountWitnessSignerPubKeys=Filtered account witness signer pub keys (comma sep. hex of pub keys) -filterWindow.bannedPrivilegedDevPubKeys=Filtered privileged dev pub keys (comma sep. hex of pub keys) +filterWindow.bannedAccountWitnessSignerPubKeys=已過濾的帳戶證人簽名者公鑰(逗號分隔十六進位制公鑰) +filterWindow.bannedPrivilegedDevPubKeys=已過濾的特權開發者公鑰(逗號分隔十六進位制公鑰) filterWindow.arbitrators=篩選後的仲裁人(用逗號“,”隔開的洋蔥地址) filterWindow.mediators=篩選後的調解員(用逗號“,”隔開的洋蔥地址) filterWindow.refundAgents=篩選後的退款助理(用逗號“,”隔開的洋蔥地址) @@ -2023,11 +2100,12 @@ filterWindow.priceRelayNode=篩選後的價格中繼節點(用逗號“,”隔 filterWindow.btcNode=篩選後的比特幣節點(用逗號“,”隔開的地址+埠) filterWindow.preventPublicBtcNetwork=禁止使用公共比特幣網路 filterWindow.disableDao=禁用 DAO +filterWindow.disableAutoConf=禁用自動確認 filterWindow.disableDaoBelowVersion=DAO 最低所需要的版本 filterWindow.disableTradeBelowVersion=交易最低所需要的版本 filterWindow.add=新增篩選 filterWindow.remove=移除篩選 -filterWindow.btcFeeReceiverAddresses=BTC fee receiver addresses +filterWindow.btcFeeReceiverAddresses=比特幣手續費接收地址 offerDetailsWindow.minBtcAmount=最小 BTC 數量 offerDetailsWindow.min=(最小 {0}) @@ -2046,7 +2124,7 @@ offerDetailsWindow.creationDate=建立時間 offerDetailsWindow.makersOnion=賣家的匿名地址 qRCodeWindow.headline=二維碼 -qRCodeWindow.msg=請使用這二維碼從外部錢包來充值您的 Bisq 錢包。 +qRCodeWindow.msg=請使用二維碼從外部錢包充值至 Bisq 錢包 qRCodeWindow.request=付款請求:\n{0} selectDepositTxWindow.headline=選擇糾紛的存款交易 @@ -2073,6 +2151,11 @@ sendPrivateNotificationWindow.send=傳送私人通知 showWalletDataWindow.walletData=錢包資料 showWalletDataWindow.includePrivKeys=包含私鑰 +setXMRTxKeyWindow.headline=證明已傳送 XMR +setXMRTxKeyWindow.note=在下面新增 tx 資訊可以更快的自動確認交易。更多資訊::https://bisq.wiki/Trading_Monero +setXMRTxKeyWindow.txHash=交易 ID (可選) +setXMRTxKeyWindow.txKey=交易金鑰 (可選) + # We do not translate the tac because of the legal nature. We would need translations checked by lawyers # in each language which is too expensive atm. tacWindow.headline=使用者協議 @@ -2085,7 +2168,7 @@ tradeDetailsWindow.disputedPayoutTxId=糾紛支付交易 ID: tradeDetailsWindow.tradeDate=交易時間 tradeDetailsWindow.txFee=礦工手續費 tradeDetailsWindow.tradingPeersOnion=交易夥伴匿名地址 -tradeDetailsWindow.tradingPeersPubKeyHash=Trading peers pubkey hash +tradeDetailsWindow.tradingPeersPubKeyHash=交易夥伴公鑰雜湊值 tradeDetailsWindow.tradeState=交易狀態 tradeDetailsWindow.agentAddresses=仲裁員/調解員 @@ -2147,6 +2230,7 @@ error.closedTradeWithUnconfirmedDepositTx=交易 ID 為 {0} 的已關閉交易 error.closedTradeWithNoDepositTx=交易 ID 為 {0} 的保證金交易已被確認。\n\n請重新啟動應用程式來清理已關閉的交易列表。 popup.warning.walletNotInitialized=錢包至今未初始化 +popup.warning.osxKeyLoggerWarning=由於 MacOS 10.14 及更高版本中的安全措施更加嚴格,因此啟動 Java 應用程式(Bisq 使用Java)會在 MacOS 中引發彈出警告(``Bisq 希望從任何應用程式接收擊鍵'').\n\n為了避免該問題,請開啟“ MacOS 設定”,然後轉到“安全和隱私”->“隱私”->“輸入監視”,然後從右側列表中刪除“ Bisq”。\n\n一旦解決了技術限制(所需的 Java 版本的 Java 打包程式尚未交付),Bisq將升級到新的 Java 版本,以避免該問題。 popup.warning.wrongVersion=您這臺電腦上可能有錯誤的 Bisq 版本。\n您的電腦的架構是:{0}\n您安裝的 Bisq 二進位制檔案是:{1}\n請關閉並重新安裝正確的版本({2})。 popup.warning.incompatibleDB=我們檢測到不相容的資料庫檔案!\n\n那些資料庫檔案與我們當前的程式碼庫不相容:\n{0}\n\n我們對損壞的檔案進行了備份,並將預設值應用於新的資料庫版本。\n\n備份位於:\n{1}/db/backup_of_corrupted_data。\n\n請檢查您是否安裝了最新版本的 Bisq\n您可以下載:\nhttps://bisq.network/downloads\n\n請重新啟動應用程式。 popup.warning.startupFailed.twoInstances=Bisq 已經在執行。 您不能執行兩個 Bisq 例項。 @@ -2169,12 +2253,12 @@ popup.warning.insufficientBtcFundsForBsqTx=你沒有足夠的 BTC 資金支付 popup.warning.bsqChangeBelowDustException=該交易產生的 BSQ 變化輸出低於零頭限制(5.46 BSQ),將被比特幣網路拒絕。\n\n您需要傳送更高的金額以避免更改輸出(例如,通過在您的傳送金額中新增零頭),或者向您的錢包中新增更多的 BSQ 資金,以避免生成零頭輸出。\n\n零頭輸出為 {0}。 popup.warning.btcChangeBelowDustException=該交易建立的更改輸出低於零頭限制(546 聰),將被比特幣網路拒絕。\n\n您需要將零頭新增到傳送量中,以避免生成零頭輸出。\n\n零頭輸出為{0}。 -popup.warning.insufficientBsqFundsForBtcFeePayment=You''ll need more BSQ to do this transaction—the last 5.46 BSQ in your wallet cannot be used to pay trade fees because of dust limits in the Bitcoin protocol.\n\nYou can either buy more BSQ or pay trade fees with BTC.\n\nMissing funds: {0} +popup.warning.insufficientBsqFundsForBtcFeePayment=您需要更多的 BSQ 去完成這筆交易 - 錢包中最後剩餘 5.46 BSQ 將無法用於支付交易手續費因為 BTC 協議中的零頭限制。\n\n你可以購買更多的 BSQ 或用 BTC支付交易手續費\n\n缺少 BSQ 資金:{0} popup.warning.noBsqFundsForBtcFeePayment=您的 BSQ 錢包沒有足夠的資金支付 BSQ 的交易費用。 popup.warning.messageTooLong=您的資訊超過最大允許的大小。請將其分成多個部分發送,或將其上傳到 https://pastebin.com 之類的伺服器。 popup.warning.lockedUpFunds=你已經從一個失敗的交易中凍結了資金。\n凍結餘額:{0}\n存款tx地址:{1}\n交易單號:{2}\n\n請通過選擇待處理交易介面中的交易並點選“alt + o”或“option+ o”開啟幫助話題。 -popup.warning.nodeBanned=其中一個 {0} 節點被禁用。請重新啟動您的應用程式,以確保沒有連線到禁止節點。 +popup.warning.nodeBanned=其中一個 {0} 節點已被禁用 popup.warning.priceRelay=價格傳遞 popup.warning.seed=種子 popup.warning.mandatoryUpdate.trading=請更新到最新的 Bisq 版本。強制更新禁止了舊版本進行交易。更多資訊請訪問 Bisq 論壇。 @@ -2200,6 +2284,7 @@ popup.info.securityDepositInfo=為了確保雙方都遵守交易協議,雙方 popup.info.cashDepositInfo=請確保您在您的地區有一個銀行分行,以便能夠進行現金存款。\n賣方銀行的銀行 ID(BIC/SWIFT)為:{0}。 popup.info.cashDepositInfo.confirm=我確認我可以支付保證金 popup.info.shutDownWithOpenOffers=Bisq 正在被關閉,但仍有公開的報價。\n\n當 Bisq 關閉時,這些提供將不能在 P2P 網路上使用,但是它們將在您下次啟動 Bisq 時重新發布到 P2P 網路上。\n\n為了讓您的報價線上,保持 Bisq 執行,並確保這臺計算機也線上(即,確保它不會進入待機模式…顯示器待機不是問題)。 +popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at https://bisq.wiki/Running_Bisq_on_Qubes popup.privateNotification.headline=重要私人通知! @@ -2245,22 +2330,22 @@ popup.accountSigning.signedByPeer=您的一個付款帳戶已經被交易夥伴 popup.accountSigning.peerLimitLifted=您其中一個帳戶的初始限額已被取消。\n\n{0} popup.accountSigning.peerSigner=您的一個帳戶已足夠成熟,可以驗證其他付款帳戶,您的一個帳戶的初始限額已被取消。\n\n{0} -popup.accountSigning.singleAccountSelect.headline=Select account age witness -popup.accountSigning.singleAccountSelect.description=Search for account age witness. -popup.accountSigning.singleAccountSelect.datePicker=Select point of time for signing -popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age witness -popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash -popup.accountSigning.confirmSingleAccount.button=Sign account age witness -popup.accountSigning.successSingleAccount.description=Witness {0} was signed -popup.accountSigning.successSingleAccount.success.headline=Success -popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0} - -popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys -popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys -popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed -popup.accountSigning.unsignedPubKeys.result.headline=Signing completed -popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys -popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.accountSigning.singleAccountSelect.headline=選擇賬齡證據 +popup.accountSigning.singleAccountSelect.description=尋找賬齡證據 +popup.accountSigning.singleAccountSelect.datePicker=選擇要驗證的帳戶的時間點 +popup.accountSigning.confirmSingleAccount.headline=確認所選賬齡證據 +popup.accountSigning.confirmSingleAccount.selectedHash=已選擇證據雜湊值 +popup.accountSigning.confirmSingleAccount.button=驗證賬齡證據 +popup.accountSigning.successSingleAccount.description=證據 {0} 已被驗證 +popup.accountSigning.successSingleAccount.success.headline=成功 +popup.accountSigning.successSingleAccount.signError=未能成功驗證證據,{0} + +popup.accountSigning.unsignedPubKeys.headline=未驗證公鑰 +popup.accountSigning.unsignedPubKeys.sign=驗證公鑰 +popup.accountSigning.unsignedPubKeys.signed=公鑰已被驗證 +popup.accountSigning.unsignedPubKeys.result.headline=驗證已完成 +popup.accountSigning.unsignedPubKeys.result.signed=已驗證公鑰 +popup.accountSigning.unsignedPubKeys.result.failed=未能驗證公鑰 #################################################################### # Notifications @@ -2337,6 +2422,7 @@ peerInfoIcon.tooltip.age=支付賬戶在 {0} 前建立。 peerInfoIcon.tooltip.unknownAge=支付賬戶賬齡未知。 tooltip.openPopupForDetails=開啟彈出視窗的詳細資訊 +tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information tooltip.openBlockchainForAddress=使用外部區塊鏈瀏覽器開啟地址:{0} tooltip.openBlockchainForTx=使用外部區塊鏈瀏覽器開啟交易:{0} @@ -2361,7 +2447,8 @@ addressTextField.openWallet.failed=開啟預設的比特幣錢包應用程式失 peerInfoIcon.tooltip={0}\n標識:{1} txIdTextField.copyIcon.tooltip=複製交易 ID 到剪貼簿 -txIdTextField.blockExplorerIcon.tooltip=使用外部區塊鏈瀏覽器開啟這個交易 ID +txIdTextField.blockExplorerIcon.tooltip=Open a blockchain explorer with this transaction ID +txIdTextField.missingTx.warning.tooltip=Missing required transaction #################################################################### @@ -2370,7 +2457,7 @@ txIdTextField.blockExplorerIcon.tooltip=使用外部區塊鏈瀏覽器開啟這 navigation.account=“賬戶” navigation.account.walletSeed=“賬戶/錢包金鑰” -navigation.funds.availableForWithdrawal=“資金/提現” +navigation.funds.availableForWithdrawal=\"Funds/Send funds\" navigation.portfolio.myOpenOffers=“資料/未完成報價” navigation.portfolio.pending=“業務/未完成交易” navigation.portfolio.closedTrades=“資料/歷史” @@ -2454,9 +2541,10 @@ seed.warn.walletNotEmpty.msg=你的比特幣錢包不是空的。\n\n在嘗試 seed.warn.walletNotEmpty.restore=無論如何我要恢復 seed.warn.walletNotEmpty.emptyWallet=我先清空我的錢包 seed.warn.notEncryptedAnymore=你的錢包被加密了。\n\n恢復後,錢包將不再加密,您必須設定新的密碼。\n\n你要繼續嗎? -seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date? +seed.warn.walletDateEmpty=由於您尚未指定錢包日期,因此 Bisq 將必須掃描 2013.10.09(BIP39創始日期)之後的區塊鏈。\n\nBIP39 錢包於 Bisq 於 2017.06.28 首次釋出(版本 v0.5)。因此,您可以使用該日期來節省時間。\n\n理想情況下,您應指定建立錢包種子的日期。\n\n\n您確定要繼續而不指定錢包日期嗎? seed.restore.success=新的還原金鑰成功地恢復了錢包。\n\n您需要關閉並重新啟動應用程式。 seed.restore.error=使用還原金鑰恢復錢包時出現錯誤。{0} +seed.restore.openOffers.warn=您有公開報價,如果您從種子詞恢復,則這些報價將被刪除。\n您確定要繼續嗎? #################################################################### @@ -2466,7 +2554,8 @@ seed.restore.error=使用還原金鑰恢復錢包時出現錯誤。{0} payment.account=賬戶 payment.account.no=賬戶編號 payment.account.name=賬戶名稱 -payment.account.userName=User name +payment.account.userName=使用者暱稱 +payment.account.phoneNr=電話號碼 payment.account.owner=賬戶擁有者姓名: payment.account.fullName=全稱(名,中間名,姓) payment.account.state=州/省/地區 @@ -2536,21 +2625,21 @@ payment.accountType=賬戶類型 payment.checking=檢查 payment.savings=儲存 payment.personalId=個人 ID -payment.clearXchange.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle:\nhttps://www.zellepay.com/get-started\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Bisq account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Bisq. +payment.clearXchange.info=Zelle是一項轉賬服務,轉賬到其他銀行做的很好。\n\n1.檢查此頁面以檢視您的銀行是否(以及如何)與 Zelle 合作:\nhttps://www.zellepay.com/get-started\n\n2.特別注意您的轉賬限額-匯款限額因銀行而異,銀行通常分別指定每日,每週和每月的限額。\n\n3.如果您的銀行不能使用 Zelle,您仍然可以通過 Zelle 移動應用程式使用它,但是您的轉賬限額會低得多。\n\n4.您的 Bisq 帳戶上指定的名稱必須與 Zelle/銀行帳戶上的名稱匹配。 \n\n如果您無法按照貿易合同中的規定完成 Zelle 交易,則可能會損失部分(或全部)保證金。\n\n由於 Zelle 的拒付風險較高,因此建議賣家通過電子郵件或 SMS 與未簽名的買家聯絡,以確認買家確實擁有 Bisq 中指定的 Zelle 帳戶。 payment.fasterPayments.newRequirements.info=有些銀行已經開始核實快捷支付收款人的全名。您當前的快捷支付帳戶沒有填寫全名。\n\n請考慮在 Bisq 中重新建立您的快捷支付帳戶,為將來的 {0} 買家提供一個完整的姓名。\n\n重新建立帳戶時,請確保將銀行區號、帳戶編號和帳齡驗證鹽值從舊帳戶複製到新帳戶。這將確保您現有的帳齡和簽名狀態得到保留。\n\n payment.moneyGram.info=使用 MoneyGram 時,BTC 買方必須將授權號碼和收據的照片通過電子郵件傳送給 BTC 賣方。收據必須清楚地顯示賣方的全名、國家或地區、州和金額。買方將在交易過程中顯示賣方的電子郵件。 payment.westernUnion.info=使用 Western Union 時,BTC 買方必須通過電子郵件將 MTCN(運單號)和收據照片傳送給 BTC 賣方。收據上必須清楚地顯示賣方的全名、城市、國家或地區和金額。買方將在交易過程中顯示賣方的電子郵件。 payment.halCash.info=使用 HalCash 時,BTC 買方需要通過手機簡訊向 BTC 賣方傳送 HalCash 程式碼。\n\n請確保不要超過銀行允許您用半現金匯款的最高金額。每次取款的最低金額是 10 歐元,最高金額是 10 歐元。金額是 600 歐元。對於重複取款,每天每個接收者 3000 歐元,每月每個接收者 6000 歐元。請與您的銀行核對這些限額,以確保它們使用與此處所述相同的限額。\n\n提現金額必須是 10 歐元的倍數,因為您不能從 ATM 機提取其他金額。 建立報價和下單螢幕中的 UI 將調整 BTC 金額,使 EUR 金額正確。你不能使用基於市場的價格,因為歐元的數量會隨著價格的變化而變化。\n -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk.\n\nTo mitigate this risk, Bisq sets per-trade limits based on two factors:\n\n1. The estimated level of chargeback risk for the payment method used\n2. The age of your account for that payment method\n\nThe account you are creating now is new and its age is zero. As your account ages, your per-trade limits will grow:\n\n● During the 1st month, your per-trade limit will be {0}\n● During the 2nd month, your per-trade limit will be {1}\n● After the 2nd month, your per-trade limit will be {2}\n\nPlease note: limits only apply to trade size. You can place as many trades as you like. -payment.limits.info.withSigning=To limit chargeback risk, Bisq sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing, and increase with account age.\n\nSee more:\nhttps://bisq.wiki/Account_limits\n\nPlease note: limits only apply to trade size. You can place as many trades as you like. +payment.limits.info=請注意,所有銀行轉賬都有一定的退款風險。\n\n為了降低這一風險,Bisq 基於兩個因素對每筆交易設定了限制:\n\n1. 使用的付款方法的預估退款風險水平\n2. 您的付款方式的賬齡\n\n您現在建立的帳戶是新的,它的賬齡為零。隨著你的賬齡增長,你的每筆交易限額也會隨之增長:\n\n●在第一個月,您的每筆交易限額為 {0}\n●在第二個月,您的每筆交易限額將為 {1}\n●第二個月後,您的每筆交易限額為 {2}\n\n請注意:限制只應用在單筆交易,你可以儘可能多的進行交易。 +payment.limits.info.withSigning=為了降低這一風險,Bisq 基於兩個因素對該付款方式每筆交易設定了限制:\n\n1. 使用的付款方法的預估退款風險水平\n2. 您的付款方式的賬齡\n\n這個付款賬戶還沒有被驗證,所以他每個交易最多購買{0}。在驗證之後,購買限制會以以下規則逐漸增加:\n\n●簽署前,以及簽署後30天內,您的每筆最大交易將限制為{0}\n●簽署後30天,每筆最大交易將限制為{1}\n●簽署後60天,每筆最大交易將限制為{2}\n\n出售限制不會被賬戶驗證狀態限制,會與隨著您的賬齡增加\n\n檢視更多:\nhttps://bisq.wiki/Account_limits\n\n請注意:限制只應用在單筆交易,你可以儘可能多的進行交易。 payment.cashDeposit.info=請確認您的銀行允許您將現金存款匯入他人賬戶。例如,美國銀行和富國銀行不再允許此類存款。 -payment.revolut.info=Revolut requires the 'User name' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not has set the ''User name''.\nPlease enter your Revolut ''User name'' to update your account data.\nThis will not affect your account age signing status. -payment.revolut.addUserNameInfo.headLine=Update Revolut account +payment.revolut.info=Revolut 要求使用“使用者名稱”作為帳戶 ID,而不是像以往的電話號碼或電子郵件。 +payment.account.revolut.addUserNameInfo={0}\n您現有的 Revolut 帳戶({1})尚未設定“使用者名稱”。\n請輸入您的 Revolut ``使用者名稱''以更新您的帳戶資料。\n這不會影響您的賬齡驗證狀態。 +payment.revolut.addUserNameInfo.headLine=更新 Revolut 賬戶 -payment.usPostalMoneyOrder.info=Trading using US Postal Money Orders (USPMO) on Bisq requires that you understand the following:\n\n- BTC buyers must write the BTC Seller’s name in both the Payer and the Payee’s fields & take a high-resolution photo of the USPMO and envelope with proof of tracking before sending.\n- BTC buyers must send the USPMO to the BTC seller with Delivery Confirmation.\n\nIn the event mediation is necessary, or if there is a trade dispute, you will be required to send the photos to the Bisq mediator or refund agent, together with the USPMO Serial Number, Post Office Number, and dollar amount, so they can verify the details on the US Post Office website.\n\nFailure to provide the required information to the Mediator or Arbitrator will result in losing the dispute case.\n\nIn all dispute cases, the USPMO sender bears 100% of the burden of responsibility in providing evidence/proof to the Mediator or Arbitrator.\n\nIf you do not understand these requirements, do not trade using USPMO on Bisq. +payment.usPostalMoneyOrder.info=在 Bisq 上交易 US Postal Money Orders (USPMO)您必須理解下述條款:\n\n- BTC 買方必須在傳送方和收款人欄位中都寫上 BTC 賣方的名稱,並在傳送之前對 USPMO 和信封進行高解析度照片拍照,並帶有跟蹤證明。\n- BTC 買方必須將 USPMO 連同交貨確認書一起傳送給 BTC 賣方。\n\n如果需要調解,或有交易糾紛,您將需要將照片連同 USPMO 編號,郵局編號和交易金額一起傳送給 Bisq 調解員或退款代理,以便他們進行驗證美國郵局網站上的詳細資訊。\n\n如未能提供要求的交易資料將在糾紛中直接判負\n\n在所有爭議案件中,USPMO 傳送方在向調解人或仲裁員提供證據/證明時承擔 100% 的責任。\n\n如果您不理解這些要求,請不要在 Bisq 上使用 USPMO 進行交易。 payment.f2f.contact=聯絡方式 payment.f2f.contact.prompt=您希望如何與交易夥伴聯絡?(電子郵箱、電話號碼、…) @@ -2758,7 +2847,7 @@ validation.interacETransfer.invalidAnswer=必須是一個單詞,只包含字 validation.inputTooLarge=輸入不能大於 {0} validation.inputTooSmall=輸入必須大於 {0} validation.inputToBeAtLeast=輸入必須至少為 {0} -validation.amountBelowDust=The amount below the dust limit of {0} satoshi is not allowed. +validation.amountBelowDust=不允許低於 {0} 聰的零頭限制。 validation.length=長度必須在 {0} 和 {1} 之間 validation.pattern=輸入格式必須為:{0} validation.noHexString=輸入不是十六進位制格式。 From f13a7b164cebf632d198be65aa2f4ad746c993fc Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 14 Sep 2020 14:53:17 -0300 Subject: [PATCH 003/123] Create a P2WPKH keychain for new btc wallets --- core/src/main/java/bisq/core/btc/setup/WalletConfig.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index acc33637023..dea3b7591a9 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -359,9 +359,7 @@ private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean i } protected Wallet createWallet(boolean isBsqWallet) { - // Change preferredOutputScriptType of btc wallet to P2WPKH to start using segwit - // Script.ScriptType preferredOutputScriptType = isBsqWallet ? Script.ScriptType.P2PKH : Script.ScriptType.P2WPKH; - Script.ScriptType preferredOutputScriptType = Script.ScriptType.P2PKH; + Script.ScriptType preferredOutputScriptType = isBsqWallet ? Script.ScriptType.P2PKH : Script.ScriptType.P2WPKH; KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(isBsqWallet); KeyChainGroup.Builder kcgBuilder = KeyChainGroup.builder(params, structure); if (restoreFromSeed != null) { From cd286600697f6cffc76ea9b84b0f6b6d3fe4e041 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 14 Sep 2020 14:53:37 -0300 Subject: [PATCH 004/123] Add a P2WPKH keychain for existing wallets --- core/src/main/java/bisq/core/btc/setup/WalletConfig.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index dea3b7591a9..d190f37170c 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -352,6 +352,14 @@ private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean i wallet = serializer.readWallet(params, extArray, proto); if (shouldReplayWallet) wallet.reset(); + if (!isBsqWallet && BisqKeyChainGroupStructure.BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH.equals(wallet.getActiveKeyChain().getAccountPath())) { + // Btc wallet does not have a native segwit keychain, we should add one. + DeterministicSeed seed = wallet.getKeyChainSeed(); + DeterministicKeyChain nativeSegwitKeyChain = DeterministicKeyChain.builder().seed(seed) + .outputScriptType(Script.ScriptType.P2WPKH) + .accountPath(new BisqKeyChainGroupStructure(isBsqWallet).accountPathFor(Script.ScriptType.P2WPKH)).build(); + wallet.addAndActivateHDChain(nativeSegwitKeyChain); + } } finally { walletStream.close(); } From ced357ce258a1538b592b5f00e703eab12619e1e Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 14 Sep 2020 17:42:31 -0300 Subject: [PATCH 005/123] AddressEntry: Add boolean segwit flag --- .../bisq/core/btc/model/AddressEntry.java | 25 +++++++++++++------ .../bisq/core/btc/model/AddressEntryList.java | 20 +++++++++------ .../core/btc/wallet/BtcWalletService.java | 22 ++++++++-------- proto/src/main/proto/pb.proto | 1 + 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntry.java b/core/src/main/java/bisq/core/btc/model/AddressEntry.java index 3b036b47a43..4e40c5d0a5d 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntry.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntry.java @@ -28,6 +28,7 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.crypto.DeterministicKey; +import org.bitcoinj.script.Script; import java.util.Optional; @@ -74,6 +75,9 @@ public enum Context { private long coinLockedInMultiSig; + @Getter + private boolean segwit; + @Nullable transient private DeterministicKey keyPair; @Nullable @@ -86,18 +90,20 @@ public enum Context { // Constructor, initialization /////////////////////////////////////////////////////////////////////////////////////////// - public AddressEntry(DeterministicKey keyPair, Context context) { - this(keyPair, context, null); + public AddressEntry(DeterministicKey keyPair, Context context, boolean segwit) { + this(keyPair, context, null, segwit); } public AddressEntry(@NotNull DeterministicKey keyPair, Context context, - @Nullable String offerId) { + @Nullable String offerId, + boolean segwit) { this.keyPair = keyPair; this.context = context; this.offerId = offerId; pubKey = keyPair.getPubKey(); pubKeyHash = keyPair.getPubKeyHash(); + this.segwit = segwit; } @@ -109,12 +115,14 @@ private AddressEntry(byte[] pubKey, byte[] pubKeyHash, Context context, @Nullable String offerId, - Coin coinLockedInMultiSig) { + Coin coinLockedInMultiSig, + boolean segwit) { this.pubKey = pubKey; this.pubKeyHash = pubKeyHash; this.context = context; this.offerId = offerId; this.coinLockedInMultiSig = coinLockedInMultiSig.value; + this.segwit = segwit; } public static AddressEntry fromProto(protobuf.AddressEntry proto) { @@ -122,7 +130,8 @@ public static AddressEntry fromProto(protobuf.AddressEntry proto) { proto.getPubKeyHash().toByteArray(), ProtoUtil.enumFromProto(AddressEntry.Context.class, proto.getContext().name()), ProtoUtil.stringOrNullFromProto(proto.getOfferId()), - Coin.valueOf(proto.getCoinLockedInMultiSig())); + Coin.valueOf(proto.getCoinLockedInMultiSig()), + proto.getSegwit()); } @Override @@ -131,7 +140,8 @@ public protobuf.AddressEntry toProtoMessage() { .setPubKey(ByteString.copyFrom(pubKey)) .setPubKeyHash(ByteString.copyFrom(pubKeyHash)) .setContext(protobuf.AddressEntry.Context.valueOf(context.name())) - .setCoinLockedInMultiSig(coinLockedInMultiSig); + .setCoinLockedInMultiSig(coinLockedInMultiSig) + .setSegwit(segwit); Optional.ofNullable(offerId).ifPresent(builder::setOfferId); return builder.build(); } @@ -175,7 +185,7 @@ public String getAddressString() { @Nullable public Address getAddress() { if (address == null && keyPair != null) - address = LegacyAddress.fromKey(Config.baseCurrencyNetworkParameters(), keyPair); + address = Address.fromKey(Config.baseCurrencyNetworkParameters(), keyPair, segwit ? Script.ScriptType.P2WPKH : Script.ScriptType.P2PKH); return address; } @@ -198,6 +208,7 @@ public String toString() { ", context=" + context + ", offerId='" + offerId + '\'' + ", coinLockedInMultiSig=" + coinLockedInMultiSig + + ", segwit=" + segwit + "}"; } } diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java index 4827fbc1621..cdf83a2c30d 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java @@ -26,6 +26,7 @@ import org.bitcoinj.core.Address; import org.bitcoinj.core.LegacyAddress; +import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.script.Script; @@ -35,6 +36,8 @@ import com.google.common.collect.ImmutableList; +import org.apache.commons.lang3.tuple.Pair; + import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -133,7 +136,7 @@ public void onWalletReady(Wallet wallet) { toBeRemoved.forEach(entrySet::remove); } else { // As long the old arbitration domain is not removed from the code base we still support it here. - entrySet.add(new AddressEntry(wallet.freshReceiveKey(), AddressEntry.Context.ARBITRATOR)); + entrySet.add(new AddressEntry(wallet.freshReceiveKey(), AddressEntry.Context.ARBITRATOR, true)); } // In case we restore from seed words and have balance we need to add the relevant addresses to our list. @@ -147,7 +150,7 @@ public void onWalletReady(Wallet wallet) { DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(address); if (key != null) { // Address will be derived from key in getAddress method - entrySet.add(new AddressEntry(key, AddressEntry.Context.AVAILABLE)); + entrySet.add(new AddressEntry(key, AddressEntry.Context.AVAILABLE, address instanceof SegwitAddress)); } }); } @@ -192,7 +195,8 @@ public void addAddressEntry(AddressEntry addressEntry) { public void swapToAvailable(AddressEntry addressEntry) { boolean setChangedByRemove = entrySet.remove(addressEntry); boolean setChangedByAdd = entrySet.add(new AddressEntry(addressEntry.getKeyPair(), - AddressEntry.Context.AVAILABLE)); + AddressEntry.Context.AVAILABLE, + addressEntry.isSegwit())); if (setChangedByRemove || setChangedByAdd) { requestPersistence(); } @@ -202,7 +206,7 @@ public AddressEntry swapAvailableToAddressEntryWithOfferId(AddressEntry addressE AddressEntry.Context context, String offerId) { boolean setChangedByRemove = entrySet.remove(addressEntry); - final AddressEntry newAddressEntry = new AddressEntry(addressEntry.getKeyPair(), context, offerId); + final AddressEntry newAddressEntry = new AddressEntry(addressEntry.getKeyPair(), context, offerId, addressEntry.isSegwit()); boolean setChangedByAdd = entrySet.add(newAddressEntry); if (setChangedByRemove || setChangedByAdd) requestPersistence(); @@ -225,10 +229,10 @@ private void maybeAddNewAddressEntry(Transaction tx) { .map(output -> output.getScriptPubKey().getToAddress(wallet.getNetworkParameters())) .filter(Objects::nonNull) .filter(this::isAddressNotInEntries) - .map(address -> (DeterministicKey) wallet.findKeyFromPubKeyHash(address.getHash(), - Script.ScriptType.P2PKH)) - .filter(Objects::nonNull) - .map(deterministicKey -> new AddressEntry(deterministicKey, AddressEntry.Context.AVAILABLE)) + .map(address -> Pair.of(address, (DeterministicKey) wallet.findKeyFromAddress(address))) + .filter(pair -> pair.getRight() != null) + .map(pair -> new AddressEntry(pair.getRight(), AddressEntry.Context.AVAILABLE, + pair.getLeft() instanceof SegwitAddress)) .forEach(this::addAddressEntry); } diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index b5860e00491..2f0a508803a 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -584,23 +584,13 @@ public AddressEntry getOrCreateAddressEntry(String offerId, AddressEntry.Context if (emptyAvailableAddressEntry.isPresent()) { return addressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); } else { - AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context, offerId); + AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context, offerId, true); addressEntryList.addAddressEntry(entry); return entry; } } } - private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optional addressEntry) { - if (addressEntry.isPresent()) { - return addressEntry.get(); - } else { - AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context); - addressEntryList.addAddressEntry(entry); - return entry; - } - } - public AddressEntry getArbitratorAddressEntry() { AddressEntry.Context context = AddressEntry.Context.ARBITRATOR; Optional addressEntry = getAddressEntryListAsImmutableList().stream() @@ -623,6 +613,16 @@ public void recoverAddressEntry(String offerId, String address, AddressEntry.Con addressEntryList.swapAvailableToAddressEntryWithOfferId(addressEntry, context, offerId)); } + private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optional addressEntry) { + if (addressEntry.isPresent()) { + return addressEntry.get(); + } else { + AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context, true); + addressEntryList.addAddressEntry(entry); + return entry; + } + } + private Optional findAddressEntry(String address, AddressEntry.Context context) { return getAddressEntryListAsImmutableList().stream() .filter(e -> address.equals(e.getAddressString())) diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index e16746c09be..11c32109811 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -1239,6 +1239,7 @@ message AddressEntry { bytes pub_key = 9; bytes pub_key_hash = 10; int64 coin_locked_in_multi_sig = 11; + bool segwit = 12; } message NavigationPath { From e85c66b8103940975ae3562110c5ea50137fc368 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 14 Sep 2020 18:33:08 -0300 Subject: [PATCH 006/123] Stop using LegacyAddress for btc addresses --- .../bisq/core/btc/model/AddressEntryList.java | 9 +++---- .../core/btc/wallet/BtcWalletService.java | 4 +++- .../core/btc/wallet/TradeWalletService.java | 24 +++++++++---------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java index cdf83a2c30d..9b1e09cf765 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java @@ -25,7 +25,6 @@ import com.google.protobuf.Message; import org.bitcoinj.core.Address; -import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey; @@ -110,11 +109,13 @@ public void onWalletReady(Wallet wallet) { if (!entrySet.isEmpty()) { Set toBeRemoved = new HashSet<>(); entrySet.forEach(addressEntry -> { + Script.ScriptType scriptType = addressEntry.isSegwit() ? Script.ScriptType.P2WPKH + : Script.ScriptType.P2PKH; DeterministicKey keyFromPubHash = (DeterministicKey) wallet.findKeyFromPubKeyHash( - addressEntry.getPubKeyHash(), - Script.ScriptType.P2PKH); + addressEntry.getPubKeyHash(), scriptType); if (keyFromPubHash != null) { - Address addressFromKey = LegacyAddress.fromKey(Config.baseCurrencyNetworkParameters(), keyFromPubHash); + Address addressFromKey = Address.fromKey(Config.baseCurrencyNetworkParameters(), keyFromPubHash, + scriptType); // We want to ensure key and address matches in case we have address in entry available already if (addressEntry.getAddress() == null || addressFromKey.equals(addressEntry.getAddress())) { addressEntry.setDeterministicKey(keyFromPubHash); diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 2f0a508803a..c0a336580b7 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -32,8 +32,10 @@ import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; +import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.LegacyAddress; +import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionInput; @@ -998,7 +1000,7 @@ private boolean feeEstimationNotSatisfied(int counter, Transaction tx) { public int getEstimatedFeeTxSize(List outputValues, Coin txFee) throws InsufficientMoneyException, AddressFormatException { Transaction transaction = new Transaction(params); - Address dummyAddress = LegacyAddress.fromKey(params, wallet.currentReceiveKey()); + Address dummyAddress = SegwitAddress.fromKey(params, new ECKey()); outputValues.forEach(outputValue -> transaction.addOutput(outputValue, dummyAddress)); SendRequest sendRequest = SendRequest.forTx(transaction); diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index f1ea7b042cb..48b4ff50ca5 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -36,8 +36,8 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.SignatureDecodeException; import org.bitcoinj.core.Transaction; @@ -153,7 +153,7 @@ public Transaction createBtcTradingFeeTx(Address fundingAddress, Transaction tradingFeeTx = new Transaction(params); SendRequest sendRequest = null; try { - tradingFeeTx.addOutput(tradingFee, LegacyAddress.fromBase58(params, feeReceiverAddress)); + tradingFeeTx.addOutput(tradingFee, Address.fromString(params, feeReceiverAddress)); // the reserved amount we need for the trade we send to our trade reservedForTradeAddress tradingFeeTx.addOutput(reservedFundsForOffer, reservedForTradeAddress); @@ -335,7 +335,7 @@ OUT[0] dummyOutputAmount (inputAmount - tx fee) Transaction dummyTX = new Transaction(params); // The output is just used to get the right inputs and change outputs, so we use an anonymous ECKey, as it will never be used for anything. // We don't care about fee calculation differences between the real tx and that dummy tx as we use a static tx fee. - TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, LegacyAddress.fromKey(params, new ECKey())); + TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, SegwitAddress.fromKey(params, new ECKey())); dummyTX.addOutput(dummyOutput); // Find the needed inputs to pay the output, optionally add 1 change output. @@ -454,7 +454,7 @@ private PreparedDepositTxAndMakerInputs makerCreatesDepositTx(boolean makerIsBuy // First we construct a dummy TX to get the inputs and outputs we want to use for the real deposit tx. // Similar to the way we did in the createTakerDepositTxInputs method. Transaction dummyTx = new Transaction(params); - TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, LegacyAddress.fromKey(params, new ECKey())); + TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, SegwitAddress.fromKey(params, new ECKey())); dummyTx.addOutput(dummyOutput); addAvailableInputsAndChangeOutputs(dummyTx, makerAddress, makerChangeAddress); // Normally we have only 1 input but we support multiple inputs if the user has paid in with several transactions. @@ -516,7 +516,7 @@ private PreparedDepositTxAndMakerInputs makerCreatesDepositTx(boolean makerIsBuy TransactionOutput takerTransactionOutput = null; if (takerChangeOutputValue > 0 && takerChangeAddressString != null) { takerTransactionOutput = new TransactionOutput(params, preparedDepositTx, Coin.valueOf(takerChangeOutputValue), - LegacyAddress.fromBase58(params, takerChangeAddressString)); + Address.fromString(params, takerChangeAddressString)); } if (makerIsBuyer) { @@ -686,7 +686,7 @@ public Transaction createDelayedUnsignedPayoutTx(Transaction depositTx, delayedPayoutTx.addInput(p2SHMultiSigOutput); applyLockTime(lockTime, delayedPayoutTx); Coin outputAmount = p2SHMultiSigOutput.getValue().subtract(minerFee); - delayedPayoutTx.addOutput(outputAmount, LegacyAddress.fromBase58(params, donationAddressString)); + delayedPayoutTx.addOutput(outputAmount, Address.fromString(params, donationAddressString)); WalletService.printTx("Unsigned delayedPayoutTx ToDonationAddress", delayedPayoutTx); WalletService.verifyTransaction(delayedPayoutTx); return delayedPayoutTx; @@ -938,10 +938,10 @@ public Transaction traderSignAndFinalizeDisputedPayoutTx(byte[] depositTxSeriali Transaction payoutTx = new Transaction(params); payoutTx.addInput(p2SHMultiSigOutput); if (buyerPayoutAmount.isPositive()) { - payoutTx.addOutput(buyerPayoutAmount, LegacyAddress.fromBase58(params, buyerAddressString)); + payoutTx.addOutput(buyerPayoutAmount, Address.fromString(params, buyerAddressString)); } if (sellerPayoutAmount.isPositive()) { - payoutTx.addOutput(sellerPayoutAmount, LegacyAddress.fromBase58(params, sellerAddressString)); + payoutTx.addOutput(sellerPayoutAmount, Address.fromString(params, sellerAddressString)); } // take care of sorting! @@ -1001,10 +1001,10 @@ public void emergencySignAndPublishPayoutTxFrom2of2MultiSig(String depositTxHex, payoutTx.addInput(new TransactionInput(params, depositTx, p2SHMultiSigOutputScript.getProgram(), new TransactionOutPoint(params, 0, spendTxHash), msOutput)); if (buyerPayoutAmount.isPositive()) { - payoutTx.addOutput(buyerPayoutAmount, LegacyAddress.fromBase58(params, buyerAddressString)); + payoutTx.addOutput(buyerPayoutAmount, Address.fromString(params, buyerAddressString)); } if (sellerPayoutAmount.isPositive()) { - payoutTx.addOutput(sellerPayoutAmount, LegacyAddress.fromBase58(params, sellerAddressString)); + payoutTx.addOutput(sellerPayoutAmount, Address.fromString(params, sellerAddressString)); } // take care of sorting! @@ -1146,10 +1146,10 @@ private Transaction createPayoutTx(Transaction depositTx, Transaction transaction = new Transaction(params); transaction.addInput(p2SHMultiSigOutput); if (buyerPayoutAmount.isPositive()) { - transaction.addOutput(buyerPayoutAmount, LegacyAddress.fromBase58(params, buyerAddressString)); + transaction.addOutput(buyerPayoutAmount, Address.fromString(params, buyerAddressString)); } if (sellerPayoutAmount.isPositive()) { - transaction.addOutput(sellerPayoutAmount, LegacyAddress.fromBase58(params, sellerAddressString)); + transaction.addOutput(sellerPayoutAmount, Address.fromString(params, sellerAddressString)); } checkArgument(transaction.getOutputs().size() >= 1, "We need at least one output."); return transaction; From 0c7f3456b6561811cc1c5f3a90b39f5ce9a3733b Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 14 Sep 2020 18:44:42 -0300 Subject: [PATCH 007/123] Fix log msg in BtcCoinSelector --- core/src/main/java/bisq/core/btc/wallet/BtcCoinSelector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcCoinSelector.java b/core/src/main/java/bisq/core/btc/wallet/BtcCoinSelector.java index f0df0adb35d..91b0c84c57b 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcCoinSelector.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcCoinSelector.java @@ -64,7 +64,7 @@ protected boolean isTxOutputSpendable(TransactionOutput output) { Address address = WalletService.getAddressFromOutput(output); return addresses.contains(address); } else { - log.warn("transactionOutput.getScriptPubKey() not isSentToAddress or isPayToScriptHash"); + log.warn("transactionOutput.getScriptPubKey() is not P2PKH nor P2SH nor P2WH"); return false; } } From 0f4c66f43d723f597e9646479d3776419681dc7e Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 28 Sep 2020 21:04:58 -0300 Subject: [PATCH 008/123] Comment out segwit BSQ account path Segwit is not used for BSQ --- .../core/btc/setup/BisqKeyChainGroupStructure.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java index 7c7f6a4e3a8..66ec8b7cea2 100644 --- a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java +++ b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java @@ -47,10 +47,11 @@ public class BisqKeyChainGroupStructure implements KeyChainGroupStructure { new ChildNumber(142, true), ChildNumber.ZERO_HARDENED); - public static final ImmutableList BIP44_BSQ_SEGWIT_ACCOUNT_PATH = ImmutableList.of( - new ChildNumber(44, true), - new ChildNumber(142, true), - ChildNumber.ONE_HARDENED); + // We don't use segwit for BSQ + // public static final ImmutableList BIP44_BSQ_SEGWIT_ACCOUNT_PATH = ImmutableList.of( + // new ChildNumber(44, true), + // new ChildNumber(142, true), + // ChildNumber.ONE_HARDENED); private boolean isBsqWallet; @@ -71,7 +72,8 @@ else if (outputScriptType == Script.ScriptType.P2WPKH) if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH) return BIP44_BSQ_NON_SEGWIT_ACCOUNT_PATH; else if (outputScriptType == Script.ScriptType.P2WPKH) - return BIP44_BSQ_SEGWIT_ACCOUNT_PATH; + //return BIP44_BSQ_SEGWIT_ACCOUNT_PATH; + throw new IllegalArgumentException(outputScriptType.toString()); else throw new IllegalArgumentException(outputScriptType.toString()); } From 25515710ab724203690de8d7057d7bbd6903a094 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 28 Sep 2020 21:05:24 -0300 Subject: [PATCH 009/123] TradeWalletService: adapt to segwit wallet --- .../core/btc/wallet/TradeWalletService.java | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index 48b4ff50ca5..d0fbd194607 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -36,6 +36,7 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; +import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Sha256Hash; @@ -44,6 +45,7 @@ import org.bitcoinj.core.TransactionInput; import org.bitcoinj.core.TransactionOutPoint; import org.bitcoinj.core.TransactionOutput; +import org.bitcoinj.core.TransactionWitness; import org.bitcoinj.core.Utils; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.TransactionSignature; @@ -601,6 +603,9 @@ public Transaction takerSignsDepositTx(boolean takerIsSeller, for (int i = 0; i < buyerInputs.size(); i++) { TransactionInput transactionInput = makersDepositTx.getInputs().get(i); depositTx.addInput(getTransactionInput(depositTx, getMakersScriptSigProgram(transactionInput), buyerInputs.get(i))); + if (!TransactionWitness.EMPTY.equals(transactionInput.getWitness())) { + depositTx.getInput(depositTx.getInputs().size()-1).setWitness(transactionInput.getWitness()); + } } // Add seller inputs @@ -665,6 +670,10 @@ public void sellerAsMakerFinalizesDepositTx(Transaction myDepositTx, TransactionInput input = takersDepositTx.getInput(i); Script scriptSig = input.getScriptSig(); myDepositTx.getInput(i).setScriptSig(scriptSig); + TransactionWitness witness = input.getWitness(); + if (!TransactionWitness.EMPTY.equals(witness)) { + myDepositTx.getInput(i).setWitness(witness); + } } WalletService.printTx("sellerAsMakerFinalizesDepositTx", myDepositTx); @@ -1165,13 +1174,27 @@ private void signInput(Transaction transaction, TransactionInput input, int inpu if (sigKey.isEncrypted()) { checkNotNull(aesKey); } - Sha256Hash hash = transaction.hashForSignature(inputIndex, scriptPubKey, Transaction.SigHash.ALL, false); - ECKey.ECDSASignature signature = sigKey.sign(hash, aesKey); - TransactionSignature txSig = new TransactionSignature(signature, Transaction.SigHash.ALL, false); - if (ScriptPattern.isP2PK(scriptPubKey)) { - input.setScriptSig(ScriptBuilder.createInputScript(txSig)); - } else if (ScriptPattern.isP2PKH(scriptPubKey)) { - input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); + + if (ScriptPattern.isP2PK(scriptPubKey) || ScriptPattern.isP2PKH(scriptPubKey)) { + Sha256Hash hash = transaction.hashForSignature(inputIndex, scriptPubKey, Transaction.SigHash.ALL, false); + ECKey.ECDSASignature signature = sigKey.sign(hash, aesKey); + TransactionSignature txSig = new TransactionSignature(signature, Transaction.SigHash.ALL, false); + if (ScriptPattern.isP2PK(scriptPubKey)) { + input.setScriptSig(ScriptBuilder.createInputScript(txSig)); + } else if (ScriptPattern.isP2PKH(scriptPubKey)) { + input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); + } + } else if (ScriptPattern.isP2WPKH(scriptPubKey)) { + // TODO: Consider using this alternative way to build the scriptCode (taken from bitcoinj master) + // Script scriptCode = ScriptBuilder.createP2PKHOutputScript(sigKey) + Script scriptCode = new ScriptBuilder().data( + ScriptBuilder.createOutputScript(LegacyAddress.fromKey(transaction.getParams(), sigKey)).getProgram()) + .build(); + Coin value = input.getValue(); + TransactionSignature txSig = transaction.calculateWitnessSignature(inputIndex, sigKey, scriptCode, value, + Transaction.SigHash.ALL, false); + input.setScriptSig(ScriptBuilder.createEmpty()); + input.setWitness(TransactionWitness.redeemP2WPKH(txSig, sigKey)); } else { throw new SigningException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); } From d8b755794e2d7e0fb2e541a5635d35c138c8ff6f Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 28 Sep 2020 21:05:56 -0300 Subject: [PATCH 010/123] WalletService: adapt to segwit wallet --- .../bisq/core/btc/wallet/WalletService.java | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index b92cdc24103..f17122ac3b3 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -17,6 +17,7 @@ package bisq.core.btc.wallet; +import bisq.core.btc.exceptions.SigningException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.listeners.AddressConfidenceListener; @@ -37,12 +38,14 @@ import org.bitcoinj.core.Context; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; +import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionInput; import org.bitcoinj.core.TransactionOutput; +import org.bitcoinj.core.TransactionWitness; import org.bitcoinj.core.VerificationException; import org.bitcoinj.core.listeners.NewBestBlockListener; import org.bitcoinj.core.listeners.TransactionConfidenceEventListener; @@ -51,6 +54,7 @@ import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.TransactionSignature; import org.bitcoinj.script.Script; +import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptChunk; import org.bitcoinj.script.ScriptException; import org.bitcoinj.script.ScriptPattern; @@ -241,7 +245,7 @@ public static void checkScriptSig(Transaction transaction, int inputIndex) throws TransactionVerificationException { try { checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null"); - input.getScriptSig().correctlySpends(transaction, inputIndex, input.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); + input.getScriptSig().correctlySpends(transaction, inputIndex, input.getWitness(), input.getValue(), input.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); } catch (Throwable t) { t.printStackTrace(); log.error(t.getMessage()); @@ -265,7 +269,7 @@ public static void signTransactionInput(Wallet wallet, // We assume if it's already signed, it's hopefully got a SIGHASH type that will not invalidate when // we sign missing pieces (to check this would require either assuming any signatures are signing // standard output types or a way to get processed signatures out of script execution) - txIn.getScriptSig().correctlySpends(tx, index, txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); + txIn.getScriptSig().correctlySpends(tx, index, txIn.getWitness(), txIn.getValue(), txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", index); return; } catch (ScriptException e) { @@ -288,7 +292,7 @@ public static void signTransactionInput(Wallet wallet, // We assume if it's already signed, it's hopefully got a SIGHASH type that will not invalidate when // we sign missing pieces (to check this would require either assuming any signatures are signing // standard output types or a way to get processed signatures out of script execution) - txIn.getScriptSig().correctlySpends(tx, index, txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); + txIn.getScriptSig().correctlySpends(tx, index, txIn.getWitness(), txIn.getValue(), txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", index); return; } catch (ScriptException e) { @@ -312,14 +316,31 @@ public static void signTransactionInput(Wallet wallet, Script inputScript = txIn.getScriptSig(); byte[] script = redeemData.redeemScript.getProgram(); - try { - TransactionSignature signature = partialTx.calculateSignature(index, key, script, Transaction.SigHash.ALL, false); - inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(), 0); - txIn.setScriptSig(inputScript); - } catch (ECKey.KeyIsEncryptedException e1) { - throw e1; - } catch (ECKey.MissingPrivateKeyException e1) { - log.warn("No private key in keypair for input {}", index); + + if (ScriptPattern.isP2PK(scriptPubKey) || ScriptPattern.isP2PKH(scriptPubKey)) { + try { + TransactionSignature signature = partialTx.calculateSignature(index, key, script, Transaction.SigHash.ALL, false); + inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(), 0); + txIn.setScriptSig(inputScript); + } catch (ECKey.KeyIsEncryptedException e1) { + throw e1; + } catch (ECKey.MissingPrivateKeyException e1) { + log.warn("No private key in keypair for input {}", index); + } + } else if (ScriptPattern.isP2WPKH(scriptPubKey)) { + // TODO: Consider using this alternative way to build the scriptCode (taken from bitcoinj master) + // Script scriptCode = ScriptBuilder.createP2PKHOutputScript(key); + Script scriptCode = new ScriptBuilder().data( + ScriptBuilder.createOutputScript(LegacyAddress.fromKey(tx.getParams(), key)).getProgram()) + .build(); + Coin value = txIn.getValue(); + TransactionSignature txSig = tx.calculateWitnessSignature(index, key, scriptCode, value, + Transaction.SigHash.ALL, false); + txIn.setScriptSig(ScriptBuilder.createEmpty()); + txIn.setWitness(TransactionWitness.redeemP2WPKH(txSig, key)); + } else { + // log.error("Unexpected script type."); + throw new RuntimeException("Unexpected script type."); } } else { log.warn("Missing connected output, assuming input {} is already signed.", index); @@ -592,7 +613,7 @@ public DeterministicKey findKeyFromPubKeyHash(byte[] pubKeyHash) { @Nullable public DeterministicKey findKeyFromPubKey(byte[] pubKey) { - return wallet.getActiveKeyChain().findKeyFromPubKey(pubKey); + return (DeterministicKey) wallet.findKeyFromPubKey(pubKey); } public boolean isEncrypted() { From a3708485f950912d18a9d62c992d35c9e175e90c Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Fri, 25 Sep 2020 02:12:18 -0300 Subject: [PATCH 011/123] New AddressEntry: use different script types Use P2WPKH for AVAILABLE context and P2PKH for the rest. Disable reusing unused AVAILABLE entries until segwit support is mandatory in Bisq. --- .../core/btc/wallet/BtcWalletService.java | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index c0a336580b7..48a477125e0 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -43,6 +43,7 @@ import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.KeyCrypterScrypt; +import org.bitcoinj.script.Script; import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; @@ -578,18 +579,20 @@ public AddressEntry getOrCreateAddressEntry(String offerId, AddressEntry.Context if (addressEntry.isPresent()) { return addressEntry.get(); } else { +// Disable reusing unused AVAILABLE entries until segwit support in mandatory in Bisq // We try to use available and not yet used entries - Optional emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream() - .filter(e -> AddressEntry.Context.AVAILABLE == e.getContext()) - .filter(e -> isAddressUnused(e.getAddress())) - .findAny(); - if (emptyAvailableAddressEntry.isPresent()) { - return addressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); - } else { - AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context, offerId, true); +// Optional emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream() +// .filter(e -> AddressEntry.Context.AVAILABLE == e.getContext()) +// .filter(e -> isAddressUnused(e.getAddress())) +// .findAny(); +// if (emptyAvailableAddressEntry.isPresent()) { +// return addressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); +// } else { + DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2PKH)); + AddressEntry entry = new AddressEntry(key, context, offerId, false); addressEntryList.addAddressEntry(entry); return entry; - } +// } } } @@ -619,7 +622,16 @@ private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optio if (addressEntry.isPresent()) { return addressEntry.get(); } else { - AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context, true); + DeterministicKey key; + boolean segwit; + if (AddressEntry.Context.AVAILABLE.equals(context)) { + key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2WPKH)); + segwit = true; + } else { + key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2PKH)); + segwit = false; + } + AddressEntry entry = new AddressEntry(key, context, segwit); addressEntryList.addAddressEntry(entry); return entry; } From a9cc28f65a701288097fe166d6b68ace0a75574c Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Fri, 25 Sep 2020 02:12:52 -0300 Subject: [PATCH 012/123] AddressEntryList: arbitrator entry use P2PKH --- core/src/main/java/bisq/core/btc/model/AddressEntryList.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java index 9b1e09cf765..c88ebc40fbd 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java @@ -137,7 +137,8 @@ public void onWalletReady(Wallet wallet) { toBeRemoved.forEach(entrySet::remove); } else { // As long the old arbitration domain is not removed from the code base we still support it here. - entrySet.add(new AddressEntry(wallet.freshReceiveKey(), AddressEntry.Context.ARBITRATOR, true)); + DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2PKH)); + entrySet.add(new AddressEntry(key, AddressEntry.Context.ARBITRATOR, false)); } // In case we restore from seed words and have balance we need to add the relevant addresses to our list. From f9f5d92941c49ecc24949c4b20b6b4c70de69729 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Fri, 25 Sep 2020 04:57:13 -0300 Subject: [PATCH 013/123] Add segwit/legacy checbox for address creation --- .../core/btc/wallet/BtcWalletService.java | 16 ++++++------ .../resources/i18n/displayStrings.properties | 1 + .../main/funds/deposit/DepositView.java | 25 ++++++++++++++----- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 48a477125e0..6603342fbff 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -601,16 +601,21 @@ public AddressEntry getArbitratorAddressEntry() { Optional addressEntry = getAddressEntryListAsImmutableList().stream() .filter(e -> context == e.getContext()) .findAny(); - return getOrCreateAddressEntry(context, addressEntry); + return getOrCreateAddressEntry(context, addressEntry, false); } public AddressEntry getFreshAddressEntry() { + return getFreshAddressEntry(true); + } + + public AddressEntry getFreshAddressEntry(boolean segwit) { AddressEntry.Context context = AddressEntry.Context.AVAILABLE; Optional addressEntry = getAddressEntryListAsImmutableList().stream() .filter(e -> context == e.getContext()) .filter(e -> isAddressUnused(e.getAddress())) + .filter(e -> Script.ScriptType.P2WPKH.equals(e.getAddress().getOutputScriptType()) == segwit) .findAny(); - return getOrCreateAddressEntry(context, addressEntry); + return getOrCreateAddressEntry(context, addressEntry, segwit); } public void recoverAddressEntry(String offerId, String address, AddressEntry.Context context) { @@ -618,18 +623,15 @@ public void recoverAddressEntry(String offerId, String address, AddressEntry.Con addressEntryList.swapAvailableToAddressEntryWithOfferId(addressEntry, context, offerId)); } - private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optional addressEntry) { + private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optional addressEntry, boolean segwit) { if (addressEntry.isPresent()) { return addressEntry.get(); } else { DeterministicKey key; - boolean segwit; - if (AddressEntry.Context.AVAILABLE.equals(context)) { + if (segwit) { key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2WPKH)); - segwit = true; } else { key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2PKH)); - segwit = false; } AddressEntry entry = new AddressEntry(key, context, segwit); addressEntryList.addAddressEntry(entry); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 3afb4f273af..32c2813bed9 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -992,6 +992,7 @@ funds.deposit.fundWallet=Fund your wallet funds.deposit.withdrawFromWallet=Send funds from wallet funds.deposit.amount=Amount in BTC (optional) funds.deposit.generateAddress=Generate new address +funds.deposit.generateAddressSegwit=Native segwit format (Bech32) funds.deposit.selectUnused=Please select an unused address from the table above rather than generating a new one. funds.withdrawal.arbitrationFee=Arbitration fee diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java index 3d08c7528ce..1195c069369 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java @@ -41,8 +41,12 @@ import bisq.common.UserThread; import bisq.common.app.DevEnv; +import bisq.common.config.Config; +import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Transaction; import net.glxn.qrgen.QRCode; @@ -54,6 +58,7 @@ import javafx.fxml.FXML; import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; @@ -85,10 +90,7 @@ import org.jetbrains.annotations.NotNull; -import static bisq.desktop.util.FormBuilder.addAddressTextField; -import static bisq.desktop.util.FormBuilder.addButton; -import static bisq.desktop.util.FormBuilder.addInputTextField; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; +import static bisq.desktop.util.FormBuilder.*; @FxmlView public class DepositView extends ActivatableView { @@ -102,6 +104,7 @@ public class DepositView extends ActivatableView { private ImageView qrCodeImageView; private AddressTextField addressTextField; private Button generateNewAddressButton; + private CheckBox generateNewAddressSegwitCheckbox; private TitledGroupBg titledGroupBg; private InputTextField amountTextField; @@ -195,16 +198,26 @@ public void initialize() { addressTextField.setManaged(false); amountTextField.setManaged(false); + generateNewAddressSegwitCheckbox = addCheckBox(gridPane, ++gridRow, + Res.get("funds.deposit.generateAddressSegwit"), -20); + generateNewAddressSegwitCheckbox.setAllowIndeterminate(false); + generateNewAddressSegwitCheckbox.setSelected(true); + GridPane.setColumnIndex(generateNewAddressSegwitCheckbox, 0); + GridPane.setHalignment(generateNewAddressSegwitCheckbox, HPos.LEFT); + generateNewAddressButton = addButton(gridPane, ++gridRow, Res.get("funds.deposit.generateAddress"), -20); GridPane.setColumnIndex(generateNewAddressButton, 0); GridPane.setHalignment(generateNewAddressButton, HPos.LEFT); generateNewAddressButton.setOnAction(event -> { - boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0); + boolean segwit = generateNewAddressSegwitCheckbox.isSelected(); + NetworkParameters params = Config.baseCurrencyNetworkParameters(); + boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0 + && (Address.fromString(params, e.getAddressString()) instanceof SegwitAddress) == segwit); if (hasUnUsedAddress) { new Popup().warning(Res.get("funds.deposit.selectUnused")).show(); } else { - AddressEntry newSavingsAddressEntry = walletService.getFreshAddressEntry(); + AddressEntry newSavingsAddressEntry = walletService.getFreshAddressEntry(segwit); updateList(); observableList.stream() .filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString())) From 1f3c585c350320be1547bb7b47963b385dad736d Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 28 Sep 2020 17:41:11 -0300 Subject: [PATCH 014/123] Serialize tx without segwit This is for backwards compatibility with bisq nodes that have not upgraded to bitcoinj 0.15. --- core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index d0fbd194607..0951706590e 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -1091,7 +1091,7 @@ private RawTransactionInput getRawInputFromTransactionInput(@NotNull Transaction checkNotNull(input.getValue(), "input.getValue() must not be null"); return new RawTransactionInput(input.getOutpoint().getIndex(), - input.getConnectedOutput().getParentTransaction().bitcoinSerialize(), + input.getConnectedOutput().getParentTransaction().bitcoinSerialize(false), input.getValue().value); } From 58afc00282d26fc0d8f92937b4fb955f31a269d4 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 28 Sep 2020 20:17:49 -0300 Subject: [PATCH 015/123] Don't create an extra address at startup --- core/src/main/java/bisq/core/btc/setup/WalletConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index d190f37170c..8146bba6970 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -321,7 +321,7 @@ private Wallet createOrLoadWallet(boolean shouldReplayWallet, File walletFile, b wallet = loadWallet(shouldReplayWallet, walletFile, isBsqWallet); } else { wallet = createWallet(isBsqWallet); - wallet.freshReceiveKey(); + //wallet.freshReceiveKey(); // Currently the only way we can be sure that an extension is aware of its containing wallet is by // deserializing the extension (see WalletExtension#deserializeWalletExtension(Wallet, byte[])) From e2f74f025093ed2bfa71cf5d0b7922691f435484 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 28 Sep 2020 20:18:17 -0300 Subject: [PATCH 016/123] Don't create a wallet address when not needed --- core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 6603342fbff..0e6e2e2b89b 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -985,7 +985,7 @@ public Transaction getFeeEstimationTransactionForMultipleAddresses(Set f counter++; fee = txFeeForWithdrawalPerByte.multiply(txSize); // We use a dummy address for the output - final String dummyReceiver = getFreshAddressEntry().getAddressString(); + final String dummyReceiver = LegacyAddress.fromKey(params, new ECKey()).toBase58(); SendRequest sendRequest = getSendRequestForMultipleAddresses(fromAddresses, dummyReceiver, amount, fee, null, aesKey); wallet.completeTx(sendRequest); tx = sendRequest.tx; From d1aeedd98b9eec683202b6210511fa5ce87cae0d Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 24 Sep 2020 17:44:09 -0300 Subject: [PATCH 017/123] Remove unused WalletService.findKeyFromPubKeyHash() --- core/src/main/java/bisq/core/btc/wallet/WalletService.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index f17122ac3b3..8ad8e01db35 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -606,11 +606,6 @@ public boolean checkAESKey(KeyParameter aesKey) { return wallet.checkAESKey(aesKey); } - @Nullable - public DeterministicKey findKeyFromPubKeyHash(byte[] pubKeyHash) { - return wallet.getActiveKeyChain().findKeyFromPubHash(pubKeyHash); - } - @Nullable public DeterministicKey findKeyFromPubKey(byte[] pubKey) { return (DeterministicKey) wallet.findKeyFromPubKey(pubKey); From 78a2a43d48cea65688e0946c8b8bac3ebac66fc3 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 14 Sep 2020 17:49:53 -0300 Subject: [PATCH 018/123] Remove unused import --- core/src/main/java/bisq/core/btc/model/AddressEntry.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntry.java b/core/src/main/java/bisq/core/btc/model/AddressEntry.java index 4e40c5d0a5d..5348950b391 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntry.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntry.java @@ -26,7 +26,6 @@ import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; -import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.script.Script; From 01455d2b209cc49a4f3688add3e481422c1bb3a2 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 1 Oct 2020 11:54:20 -0300 Subject: [PATCH 019/123] Enable reusing unused AVAILABLE entries --- .../bisq/core/btc/wallet/BtcWalletService.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 0e6e2e2b89b..e638f5a2861 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -579,20 +579,20 @@ public AddressEntry getOrCreateAddressEntry(String offerId, AddressEntry.Context if (addressEntry.isPresent()) { return addressEntry.get(); } else { -// Disable reusing unused AVAILABLE entries until segwit support in mandatory in Bisq // We try to use available and not yet used entries -// Optional emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream() -// .filter(e -> AddressEntry.Context.AVAILABLE == e.getContext()) -// .filter(e -> isAddressUnused(e.getAddress())) -// .findAny(); -// if (emptyAvailableAddressEntry.isPresent()) { -// return addressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); -// } else { + Optional emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream() + .filter(e -> AddressEntry.Context.AVAILABLE == e.getContext()) + .filter(e -> isAddressUnused(e.getAddress())) + .filter(e -> Script.ScriptType.P2PKH.equals(e.getAddress().getOutputScriptType())) + .findAny(); + if (emptyAvailableAddressEntry.isPresent()) { + return addressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); + } else { DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2PKH)); AddressEntry entry = new AddressEntry(key, context, offerId, false); addressEntryList.addAddressEntry(entry); return entry; -// } + } } } From 4a2c0ad75a8b49748b687f7171281d189b333d37 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 1 Oct 2020 13:15:02 -0300 Subject: [PATCH 020/123] Make codacy happy --- core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java | 2 +- core/src/main/java/bisq/core/btc/wallet/WalletService.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index 0951706590e..7422a62e0da 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -604,7 +604,7 @@ public Transaction takerSignsDepositTx(boolean takerIsSeller, TransactionInput transactionInput = makersDepositTx.getInputs().get(i); depositTx.addInput(getTransactionInput(depositTx, getMakersScriptSigProgram(transactionInput), buyerInputs.get(i))); if (!TransactionWitness.EMPTY.equals(transactionInput.getWitness())) { - depositTx.getInput(depositTx.getInputs().size()-1).setWitness(transactionInput.getWitness()); + depositTx.getInput(depositTx.getInputs().size() - 1).setWitness(transactionInput.getWitness()); } } diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index 8ad8e01db35..84005978348 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -17,7 +17,6 @@ package bisq.core.btc.wallet; -import bisq.core.btc.exceptions.SigningException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.listeners.AddressConfidenceListener; From 86ddd06e276f02cd07a137214b851666926acc69 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 15:08:19 -0300 Subject: [PATCH 021/123] Validate AddressEntry.segwit --- core/src/main/java/bisq/core/btc/model/AddressEntry.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntry.java b/core/src/main/java/bisq/core/btc/model/AddressEntry.java index 5348950b391..a3a0387be2a 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntry.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntry.java @@ -97,6 +97,10 @@ public AddressEntry(@NotNull DeterministicKey keyPair, Context context, @Nullable String offerId, boolean segwit) { + if (segwit && (!Context.AVAILABLE.equals(context) || offerId != null)) { + throw new IllegalArgumentException("Segwit addresses are only allowed for " + + "AVAILABLE entries without an offerId"); + } this.keyPair = keyPair; this.context = context; this.offerId = offerId; From b9e404f0e25a8d14fd57eecea1850e97f7f5cc65 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 16:50:14 -0300 Subject: [PATCH 022/123] Make it clear segwit is not used for the trade protocol yet. --- core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index e638f5a2861..3521903a68c 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -579,6 +579,7 @@ public AddressEntry getOrCreateAddressEntry(String offerId, AddressEntry.Context if (addressEntry.isPresent()) { return addressEntry.get(); } else { + // We still use non-segwit addresses for the trade protocol. // We try to use available and not yet used entries Optional emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream() .filter(e -> AddressEntry.Context.AVAILABLE == e.getContext()) From 5524ba3e97d98f7f4559625ee1e83307596f1efe Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 17:05:59 -0300 Subject: [PATCH 023/123] BtcWalletService.getFreshAddressEntry(): code clean up --- .../main/java/bisq/core/btc/wallet/BtcWalletService.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 3521903a68c..04e1cce087b 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -614,7 +614,12 @@ public AddressEntry getFreshAddressEntry(boolean segwit) { Optional addressEntry = getAddressEntryListAsImmutableList().stream() .filter(e -> context == e.getContext()) .filter(e -> isAddressUnused(e.getAddress())) - .filter(e -> Script.ScriptType.P2WPKH.equals(e.getAddress().getOutputScriptType()) == segwit) + .filter(e -> { + boolean isSegwitOutputScriptType = Script.ScriptType.P2WPKH.equals(e.getAddress().getOutputScriptType()); + // We need to ensure that we take only addressEntries which matches our segWit flag + boolean isMatchingOutputScriptType = isSegwitOutputScriptType == segwit; + return isMatchingOutputScriptType; + }) .findAny(); return getOrCreateAddressEntry(context, addressEntry, segwit); } From d1aaf3e4eb9770d6a551001428e2599aa0616f61 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 17:26:06 -0300 Subject: [PATCH 024/123] Construct dummy outputs with LegacyAddress --- .../main/java/bisq/core/btc/wallet/TradeWalletService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index 7422a62e0da..b58c7c9f9e9 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -38,7 +38,6 @@ import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.SignatureDecodeException; import org.bitcoinj.core.Transaction; @@ -337,7 +336,7 @@ OUT[0] dummyOutputAmount (inputAmount - tx fee) Transaction dummyTX = new Transaction(params); // The output is just used to get the right inputs and change outputs, so we use an anonymous ECKey, as it will never be used for anything. // We don't care about fee calculation differences between the real tx and that dummy tx as we use a static tx fee. - TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, SegwitAddress.fromKey(params, new ECKey())); + TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, LegacyAddress.fromKey(params, new ECKey())); dummyTX.addOutput(dummyOutput); // Find the needed inputs to pay the output, optionally add 1 change output. @@ -456,7 +455,7 @@ private PreparedDepositTxAndMakerInputs makerCreatesDepositTx(boolean makerIsBuy // First we construct a dummy TX to get the inputs and outputs we want to use for the real deposit tx. // Similar to the way we did in the createTakerDepositTxInputs method. Transaction dummyTx = new Transaction(params); - TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, SegwitAddress.fromKey(params, new ECKey())); + TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, LegacyAddress.fromKey(params, new ECKey())); dummyTx.addOutput(dummyOutput); addAvailableInputsAndChangeOutputs(dummyTx, makerAddress, makerChangeAddress); // Normally we have only 1 input but we support multiple inputs if the user has paid in with several transactions. From 3554e190842d100d2fb9f111a9219fea75024231 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 17:38:31 -0300 Subject: [PATCH 025/123] setWitness(): Code clean up --- .../core/btc/wallet/TradeWalletService.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index b58c7c9f9e9..eb7b7485c10 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -600,11 +600,13 @@ public Transaction takerSignsDepositTx(boolean takerIsSeller, // Add buyer inputs and apply signature // We grab the signature from the makersDepositTx and apply it to the new tx input for (int i = 0; i < buyerInputs.size(); i++) { - TransactionInput transactionInput = makersDepositTx.getInputs().get(i); - depositTx.addInput(getTransactionInput(depositTx, getMakersScriptSigProgram(transactionInput), buyerInputs.get(i))); - if (!TransactionWitness.EMPTY.equals(transactionInput.getWitness())) { - depositTx.getInput(depositTx.getInputs().size() - 1).setWitness(transactionInput.getWitness()); + TransactionInput makersInput = makersDepositTx.getInputs().get(i); + byte[] makersScriptSigProgram = getMakersScriptSigProgram(makersInput); + TransactionInput input = getTransactionInput(depositTx, makersScriptSigProgram, buyerInputs.get(i)); + if (!TransactionWitness.EMPTY.equals(makersInput.getWitness())) { + input.setWitness(makersInput.getWitness()); } + depositTx.addInput(input); } // Add seller inputs @@ -666,12 +668,13 @@ public void sellerAsMakerFinalizesDepositTx(Transaction myDepositTx, // We add takers signature from his inputs and add it to out tx which was already signed earlier. for (int i = 0; i < numTakersInputs; i++) { - TransactionInput input = takersDepositTx.getInput(i); - Script scriptSig = input.getScriptSig(); - myDepositTx.getInput(i).setScriptSig(scriptSig); - TransactionWitness witness = input.getWitness(); + TransactionInput takersInput = takersDepositTx.getInput(i); + Script takersScriptSig = takersInput.getScriptSig(); + TransactionInput txInput = myDepositTx.getInput(i); + txInput.setScriptSig(takersScriptSig); + TransactionWitness witness = takersInput.getWitness(); if (!TransactionWitness.EMPTY.equals(witness)) { - myDepositTx.getInput(i).setWitness(witness); + txInput.setWitness(witness); } } From 499d7b7d35683b4f0747e9a674887c9329cc08c3 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 17:40:42 -0300 Subject: [PATCH 026/123] Use try-with-resources --- core/src/main/java/bisq/core/btc/setup/WalletConfig.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 8146bba6970..22aaae82fad 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -341,8 +341,7 @@ protected void setupAutoSave(Wallet wallet, File walletFile) { private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean isBsqWallet) throws Exception { Wallet wallet; - FileInputStream walletStream = new FileInputStream(walletFile); - try { + try (FileInputStream walletStream = new FileInputStream(walletFile)) { WalletExtension[] extArray = new WalletExtension[]{}; Protos.Wallet proto = WalletProtobufSerializer.parseToProto(walletStream); final WalletProtobufSerializer serializer; @@ -360,8 +359,6 @@ private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean i .accountPath(new BisqKeyChainGroupStructure(isBsqWallet).accountPathFor(Script.ScriptType.P2WPKH)).build(); wallet.addAndActivateHDChain(nativeSegwitKeyChain); } - } finally { - walletStream.close(); } return wallet; } From 1d82c01670ba2943ef700d2e4116a0f649eb1b46 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 17:49:20 -0300 Subject: [PATCH 027/123] Improve error handling for P2WPKH --- .../bisq/core/btc/wallet/WalletService.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index 84005978348..920df501e82 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -327,16 +327,22 @@ public static void signTransactionInput(Wallet wallet, log.warn("No private key in keypair for input {}", index); } } else if (ScriptPattern.isP2WPKH(scriptPubKey)) { - // TODO: Consider using this alternative way to build the scriptCode (taken from bitcoinj master) - // Script scriptCode = ScriptBuilder.createP2PKHOutputScript(key); - Script scriptCode = new ScriptBuilder().data( - ScriptBuilder.createOutputScript(LegacyAddress.fromKey(tx.getParams(), key)).getProgram()) - .build(); - Coin value = txIn.getValue(); - TransactionSignature txSig = tx.calculateWitnessSignature(index, key, scriptCode, value, - Transaction.SigHash.ALL, false); - txIn.setScriptSig(ScriptBuilder.createEmpty()); - txIn.setWitness(TransactionWitness.redeemP2WPKH(txSig, key)); + try { + // TODO: Consider using this alternative way to build the scriptCode (taken from bitcoinj master) + // Script scriptCode = ScriptBuilder.createP2PKHOutputScript(key); + Script scriptCode = new ScriptBuilder().data( + ScriptBuilder.createOutputScript(LegacyAddress.fromKey(tx.getParams(), key)).getProgram()) + .build(); + Coin value = txIn.getValue(); + TransactionSignature txSig = tx.calculateWitnessSignature(index, key, scriptCode, value, + Transaction.SigHash.ALL, false); + txIn.setScriptSig(ScriptBuilder.createEmpty()); + txIn.setWitness(TransactionWitness.redeemP2WPKH(txSig, key)); + } catch (ECKey.KeyIsEncryptedException e1) { + throw e1; + } catch (ECKey.MissingPrivateKeyException e1) { + log.warn("No private key in keypair for input {}", index); + } } else { // log.error("Unexpected script type."); throw new RuntimeException("Unexpected script type."); From 694446c296466904aa748f45db78b73d854d7d1e Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 5 Oct 2020 17:58:07 -0300 Subject: [PATCH 028/123] Switch back to LegacyAddress for fee estimation --- core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 04e1cce087b..e21ce45b99a 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -35,7 +35,6 @@ import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.LegacyAddress; -import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionInput; @@ -1020,7 +1019,7 @@ private boolean feeEstimationNotSatisfied(int counter, Transaction tx) { public int getEstimatedFeeTxSize(List outputValues, Coin txFee) throws InsufficientMoneyException, AddressFormatException { Transaction transaction = new Transaction(params); - Address dummyAddress = SegwitAddress.fromKey(params, new ECKey()); + Address dummyAddress = LegacyAddress.fromKey(params, new ECKey()); outputValues.forEach(outputValue -> transaction.addOutput(outputValue, dummyAddress)); SendRequest sendRequest = SendRequest.forTx(transaction); From a747e83211492a8d10ef4ce59d3c4cab99074948 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Wed, 7 Oct 2020 16:31:44 -0300 Subject: [PATCH 029/123] Fix add segwit keychain for encrypted wallet --- .../main/java/bisq/core/app/BisqSetup.java | 2 + .../bisq/core/btc/setup/WalletConfig.java | 78 ++++++++++++++----- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 6027f7f892a..3660544f60d 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -389,6 +389,8 @@ private void initWallet() { if (requestWalletPasswordHandler != null) { requestWalletPasswordHandler.accept(aesKey -> { walletsManager.setAesKey(aesKey); + walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(), + aesKey); if (preferences.isResyncSpvRequested()) { if (showFirstPopupIfResyncSPVRequestedHandler != null) showFirstPopupIfResyncSPVRequestedHandler.run(); diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 22aaae82fad..6bf36d82b48 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -27,6 +27,7 @@ import com.google.common.util.concurrent.*; import org.bitcoinj.core.listeners.*; import org.bitcoinj.core.*; +import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.net.BlockingClientManager; import org.bitcoinj.net.discovery.*; import org.bitcoinj.script.Script; @@ -35,6 +36,11 @@ import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; + +import org.bouncycastle.crypto.params.KeyParameter; + import org.slf4j.*; import javax.annotation.*; @@ -103,6 +109,8 @@ public class WalletConfig extends AbstractIdleService { @Getter @Setter private int minBroadcastConnections; + @Getter + private BooleanProperty migratedWalletToSegwit = new SimpleBooleanProperty(false); /** * Creates a new WalletConfig, with a newly created {@link Context}. Files will be stored in the given directory. @@ -293,25 +301,34 @@ protected void startUp() throws Exception { vPeerGroup.addWallet(vBsqWallet); onSetupCompleted(); - Futures.addCallback((ListenableFuture) vPeerGroup.startAsync(), new FutureCallback() { - @Override - public void onSuccess(@Nullable Object result) { - //completeExtensionInitiations(vPeerGroup); - DownloadProgressTracker tracker = downloadListener == null ? new DownloadProgressTracker() : downloadListener; - vPeerGroup.startBlockChainDownload(tracker); - } - - @Override - public void onFailure(Throwable t) { - throw new RuntimeException(t); + if (migratedWalletToSegwit.get()) { + startPeerGroup(); + } else { + migratedWalletToSegwit.addListener((observable, oldValue, newValue) -> startPeerGroup()); + } - } - }, MoreExecutors.directExecutor()); } catch (BlockStoreException e) { throw new IOException(e); } } + private void startPeerGroup() { + Futures.addCallback((ListenableFuture) vPeerGroup.startAsync(), new FutureCallback() { + @Override + public void onSuccess(@Nullable Object result) { + //completeExtensionInitiations(vPeerGroup); + DownloadProgressTracker tracker = downloadListener == null ? new DownloadProgressTracker() : downloadListener; + vPeerGroup.startBlockChainDownload(tracker); + } + + @Override + public void onFailure(Throwable t) { + throw new RuntimeException(t); + + } + }, MoreExecutors.directExecutor()); + } + private Wallet createOrLoadWallet(boolean shouldReplayWallet, File walletFile, boolean isBsqWallet) throws Exception { Wallet wallet; @@ -351,13 +368,8 @@ private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean i wallet = serializer.readWallet(params, extArray, proto); if (shouldReplayWallet) wallet.reset(); - if (!isBsqWallet && BisqKeyChainGroupStructure.BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH.equals(wallet.getActiveKeyChain().getAccountPath())) { - // Btc wallet does not have a native segwit keychain, we should add one. - DeterministicSeed seed = wallet.getKeyChainSeed(); - DeterministicKeyChain nativeSegwitKeyChain = DeterministicKeyChain.builder().seed(seed) - .outputScriptType(Script.ScriptType.P2WPKH) - .accountPath(new BisqKeyChainGroupStructure(isBsqWallet).accountPathFor(Script.ScriptType.P2WPKH)).build(); - wallet.addAndActivateHDChain(nativeSegwitKeyChain); + if (!isBsqWallet) { + maybeAddSegwitKeychain(wallet, null); } } return wallet; @@ -491,4 +503,30 @@ public PeerGroup peerGroup() { public File directory() { return directory; } + + public void maybeAddSegwitKeychain(Wallet wallet, KeyParameter aesKey) { + if (BisqKeyChainGroupStructure.BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH.equals(wallet.getActiveKeyChain().getAccountPath())) { + if (wallet.isEncrypted() && aesKey == null) { + // wait for the aesKey to be set and this method to be invoked again. + return; + } + // Btc wallet does not have a native segwit keychain, we should add one. + DeterministicSeed seed = wallet.getKeyChainSeed(); + if (aesKey != null) { + // If wallet is encrypted, decrypt the seed. + KeyCrypter keyCrypter = wallet.getKeyCrypter(); + seed = seed.decrypt(keyCrypter, DeterministicKeyChain.DEFAULT_PASSPHRASE_FOR_MNEMONIC, aesKey); + } + DeterministicKeyChain nativeSegwitKeyChain = DeterministicKeyChain.builder().seed(seed) + .outputScriptType(Script.ScriptType.P2WPKH) + .accountPath(new BisqKeyChainGroupStructure(false).accountPathFor(Script.ScriptType.P2WPKH)).build(); + if (aesKey != null) { + // If wallet is encrypted, encrypt the new keychain. + KeyCrypter keyCrypter = wallet.getKeyCrypter(); + nativeSegwitKeyChain = nativeSegwitKeyChain.toEncrypted(keyCrypter, aesKey); + } + wallet.addAndActivateHDChain(nativeSegwitKeyChain); + } + migratedWalletToSegwit.set(true); + } } From 417daf56926c9f6a4c65dc0adfad473712ea0ddb Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 8 Oct 2020 15:56:49 -0300 Subject: [PATCH 030/123] Use bitcoinj 0.15.8 (commit a733034) --- build.gradle | 2 +- core/src/main/java/bisq/core/app/WalletAppSetup.java | 2 +- gradle/witness/gradle-witness.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index fe986909e55..56cdb1cc792 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ configure(subprojects) { ext { // in alphabetical order bcVersion = '1.63' - bitcoinjVersion = '44ddbdc' + bitcoinjVersion = 'a733034' btcdCli4jVersion = '27b94333' codecVersion = '1.13' easybindVersion = '1.0.3' diff --git a/core/src/main/java/bisq/core/app/WalletAppSetup.java b/core/src/main/java/bisq/core/app/WalletAppSetup.java index 74049ae6376..c9b276b2948 100644 --- a/core/src/main/java/bisq/core/app/WalletAppSetup.java +++ b/core/src/main/java/bisq/core/app/WalletAppSetup.java @@ -105,7 +105,7 @@ void init(@Nullable Consumer chainFileLockedExceptionHandler, Runnable downloadCompleteHandler, Runnable walletInitializedHandler) { log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}", - VersionMessage.BITCOINJ_VERSION, "44ddbdc"); + VersionMessage.BITCOINJ_VERSION, "a733034"); ObjectProperty walletServiceException = new SimpleObjectProperty<>(); btcInfoBinding = EasyBind.combine(walletsSetup.downloadPercentageProperty(), diff --git a/gradle/witness/gradle-witness.gradle b/gradle/witness/gradle-witness.gradle index 7ca83933227..f73dba9b81b 100644 --- a/gradle/witness/gradle-witness.gradle +++ b/gradle/witness/gradle-witness.gradle @@ -20,7 +20,7 @@ dependencyVerification { 'com.fasterxml.jackson.core:jackson-core:39a74610521d7fb9eb3f437bb8739bbf47f6435be12d17bf954c731a0c6352bb', 'com.fasterxml.jackson.core:jackson-databind:fcf3c2b0c332f5f54604f7e27fa7ee502378a2cc5df6a944bbfae391872c32ff', 'com.github.JesusMcCloud:jtorctl:389d61b1b5a85eb2f23c582c3913ede49f80c9f2b553e4762382c836270e57e5', - 'com.github.bisq-network:bitcoinj:85d609e9bbaa93de0a9ca1ab436f578c14f7cfa1876b50878046d9f624b48a6b', + 'com.github.bisq-network:bitcoinj:b8b6e4b8010f2b8d4aac7141c0809dea6d102c3ff3c06ceba78c2626d531b0af', 'com.github.cd2357.netlayer:tor.external:7c70846d36465279c2664f147a0f2d47202c5d67c6a2075225194779c3fbe122', 'com.github.cd2357.netlayer:tor.native:84b449191d535a3c2187f7f7f3bb9bcb7d1097f07c6bf8c4f2b3331c20107d9a', 'com.github.cd2357.netlayer:tor:ff92e4a7b59d1b480e0427fcfcf3f82a6fd69be68eec91c6360774d599e3c2e0', From 87da2ae349cee0d7733adbfbe290cc38dfc0e657 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 8 Oct 2020 16:22:52 -0300 Subject: [PATCH 031/123] Do a backup of the wallet before segwit migration --- core/src/main/java/bisq/core/btc/setup/WalletConfig.java | 9 +++++++++ core/src/main/java/bisq/core/btc/setup/WalletsSetup.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 6bf36d82b48..b7042e1c740 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -22,6 +22,7 @@ import bisq.core.btc.wallet.BisqRiskAnalysis; import bisq.common.config.Config; +import bisq.common.file.FileUtil; import com.google.common.io.Closeables; import com.google.common.util.concurrent.*; @@ -510,6 +511,14 @@ public void maybeAddSegwitKeychain(Wallet wallet, KeyParameter aesKey) { // wait for the aesKey to be set and this method to be invoked again. return; } + // Do a backup of the wallet + File backup = new File(directory, WalletsSetup.PRE_SEGWIT_WALLET_BACKUP); + try { + FileUtil.copyFile(new File(directory, "bisq_BTC.wallet"), backup); + } catch (IOException e) { + log.error(e.toString(), e); + } + // Btc wallet does not have a native segwit keychain, we should add one. DeterministicSeed seed = wallet.getKeyChainSeed(); if (aesKey != null) { diff --git a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java index 650f9297853..9b1ce934186 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java @@ -109,6 +109,8 @@ @Slf4j public class WalletsSetup { + public static final String PRE_SEGWIT_WALLET_BACKUP = "pre_segwit_bisq_BTC.wallet.backup"; + @Getter public final BooleanProperty walletsSetupFailed = new SimpleBooleanProperty(); @@ -421,6 +423,13 @@ public void clearBackups() { log.error("Could not delete directory " + e.getMessage()); e.printStackTrace(); } + + File segwitBackup = new File(walletDir, PRE_SEGWIT_WALLET_BACKUP); + try { + FileUtil.deleteFileIfExists(segwitBackup); + } catch (IOException e) { + log.error(e.toString(), e); + } } From 261e0ec714027be657ba7d1125ac65bd81b0135f Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 8 Oct 2020 17:26:24 -0300 Subject: [PATCH 032/123] Check migratedWalletToSegwit is true --- core/src/main/java/bisq/core/btc/setup/WalletConfig.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index b7042e1c740..fc8c6a09506 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -305,7 +305,11 @@ protected void startUp() throws Exception { if (migratedWalletToSegwit.get()) { startPeerGroup(); } else { - migratedWalletToSegwit.addListener((observable, oldValue, newValue) -> startPeerGroup()); + migratedWalletToSegwit.addListener((observable, oldValue, newValue) -> { + if (newValue) { + startPeerGroup(); + } + }); } } catch (BlockStoreException e) { From 9404e8fa87cc51073823aa2207c23134c2c1d876 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 09:43:39 -0500 Subject: [PATCH 033/123] Add NO_ADDRESS_PRE_FIX capability. Add `boolean contains(Capability capability)` method --- common/src/main/java/bisq/common/app/Capabilities.java | 4 ++++ common/src/main/java/bisq/common/app/Capability.java | 3 ++- .../main/java/bisq/core/setup/CoreNetworkCapabilities.java | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/bisq/common/app/Capabilities.java b/common/src/main/java/bisq/common/app/Capabilities.java index 25913c9b0a7..bf95c15c48d 100644 --- a/common/src/main/java/bisq/common/app/Capabilities.java +++ b/common/src/main/java/bisq/common/app/Capabilities.java @@ -98,6 +98,10 @@ public boolean containsAll(Capability... capabilities) { return this.capabilities.containsAll(Arrays.asList(capabilities)); } + public boolean contains(Capability capability) { + return this.capabilities.contains(capability); + } + public boolean isEmpty() { return capabilities.isEmpty(); } diff --git a/common/src/main/java/bisq/common/app/Capability.java b/common/src/main/java/bisq/common/app/Capability.java index 33040bbfa40..9379c9dd392 100644 --- a/common/src/main/java/bisq/common/app/Capability.java +++ b/common/src/main/java/bisq/common/app/Capability.java @@ -40,5 +40,6 @@ public enum Capability { SIGNED_ACCOUNT_AGE_WITNESS, // Supports the signed account age witness feature MEDIATION, // Supports mediation feature REFUND_AGENT, // Supports refund agents - TRADE_STATISTICS_HASH_UPDATE // We changed the hash method in 1.2.0 and that requires update to 1.2.2 for handling it correctly, otherwise the seed nodes have to process too much data. + TRADE_STATISTICS_HASH_UPDATE, // We changed the hash method in 1.2.0 and that requires update to 1.2.2 for handling it correctly, otherwise the seed nodes have to process too much data. + NO_ADDRESS_PRE_FIX // At 1.4.0 we removed the prefix filter for mailbox messages. If a peer has that capability we do not sent the prefix. } diff --git a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java index fcd03280337..fd951d98592 100644 --- a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java +++ b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java @@ -39,7 +39,8 @@ static void setSupportedCapabilities(Config config) { Capability.MEDIATION, Capability.SIGNED_ACCOUNT_AGE_WITNESS, Capability.REFUND_AGENT, - Capability.TRADE_STATISTICS_HASH_UPDATE + Capability.TRADE_STATISTICS_HASH_UPDATE, + Capability.NO_ADDRESS_PRE_FIX ); if (config.daoActivated) { From e73a4b4ae44529816407838c70ee79ea704dd138 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 09:44:13 -0500 Subject: [PATCH 034/123] Cleanups Remove debug log, remove annotation --- .../bisq/network/p2p/DecryptedDirectMessageListener.java | 2 +- .../main/java/bisq/network/p2p/storage/P2PDataStorage.java | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/DecryptedDirectMessageListener.java b/p2p/src/main/java/bisq/network/p2p/DecryptedDirectMessageListener.java index c3eb61e7b41..2747bcedb27 100644 --- a/p2p/src/main/java/bisq/network/p2p/DecryptedDirectMessageListener.java +++ b/p2p/src/main/java/bisq/network/p2p/DecryptedDirectMessageListener.java @@ -19,5 +19,5 @@ public interface DecryptedDirectMessageListener { - void onDirectMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, @SuppressWarnings("UnusedParameters") NodeAddress peerNodeAddress); + void onDirectMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, NodeAddress peerNodeAddress); } diff --git a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java index f412e5146a4..7b9e3714cea 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java @@ -637,12 +637,8 @@ private boolean addProtectedStorageEntry(ProtectedStorageEntry protectedStorageE // To avoid that expired data get stored and broadcast we check early for expire date. if (protectedStorageEntry.isExpired(clock)) { String peer = sender != null ? sender.getFullAddress() : "sender is null"; - log.warn("We received an expired protectedStorageEntry from peer {}. ProtectedStoragePayload={}", + log.debug("We received an expired protectedStorageEntry from peer {}. ProtectedStoragePayload={}", peer, protectedStorageEntry.getProtectedStoragePayload().getClass().getSimpleName()); - log.debug("Expired protectedStorageEntry from peer {}. getCreationTimeStamp={}, protectedStorageEntry={}", - peer, - new Date(protectedStorageEntry.getCreationTimeStamp()), - protectedStorageEntry); return false; } From 6f7dfcf4efcdbdd42d2ac56fa1618a4df2b2bed9 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 11:02:20 -0500 Subject: [PATCH 035/123] Make onRemoved default in interface --- .../bisq/network/p2p/storage/HashMapChangedListener.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/storage/HashMapChangedListener.java b/p2p/src/main/java/bisq/network/p2p/storage/HashMapChangedListener.java index ce483889703..b225b25f553 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/HashMapChangedListener.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/HashMapChangedListener.java @@ -24,6 +24,7 @@ public interface HashMapChangedListener { void onAdded(Collection protectedStorageEntries); - @SuppressWarnings("UnusedParameters") - void onRemoved(Collection protectedStorageEntries); + default void onRemoved(Collection protectedStorageEntries) { + // Often we are only interested in added data as there is no use case for remove + } } From 40f9cfb7c59a448067843717afdb44224edfdadb Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 11:49:47 -0500 Subject: [PATCH 036/123] Add methods for getting peers capabilities --- .../java/bisq/network/p2p/peers/PeerManager.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index e8cf818336d..f12c95b9fd7 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -32,6 +32,7 @@ import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.config.Config; import bisq.common.persistence.PersistenceManager; import bisq.common.proto.persistable.PersistedDataHost; @@ -206,6 +207,21 @@ private void setConnectionLimits(int maxConnections) { } + public boolean peerHasCapability(NodeAddress peersNodeAddress, Capability capability) { + return findPeersCapabilities(peersNodeAddress) + .map(capabilities -> capabilities.contains(capability)) + .orElse(false); + } + + // TODO persist Capabilities + public Optional findPeersCapabilities(NodeAddress peersNodeAddress) { + return networkNode.getConfirmedConnections().stream() + .filter(c -> c.getPeersNodeAddressProperty().get() != null) + .filter(c -> c.getPeersNodeAddressProperty().get().equals(peersNodeAddress)) + .map(Connection::getCapabilities) + .findAny(); + } + /////////////////////////////////////////////////////////////////////////////////////////// // ConnectionListener implementation /////////////////////////////////////////////////////////////////////////////////////////// From 8aec30615922f368e98904fa9c9370cad141876c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 11:52:17 -0500 Subject: [PATCH 037/123] Remove verification for address prefix Set address prefix to empty bytes in case we know that peer has capability (updated version) Batch process mailbox messages in a thread. Refactor handling of mailbox messages --- .../java/bisq/network/p2p/P2PService.java | 262 ++++++++++-------- .../p2p/PrefixedSealedAndSignedMessage.java | 4 + 2 files changed, 149 insertions(+), 117 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 4d2a5ef9025..9abde7bf16d 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -46,6 +46,7 @@ import bisq.network.p2p.storage.payload.ProtectedStoragePayload; import bisq.common.UserThread; +import bisq.common.app.Capability; import bisq.common.crypto.CryptoException; import bisq.common.crypto.KeyRing; import bisq.common.crypto.PubKeyRing; @@ -53,12 +54,15 @@ import bisq.common.proto.network.NetworkEnvelope; import bisq.common.proto.persistable.PersistedDataHost; import bisq.common.util.Tuple2; +import bisq.common.util.Utilities; import com.google.inject.Inject; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; @@ -74,16 +78,20 @@ import java.security.PublicKey; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -164,7 +172,6 @@ public P2PService(NetworkNode networkNode, this.networkNode.addConnectionListener(this); this.networkNode.addMessageListener(this); - this.p2PDataStorage.addHashMapChangedListener(this); this.requestDataManager.addListener(this); // We need to have both the initial data delivered and the hidden service published @@ -197,13 +204,11 @@ public void start(@Nullable P2PServiceListener listener) { public void onAllServicesInitialized() { if (networkNode.getNodeAddress() != null) { - maybeProcessAllMailboxEntries(); myNodeAddress = networkNode.getNodeAddress(); } else { // If our HS is still not published networkNode.nodeAddressProperty().addListener((observable, oldValue, newValue) -> { if (newValue != null) { - maybeProcessAllMailboxEntries(); myNodeAddress = networkNode.getNodeAddress(); } }); @@ -284,7 +289,8 @@ public void onTorNodeReady() { if (!seedNodesAvailable) { isBootstrapped = true; - maybeProcessAllMailboxEntries(); + // As we do not expect a updated data request response we start here listening + addHashMapChangedListenerAndApply(); p2pServiceListeners.stream().forEach(P2PServiceListener::onNoSeedNodeAvailable); } } @@ -346,7 +352,9 @@ public void onPreliminaryDataReceived() { public void onUpdatedDataReceived() { if (!isBootstrapped) { isBootstrapped = true; - maybeProcessAllMailboxEntries(); + // Only now we start listening and processing. The p2PDataStorage is our cache for data we have received + // so far. + addHashMapChangedListenerAndApply(); p2pServiceListeners.stream().forEach(P2PServiceListener::onUpdatedDataReceived); p2PDataStorage.onBootstrapComplete(); } @@ -398,58 +406,120 @@ public void onError(Throwable throwable) { @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof PrefixedSealedAndSignedMessage) { - // Seed nodes don't have set the encryptionService + PrefixedSealedAndSignedMessage sealedMsg = (PrefixedSealedAndSignedMessage) networkEnvelope; + connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER); try { - PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = (PrefixedSealedAndSignedMessage) networkEnvelope; - if (verifyAddressPrefixHash(prefixedSealedAndSignedMessage)) { - // We set connectionType to that connection to avoid that is get closed when - // we get too many connection attempts. - connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER); - - log.debug("Try to decrypt..."); - DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify( - prefixedSealedAndSignedMessage.getSealedAndSigned()); - - log.debug("\n\nDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\n" + - "Decrypted SealedAndSignedMessage:\ndecryptedMsgWithPubKey={}" - + "\nDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\n", decryptedMessageWithPubKey); - if (connection.getPeersNodeAddressOptional().isPresent()) - decryptedDirectMessageListeners.forEach( - e -> e.onDirectMessage(decryptedMessageWithPubKey, connection.getPeersNodeAddressOptional().get())); - else - log.error("peersNodeAddress is not available at onMessage."); - } else { - log.debug("Wrong receiverAddressMaskHash. The message is not intended for us."); - } + DecryptedMessageWithPubKey decryptedMsg = encryptionService.decryptAndVerify(sealedMsg.getSealedAndSigned()); + connection.getPeersNodeAddressOptional().ifPresentOrElse(nodeAddress -> + decryptedDirectMessageListeners.forEach(e -> e.onDirectMessage(decryptedMsg, nodeAddress)), + () -> { + log.error("peersNodeAddress is expected to be available at onMessage for " + + "processing PrefixedSealedAndSignedMessage."); + }); } catch (CryptoException e) { - log.debug(networkEnvelope.toString()); - log.debug(e.toString()); - log.debug("Decryption of prefixedSealedAndSignedMessage.sealedAndSigned failed. " + - "That is expected if the message is not intended for us."); + log.warn("Decryption of a direct message failed. This is not expected as the " + + "direct message was sent to our node."); } catch (ProtobufferException e) { - log.error("Protobuffer data could not be processed: {}", e.toString()); + log.error("ProtobufferException at decryptAndVerify: {}", e.toString()); + e.getStackTrace(); } } } /////////////////////////////////////////////////////////////////////////////////////////// - // HashMapChangedListener implementation + // HashMapChangedListener implementation for ProtectedStorageEntry items /////////////////////////////////////////////////////////////////////////////////////////// + private void addHashMapChangedListenerAndApply() { + p2PDataStorage.addHashMapChangedListener(this); + onAdded(p2PDataStorage.getMap().values()); + } + @Override public void onAdded(Collection protectedStorageEntries) { - protectedStorageEntries.forEach(protectedStorageEntry -> { - if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry) - processMailboxEntry((ProtectedMailboxStorageEntry) protectedStorageEntry); + Collection entries = protectedStorageEntries.stream() + .filter(e -> e instanceof ProtectedMailboxStorageEntry) + .map(e -> (ProtectedMailboxStorageEntry) e) + .filter(e -> networkNode.getNodeAddress() != null) + .filter(e -> !seedNodeRepository.isSeedNode(networkNode.getNodeAddress())) // Seed nodes don't expect mailbox messages + .collect(Collectors.toSet()); + if (entries.size() > 1) { + threadedBatchProcessMailboxEntries(entries); + } else if (entries.size() == 1) { + processSingleMailboxEntry(entries); + } + } + + private void processSingleMailboxEntry(Collection protectedMailboxStorageEntries) { + var decryptedEntries = new ArrayList<>(getDecryptedEntries(protectedMailboxStorageEntries)); + checkArgument(decryptedEntries.size() == 1); + storeMailboxDataAndNotifyListeners(decryptedEntries.get(0)); + } + + // We run the batch processing of all mailbox messages we have received at startup in a thread to not block the UI. + // For about 1000 messages decryption takes about 1 sec. + private void threadedBatchProcessMailboxEntries(Collection protectedMailboxStorageEntries) { + ListeningExecutorService executor = Utilities.getSingleThreadExecutor("processMailboxEntry-" + new Random().nextInt(1000)); + long ts = System.currentTimeMillis(); + ListenableFuture>> future = executor.submit(() -> { + var decryptedEntries = getDecryptedEntries(protectedMailboxStorageEntries); + log.info("Batch processing of {} mailbox entries took {} ms", + protectedMailboxStorageEntries.size(), + System.currentTimeMillis() - ts); + return decryptedEntries; }); + + Futures.addCallback(future, new FutureCallback<>() { + public void onSuccess(Set> decryptedEntries) { + UserThread.execute(() -> decryptedEntries.forEach(e -> storeMailboxDataAndNotifyListeners(e))); + } + + public void onFailure(@NotNull Throwable throwable) { + log.error(throwable.toString()); + } + }, MoreExecutors.directExecutor()); } - @Override - public void onRemoved(Collection protectedStorageEntries) { - // not used + private Set> getDecryptedEntries(Collection protectedMailboxStorageEntries) { + Set> decryptedEntries = new HashSet<>(); + protectedMailboxStorageEntries.stream() + .map(this::decryptProtectedMailboxStorageEntry) + .filter(Objects::nonNull) + .forEach(decryptedEntries::add); + return decryptedEntries; } + @Nullable + private Tuple2 decryptProtectedMailboxStorageEntry( + ProtectedMailboxStorageEntry protectedMailboxStorageEntry) { + try { + DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify(protectedMailboxStorageEntry + .getMailboxStoragePayload() + .getPrefixedSealedAndSignedMessage() + .getSealedAndSigned()); + checkArgument(decryptedMessageWithPubKey.getNetworkEnvelope() instanceof MailboxMessage); + return new Tuple2<>(protectedMailboxStorageEntry, decryptedMessageWithPubKey); + } catch (CryptoException ignore) { + // Expected if message was not intended for us + } catch (ProtobufferException e) { + log.error(e.toString()); + e.getStackTrace(); + } + return null; + } + + private void storeMailboxDataAndNotifyListeners(Tuple2 tuple2) { + DecryptedMessageWithPubKey decryptedMessageWithPubKey = tuple2.second; + MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope(); + NodeAddress sender = mailboxMessage.getSenderNodeAddress(); + mailboxMap.put(mailboxMessage.getUid(), tuple2); + log.info("Received a {} mailbox message with uid {} and senderAddress {}", + mailboxMessage.getClass().getSimpleName(), mailboxMessage.getUid(), sender); + decryptedMailboxListeners.forEach(e -> e.onMailboxMessageAdded(decryptedMessageWithPubKey, sender)); + } + + /////////////////////////////////////////////////////////////////////////////////////////// // DirectMessages /////////////////////////////////////////////////////////////////////////////////////////// @@ -486,13 +556,16 @@ private void doSendEncryptedDirectMessage(@NotNull NodeAddress peersNodeAddress, log.debug("\n\nEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" + "Encrypt message:\nmessage={}" + "\nEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n", message); - PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = new PrefixedSealedAndSignedMessage( - networkNode.getNodeAddress(), - encryptionService.encryptAndSign(pubKeyRing, message), - peersNodeAddress.getAddressPrefixHash(), - UUID.randomUUID().toString()); - SettableFuture future = networkNode.sendMessage(peersNodeAddress, prefixedSealedAndSignedMessage); - Futures.addCallback(future, new FutureCallback() { + + // Prefix is not needed for direct messages but as old code is doing the verification we still need to + // send it if peer has not updated. + // TODO persist capability + PrefixedSealedAndSignedMessage sealedMsg = getPrefixedSealedAndSignedMessage(peersNodeAddress, + pubKeyRing, + message); + + SettableFuture future = networkNode.sendMessage(peersNodeAddress, sealedMsg); + Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(@Nullable Connection connection) { sendDirectMessageListener.onArrived(); @@ -513,48 +586,30 @@ public void onFailure(@NotNull Throwable throwable) { } } + private PrefixedSealedAndSignedMessage getPrefixedSealedAndSignedMessage(NodeAddress peersNodeAddress, + PubKeyRing pubKeyRing, + NetworkEnvelope message) throws CryptoException { + byte[] addressPrefixHash; + if (peerManager.peerHasCapability(peersNodeAddress, Capability.NO_ADDRESS_PRE_FIX)) { + // The peer has an updated version so we do not need to send the prefix. + // We cannot use null as not updated nodes would get a nullPointer at protobuf serialisation. + addressPrefixHash = new byte[0]; + } else { + addressPrefixHash = peersNodeAddress.getAddressPrefixHash(); + } + return new PrefixedSealedAndSignedMessage( + networkNode.getNodeAddress(), + encryptionService.encryptAndSign(pubKeyRing, message), + addressPrefixHash, + UUID.randomUUID().toString()); + } + /////////////////////////////////////////////////////////////////////////////////////////// // MailboxMessages /////////////////////////////////////////////////////////////////////////////////////////// - private void processMailboxEntry(ProtectedMailboxStorageEntry protectedMailboxStorageEntry) { - NodeAddress nodeAddress = networkNode.getNodeAddress(); - // Seed nodes don't receive mailbox network_messages - if (nodeAddress != null && !seedNodeRepository.isSeedNode(nodeAddress)) { - MailboxStoragePayload mailboxStoragePayload = protectedMailboxStorageEntry.getMailboxStoragePayload(); - PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = mailboxStoragePayload.getPrefixedSealedAndSignedMessage(); - if (verifyAddressPrefixHash(prefixedSealedAndSignedMessage)) { - try { - DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify( - prefixedSealedAndSignedMessage.getSealedAndSigned()); - if (decryptedMessageWithPubKey.getNetworkEnvelope() instanceof MailboxMessage) { - MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope(); - NodeAddress senderNodeAddress = mailboxMessage.getSenderNodeAddress(); - checkNotNull(senderNodeAddress, "senderAddress must not be null for mailbox network_messages"); - - mailboxMap.put(mailboxMessage.getUid(), new Tuple2<>(protectedMailboxStorageEntry, decryptedMessageWithPubKey)); - log.info("Received a {} mailbox message with messageUid {} and senderAddress {}", mailboxMessage.getClass().getSimpleName(), mailboxMessage.getUid(), senderNodeAddress); - decryptedMailboxListeners.forEach( - e -> e.onMailboxMessageAdded(decryptedMessageWithPubKey, senderNodeAddress)); - } else { - log.warn("tryDecryptMailboxData: Expected MailboxMessage but got other type. " + - "decryptedMsgWithPubKey.message={}", decryptedMessageWithPubKey.getNetworkEnvelope()); - } - } catch (CryptoException e) { - log.debug(e.toString()); - log.debug("Decryption of prefixedSealedAndSignedMessage.sealedAndSigned failed. " + - "That is expected if the message is not intended for us."); - } catch (ProtobufferException e) { - log.error("Protobuffer data could not be processed: {}", e.toString()); - } - } else { - log.trace("Wrong blurredAddressHash. The message is not intended for us."); - } - } - } - - public void sendEncryptedMailboxMessage(NodeAddress peersNodeAddress, PubKeyRing peersPubKeyRing, + public void sendEncryptedMailboxMessage(NodeAddress peer, PubKeyRing peersPubKeyRing, NetworkEnvelope message, SendMailboxMessageListener sendMailboxMessageListener) { if (peersPubKeyRing == null) { @@ -562,7 +617,7 @@ public void sendEncryptedMailboxMessage(NodeAddress peersNodeAddress, PubKeyRing return; } - checkNotNull(peersNodeAddress, + checkNotNull(peer, "PeerAddress must not be null (sendEncryptedMailboxMessage)"); checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at sendEncryptedMailboxMessage"); @@ -578,7 +633,7 @@ public void sendEncryptedMailboxMessage(NodeAddress peersNodeAddress, PubKeyRing return; } - if (capabilityRequiredAndCapabilityNotSupported(peersNodeAddress, message)) { + if (capabilityRequiredAndCapabilityNotSupported(peer, message)) { sendMailboxMessageListener.onFault("We did not send the EncryptedMailboxMessage " + "because the peer does not support the capability."); return; @@ -589,14 +644,11 @@ public void sendEncryptedMailboxMessage(NodeAddress peersNodeAddress, PubKeyRing "Encrypt message:\nmessage={}" + "\nEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n", message); - PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = new PrefixedSealedAndSignedMessage( - networkNode.getNodeAddress(), - encryptionService.encryptAndSign(peersPubKeyRing, message), - peersNodeAddress.getAddressPrefixHash(), - UUID.randomUUID().toString()); + PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = getPrefixedSealedAndSignedMessage(peer, + peersPubKeyRing, message); - log.debug("sendEncryptedMailboxMessage msg={}, peersNodeAddress={}", message, peersNodeAddress); - SettableFuture future = networkNode.sendMessage(peersNodeAddress, prefixedSealedAndSignedMessage); + log.debug("sendEncryptedMailboxMessage msg={}, peersNodeAddress={}", message, peer); + SettableFuture future = networkNode.sendMessage(peer, prefixedSealedAndSignedMessage); Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(@Nullable Connection connection) { @@ -654,15 +706,6 @@ private boolean capabilityRequiredAndCapabilityNotSupported(NodeAddress peersNod } - private void maybeProcessAllMailboxEntries() { - if (isBootstrapped) { - p2PDataStorage.getMap().values().forEach(protectedStorageEntry -> { - if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry) - processMailboxEntry((ProtectedMailboxStorageEntry) protectedStorageEntry); - }); - } - } - private void addMailboxData(MailboxStoragePayload expirableMailboxStoragePayload, PublicKey receiversPublicKey, SendMailboxMessageListener sendMailboxMessageListener) { @@ -892,19 +935,4 @@ public PeerManager getPeerManager() { public KeyRing getKeyRing() { return keyRing; } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private boolean verifyAddressPrefixHash(PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage) { - if (networkNode.getNodeAddress() != null) { - byte[] blurredAddressHash = networkNode.getNodeAddress().getAddressPrefixHash(); - return blurredAddressHash != null && - Arrays.equals(blurredAddressHash, prefixedSealedAndSignedMessage.getAddressPrefixHash()); - } else { - log.debug("myOnionAddress is null at verifyAddressPrefixHash. That is expected at startup."); - return false; - } - } } diff --git a/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java b/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java index d5f626a90d5..9f6177ba596 100644 --- a/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java +++ b/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java @@ -33,7 +33,11 @@ public final class PrefixedSealedAndSignedMessage extends NetworkEnvelope implements MailboxMessage, SendersNodeAddressMessage { private final NodeAddress senderNodeAddress; private final SealedAndSigned sealedAndSigned; + + // From v1.4.0 on that can be an empty byte array. We cannot use null as not updated nodes would get a nullPointer + // at protobuf serialisation. private final byte[] addressPrefixHash; + private final String uid; public PrefixedSealedAndSignedMessage(NodeAddress senderNodeAddress, From 9821dd62719ea354c3d52edff8c4d57568d946d4 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 13:03:18 -0500 Subject: [PATCH 038/123] Clear capabilitiesListeners at shutdown Improve logs --- .../bisq/network/p2p/network/Connection.java | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/Connection.java b/p2p/src/main/java/bisq/network/p2p/network/Connection.java index 8b3560e3362..7789d0422b8 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Connection.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Connection.java @@ -159,6 +159,8 @@ public static int getPermittedMessageSize() { private final List messageTimeStamps = new ArrayList<>(); private final CopyOnWriteArraySet messageListeners = new CopyOnWriteArraySet<>(); private volatile long lastSendTimeStamp = 0; + // We use a weak reference here to ensure that no connection causes a memory leak in case it get closed without + // the shutDown being called. private final CopyOnWriteArraySet> capabilitiesListeners = new CopyOnWriteArraySet<>(); @Getter @@ -514,6 +516,8 @@ private void doShutDown(CloseConnectionReason closeConnectionReason, @Nullable R } finally { protoOutputStream.onConnectionShutdown(); + capabilitiesListeners.clear(); + try { protoInputStream.close(); } catch (IOException e) { @@ -559,7 +563,6 @@ public String toString() { '}'; } - @SuppressWarnings("unused") public String printDetails() { String portInfo; if (socket.getLocalPort() == 0) @@ -783,19 +786,26 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { } if (networkEnvelope instanceof SupportedCapabilitiesMessage) { - Capabilities supportedCapabilities = ((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities(); - if (supportedCapabilities != null) { - if (!capabilities.equals(supportedCapabilities)) { - capabilities.set(supportedCapabilities); + Capabilities capabilities = ((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities(); + if (capabilities != null) { + if (!this.capabilities.equals(capabilities)) { + this.capabilities.set(capabilities); // Capabilities can be empty. We only check for mandatory if we get some capabilities. - if (!capabilities.isEmpty() && !Capabilities.hasMandatoryCapability(capabilities)) { - String senderNodeAddress = networkEnvelope instanceof SendersNodeAddressMessage ? - ((SendersNodeAddressMessage) networkEnvelope).getSenderNodeAddress().getFullAddress() : - "[unknown address]"; - log.info("We close a connection to old node {}. " + - "Capabilities of old node: {}, networkEnvelope class name={}", - senderNodeAddress, capabilities.prettyPrint(), networkEnvelope.getClass().getSimpleName()); + if (!this.capabilities.isEmpty() && !Capabilities.hasMandatoryCapability(this.capabilities)) { + String senderNodeAddress = getPeersNodeAddressOptional().isPresent() ? + getPeersNodeAddressOptional().get().getFullAddress() : + networkEnvelope instanceof SendersNodeAddressMessage ? + ((SendersNodeAddressMessage) networkEnvelope).getSenderNodeAddress().getFullAddress() : + "[unknown address]"; + + log.info("We close a connection because of " + + "CloseConnectionReason.MANDATORY_CAPABILITIES_NOT_SUPPORTED " + + "to node {}. Capabilities of old node: {}, " + + "networkEnvelope class name={}", + senderNodeAddress, + this.capabilities.prettyPrint(), + networkEnvelope.getClass().getSimpleName()); shutDown(CloseConnectionReason.MANDATORY_CAPABILITIES_NOT_SUPPORTED); return; } @@ -803,7 +813,7 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { capabilitiesListeners.forEach(weakListener -> { SupportedCapabilitiesListener supportedCapabilitiesListener = weakListener.get(); if (supportedCapabilitiesListener != null) { - UserThread.execute(() -> supportedCapabilitiesListener.onChanged(supportedCapabilities)); + UserThread.execute(() -> supportedCapabilitiesListener.onChanged(capabilities)); } }); } From 17974f3dcc358c9ca47f28aec97f4cf6c95f39c5 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 13:11:29 -0500 Subject: [PATCH 039/123] Refactor: move SupportedCapabilitiesMessage handling code out to a method Return early --- .../bisq/network/p2p/network/Connection.java | 82 ++++++++++++------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/Connection.java b/p2p/src/main/java/bisq/network/p2p/network/Connection.java index 7789d0422b8..2a28158aac5 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Connection.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Connection.java @@ -786,37 +786,9 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { } if (networkEnvelope instanceof SupportedCapabilitiesMessage) { - Capabilities capabilities = ((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities(); - if (capabilities != null) { - if (!this.capabilities.equals(capabilities)) { - this.capabilities.set(capabilities); - - // Capabilities can be empty. We only check for mandatory if we get some capabilities. - if (!this.capabilities.isEmpty() && !Capabilities.hasMandatoryCapability(this.capabilities)) { - String senderNodeAddress = getPeersNodeAddressOptional().isPresent() ? - getPeersNodeAddressOptional().get().getFullAddress() : - networkEnvelope instanceof SendersNodeAddressMessage ? - ((SendersNodeAddressMessage) networkEnvelope).getSenderNodeAddress().getFullAddress() : - "[unknown address]"; - - log.info("We close a connection because of " + - "CloseConnectionReason.MANDATORY_CAPABILITIES_NOT_SUPPORTED " + - "to node {}. Capabilities of old node: {}, " + - "networkEnvelope class name={}", - senderNodeAddress, - this.capabilities.prettyPrint(), - networkEnvelope.getClass().getSimpleName()); - shutDown(CloseConnectionReason.MANDATORY_CAPABILITIES_NOT_SUPPORTED); - return; - } - - capabilitiesListeners.forEach(weakListener -> { - SupportedCapabilitiesListener supportedCapabilitiesListener = weakListener.get(); - if (supportedCapabilitiesListener != null) { - UserThread.execute(() -> supportedCapabilitiesListener.onChanged(capabilities)); - } - }); - } + boolean causedShutDown = handleSupportedCapabilitiesMessage(networkEnvelope); + if (causedShutDown) { + return; } } @@ -892,4 +864,52 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { handleException(t); } } + + protected boolean handleSupportedCapabilitiesMessage(NetworkEnvelope networkEnvelope) { + Capabilities supportedCapabilities = ((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities(); + if (supportedCapabilities == null || supportedCapabilities.isEmpty()) { + return false; + } + + if (this.capabilities.equals(supportedCapabilities)) { + return false; + } + + if (!Capabilities.hasMandatoryCapability(supportedCapabilities)) { + log.info("We close a connection because of " + + "CloseConnectionReason.MANDATORY_CAPABILITIES_NOT_SUPPORTED " + + "to node {}. Capabilities of old node: {}, " + + "networkEnvelope class name={}", + getSenderNodeAddressAsString(networkEnvelope), + supportedCapabilities.prettyPrint(), + networkEnvelope.getClass().getSimpleName()); + shutDown(CloseConnectionReason.MANDATORY_CAPABILITIES_NOT_SUPPORTED); + return true; + } + + this.capabilities.set(supportedCapabilities); + + capabilitiesListeners.forEach(weakListener -> { + SupportedCapabilitiesListener supportedCapabilitiesListener = weakListener.get(); + if (supportedCapabilitiesListener != null) { + UserThread.execute(() -> supportedCapabilitiesListener.onChanged(supportedCapabilities)); + } + }); + return false; + } + + @Nullable + private NodeAddress getSenderNodeAddress(NetworkEnvelope networkEnvelope) { + return getPeersNodeAddressOptional().isPresent() ? + getPeersNodeAddressOptional().get() : + networkEnvelope instanceof SendersNodeAddressMessage ? + ((SendersNodeAddressMessage) networkEnvelope).getSenderNodeAddress() : + null; + } + + private String getSenderNodeAddressAsString(NetworkEnvelope networkEnvelope) { + NodeAddress nodeAddress = getSenderNodeAddress(networkEnvelope); + return nodeAddress == null ? "null" : nodeAddress.getFullAddress(); + } + } From 1c07be05074f79b75a0064fee12c6e483762be56 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 13:14:37 -0500 Subject: [PATCH 040/123] Use only node address for equals and hashcode Make capabilities final If capability changes we would have had duplicate entries --- .../network/p2p/peers/peerexchange/Peer.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java index cb3f8d64e7e..8c716c6787a 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java @@ -27,7 +27,6 @@ import java.util.Date; -import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -35,18 +34,15 @@ import javax.annotation.Nullable; @Getter -@EqualsAndHashCode(exclude = {"date"}) // failedConnectionAttempts is transient and therefore excluded anyway @Slf4j public final class Peer implements HasCapabilities, NetworkPayload, PersistablePayload, SupportedCapabilitiesListener { private static final int MAX_FAILED_CONNECTION_ATTEMPTS = 5; private final NodeAddress nodeAddress; private final long date; - // Added in v. 0.7.1 - @Setter transient private int failedConnectionAttempts = 0; - private Capabilities capabilities = new Capabilities(); + private final Capabilities capabilities = new Capabilities(); public Peer(NodeAddress nodeAddress, @Nullable Capabilities supportedCapabilities) { this(nodeAddress, new Date().getTime(), supportedCapabilities); @@ -102,14 +98,29 @@ public void onChanged(Capabilities supportedCapabilities) { } } + // We use only node address for equals and hashcode + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Peer)) return false; + + Peer peer = (Peer) o; + + return nodeAddress != null ? nodeAddress.equals(peer.nodeAddress) : peer.nodeAddress == null; + } + + @Override + public int hashCode() { + return nodeAddress != null ? nodeAddress.hashCode() : 0; + } @Override public String toString() { return "Peer{" + "\n nodeAddress=" + nodeAddress + - ",\n supportedCapabilities=" + capabilities + - ",\n failedConnectionAttempts=" + failedConnectionAttempts + ",\n date=" + date + + ",\n failedConnectionAttempts=" + failedConnectionAttempts + + ",\n capabilities=" + capabilities + "\n}"; } } From bf659a1e6d0d235a2e85e0efab74fff39d6682a9 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 13:15:49 -0500 Subject: [PATCH 041/123] Pass supportedCapabilities to PeerManager. Not further processed yet, will be done in next commits Cleanups --- p2p/src/main/java/bisq/network/p2p/P2PService.java | 2 -- p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java | 7 ++++++- .../p2p/peers/peerexchange/GetPeersRequestHandler.java | 5 +++-- .../p2p/peers/peerexchange/PeerExchangeHandler.java | 4 +++- .../p2p/peers/peerexchange/PeerExchangeManager.java | 3 ++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 9abde7bf16d..b36a67c0a73 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -323,8 +323,6 @@ private void onNetworkReady() { "seedNodeOfPreliminaryDataRequest must be present"); requestDataManager.requestUpdateData(); - /*if (Capabilities.app.containsAll(Capability.SEED_NODE)) - UserThread.runPeriodically(() -> requestDataManager.requestUpdateData(), 1, TimeUnit.HOURS);*/ // If we start up first time we don't have any peers so we need to request from seed node. // As well it can be that the persisted peer list is outdated with dead peers. diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index f12c95b9fd7..15cb5508b80 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -464,7 +464,12 @@ public Set getReportedPeers() { return reportedPeers; } - public void addToReportedPeers(Set reportedPeersToAdd, Connection connection) { + public void addToReportedPeers(Set reportedPeersToAdd, + Connection connection, + Capabilities supportedCapabilities) { + + //TODO apply supportedCapabilities + printNewReportedPeers(reportedPeersToAdd); // We check if the reported msg is not violating our rules diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java index 2297758ee30..f280587c86b 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java @@ -130,8 +130,9 @@ public void onFailure(@NotNull Throwable throwable) { } } }, MoreExecutors.directExecutor()); - - peerManager.addToReportedPeers(getPeersRequest.getReportedPeers(), connection); + peerManager.addToReportedPeers(getPeersRequest.getReportedPeers(), + connection, + getPeersRequest.getSupportedCapabilities()); } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java index d0266c2ea36..df6b685dec7 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java @@ -168,7 +168,9 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { // Check if the response is for our request if (getPeersResponse.getRequestNonce() == nonce) { - peerManager.addToReportedPeers(getPeersResponse.getReportedPeers(), connection); + peerManager.addToReportedPeers(getPeersResponse.getReportedPeers(), + connection, + getPeersResponse.getSupportedCapabilities()); cleanup(); listener.onComplete(); } else { diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java index ad8489a1790..8d62ead5a59 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java @@ -224,7 +224,8 @@ public void onFault(String errorMessage, Connection connection) { /////////////////////////////////////////////////////////////////////////////////////////// private void requestReportedPeers(NodeAddress nodeAddress, List remainingNodeAddresses) { - log.debug("requestReportedPeers nodeAddress={}; remainingNodeAddresses.size={}", nodeAddress, remainingNodeAddresses.size()); + log.debug("requestReportedPeers nodeAddress={}; remainingNodeAddresses.size={}", + nodeAddress, remainingNodeAddresses.size()); if (!stopped) { if (!handlerMap.containsKey(nodeAddress)) { PeerExchangeHandler peerExchangeHandler = new PeerExchangeHandler(networkNode, From 2523c2e9146c0c26e4a551716dcbecf77c0a2e8b Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 13:35:10 -0500 Subject: [PATCH 042/123] Use getSingleThreadListeningExecutor, cleanups --- p2p/src/main/java/bisq/network/p2p/P2PService.java | 13 +++++-------- .../network/p2p/PrefixedSealedAndSignedMessage.java | 7 ++++--- .../java/bisq/network/p2p/network/Connection.java | 1 - .../java/bisq/network/p2p/peers/PeerManager.java | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index b36a67c0a73..f129127941a 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -289,7 +289,7 @@ public void onTorNodeReady() { if (!seedNodesAvailable) { isBootstrapped = true; - // As we do not expect a updated data request response we start here listening + // As we do not expect a updated data request response we start here with addHashMapChangedListenerAndApply addHashMapChangedListenerAndApply(); p2pServiceListeners.stream().forEach(P2PServiceListener::onNoSeedNodeAvailable); } @@ -351,7 +351,7 @@ public void onUpdatedDataReceived() { if (!isBootstrapped) { isBootstrapped = true; // Only now we start listening and processing. The p2PDataStorage is our cache for data we have received - // so far. + // after the hidden service was ready. addHashMapChangedListenerAndApply(); p2pServiceListeners.stream().forEach(P2PServiceListener::onUpdatedDataReceived); p2PDataStorage.onBootstrapComplete(); @@ -458,7 +458,7 @@ private void processSingleMailboxEntry(Collection // We run the batch processing of all mailbox messages we have received at startup in a thread to not block the UI. // For about 1000 messages decryption takes about 1 sec. private void threadedBatchProcessMailboxEntries(Collection protectedMailboxStorageEntries) { - ListeningExecutorService executor = Utilities.getSingleThreadExecutor("processMailboxEntry-" + new Random().nextInt(1000)); + ListeningExecutorService executor = Utilities.getSingleThreadListeningExecutor("processMailboxEntry-" + new Random().nextInt(1000)); long ts = System.currentTimeMillis(); ListenableFuture>> future = executor.submit(() -> { var decryptedEntries = getDecryptedEntries(protectedMailboxStorageEntries); @@ -557,7 +557,6 @@ private void doSendEncryptedDirectMessage(@NotNull NodeAddress peersNodeAddress, // Prefix is not needed for direct messages but as old code is doing the verification we still need to // send it if peer has not updated. - // TODO persist capability PrefixedSealedAndSignedMessage sealedMsg = getPrefixedSealedAndSignedMessage(peersNodeAddress, pubKeyRing, message); @@ -615,12 +614,10 @@ public void sendEncryptedMailboxMessage(NodeAddress peer, PubKeyRing peersPubKey return; } - checkNotNull(peer, - "PeerAddress must not be null (sendEncryptedMailboxMessage)"); + checkNotNull(peer, "PeerAddress must not be null (sendEncryptedMailboxMessage)"); checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at sendEncryptedMailboxMessage"); - checkArgument(!keyRing.getPubKeyRing().equals(peersPubKeyRing), - "We got own keyring instead of that from peer"); + checkArgument(!keyRing.getPubKeyRing().equals(peersPubKeyRing), "We got own keyring instead of that from peer"); if (!isBootstrapped()) throw new NetworkNotReadyException(); diff --git a/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java b/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java index 9f6177ba596..bf617dce114 100644 --- a/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java +++ b/p2p/src/main/java/bisq/network/p2p/PrefixedSealedAndSignedMessage.java @@ -34,8 +34,8 @@ public final class PrefixedSealedAndSignedMessage extends NetworkEnvelope implem private final NodeAddress senderNodeAddress; private final SealedAndSigned sealedAndSigned; - // From v1.4.0 on that can be an empty byte array. We cannot use null as not updated nodes would get a nullPointer - // at protobuf serialisation. + // From v1.4.0 on addressPrefixHash can be an empty byte array. + // We cannot make it nullable as not updated nodes would get a nullPointer exception at protobuf serialisation. private final byte[] addressPrefixHash; private final String uid; @@ -75,7 +75,8 @@ public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { .build(); } - public static PrefixedSealedAndSignedMessage fromProto(protobuf.PrefixedSealedAndSignedMessage proto, int messageVersion) { + public static PrefixedSealedAndSignedMessage fromProto(protobuf.PrefixedSealedAndSignedMessage proto, + int messageVersion) { return new PrefixedSealedAndSignedMessage(NodeAddress.fromProto(proto.getNodeAddress()), SealedAndSigned.fromProto(proto.getSealedAndSigned()), proto.getAddressPrefixHash().toByteArray(), diff --git a/p2p/src/main/java/bisq/network/p2p/network/Connection.java b/p2p/src/main/java/bisq/network/p2p/network/Connection.java index 2a28158aac5..6a29c7e0343 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Connection.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Connection.java @@ -911,5 +911,4 @@ private String getSenderNodeAddressAsString(NetworkEnvelope networkEnvelope) { NodeAddress nodeAddress = getSenderNodeAddress(networkEnvelope); return nodeAddress == null ? "null" : nodeAddress.getFullAddress(); } - } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 15cb5508b80..9ec74dc2eb9 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -213,7 +213,7 @@ public boolean peerHasCapability(NodeAddress peersNodeAddress, Capability capabi .orElse(false); } - // TODO persist Capabilities + // TODO get Capabilities from peers public Optional findPeersCapabilities(NodeAddress peersNodeAddress) { return networkNode.getConfirmedConnections().stream() .filter(c -> c.getPeersNodeAddressProperty().get() != null) From 765f9ea940fde8ae0b938c352a96461614131629 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 13:42:29 -0500 Subject: [PATCH 043/123] Apply code inspection suggestions The check for AckMessage is not needed anymore as we remove the interface from AckMessage --- .../java/bisq/network/p2p/P2PService.java | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index f129127941a..b127833fd4d 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -285,13 +285,13 @@ public void onTorNodeReady() { boolean seedNodesAvailable = requestDataManager.requestPreliminaryData(); keepAliveManager.start(); - p2pServiceListeners.stream().forEach(SetupListener::onTorNodeReady); + p2pServiceListeners.forEach(SetupListener::onTorNodeReady); if (!seedNodesAvailable) { isBootstrapped = true; // As we do not expect a updated data request response we start here with addHashMapChangedListenerAndApply addHashMapChangedListenerAndApply(); - p2pServiceListeners.stream().forEach(P2PServiceListener::onNoSeedNodeAvailable); + p2pServiceListeners.forEach(P2PServiceListener::onNoSeedNodeAvailable); } } @@ -301,17 +301,17 @@ public void onHiddenServicePublished() { hiddenServicePublished.set(true); - p2pServiceListeners.stream().forEach(SetupListener::onHiddenServicePublished); + p2pServiceListeners.forEach(SetupListener::onHiddenServicePublished); } @Override public void onSetupFailed(Throwable throwable) { - p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable)); + p2pServiceListeners.forEach(e -> e.onSetupFailed(throwable)); } @Override public void onRequestCustomBridges() { - p2pServiceListeners.stream().forEach(SetupListener::onRequestCustomBridges); + p2pServiceListeners.forEach(SetupListener::onRequestCustomBridges); } // Called from networkReadyBinding @@ -353,24 +353,24 @@ public void onUpdatedDataReceived() { // Only now we start listening and processing. The p2PDataStorage is our cache for data we have received // after the hidden service was ready. addHashMapChangedListenerAndApply(); - p2pServiceListeners.stream().forEach(P2PServiceListener::onUpdatedDataReceived); + p2pServiceListeners.forEach(P2PServiceListener::onUpdatedDataReceived); p2PDataStorage.onBootstrapComplete(); } } @Override public void onNoSeedNodeAvailable() { - p2pServiceListeners.stream().forEach(P2PServiceListener::onNoSeedNodeAvailable); + p2pServiceListeners.forEach(P2PServiceListener::onNoSeedNodeAvailable); } @Override public void onNoPeersAvailable() { - p2pServiceListeners.stream().forEach(P2PServiceListener::onNoPeersAvailable); + p2pServiceListeners.forEach(P2PServiceListener::onNoPeersAvailable); } @Override public void onDataReceived() { - p2pServiceListeners.stream().forEach(P2PServiceListener::onDataReceived); + p2pServiceListeners.forEach(P2PServiceListener::onDataReceived); } @@ -644,7 +644,7 @@ public void sendEncryptedMailboxMessage(NodeAddress peer, PubKeyRing peersPubKey log.debug("sendEncryptedMailboxMessage msg={}, peersNodeAddress={}", message, peer); SettableFuture future = networkNode.sendMessage(peer, prefixedSealedAndSignedMessage); - Futures.addCallback(future, new FutureCallback() { + Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(@Nullable Connection connection) { sendMailboxMessageListener.onArrived(); @@ -671,11 +671,6 @@ private boolean capabilityRequiredAndCapabilityNotSupported(NodeAddress peersNod if (!(message instanceof CapabilityRequiringPayload)) return false; - // We only expect AckMessage so far - if (!(message instanceof AckMessage)) - log.warn("We got a CapabilityRequiringPayload for the mailbox message which is not a AckMessage. " + - "peersNodeAddress={}", peersNodeAddress); - Set allPeers = peerManager.getPersistedPeers(); allPeers.addAll(peerManager.getReportedPeers()); allPeers.addAll(peerManager.getLivePeers(null)); @@ -878,8 +873,7 @@ public void addP2PServiceListener(P2PServiceListener listener) { } public void removeP2PServiceListener(P2PServiceListener listener) { - if (p2pServiceListeners.contains(listener)) - p2pServiceListeners.remove(listener); + p2pServiceListeners.remove(listener); } public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) { From 186a9d670d33993a31bfba367eba5bb4261f3340 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 14:46:55 -0500 Subject: [PATCH 044/123] Add findPeersCapabilities method --- .../main/java/bisq/network/p2p/network/NetworkNode.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index 5b81cbb1c98..c2695833f7a 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -21,6 +21,7 @@ import bisq.common.Timer; import bisq.common.UserThread; +import bisq.common.app.Capabilities; import bisq.common.proto.network.NetworkEnvelope; import bisq.common.proto.network.NetworkProtoResolver; import bisq.common.util.Utilities; @@ -496,4 +497,12 @@ private void printInboundConnections() { public NodeAddress getNodeAddress() { return nodeAddressProperty.get(); } + + public Optional findPeersCapabilities(NodeAddress nodeAddress) { + return getConfirmedConnections().stream() + .filter(c -> c.getPeersNodeAddressProperty().get() != null) + .filter(c -> c.getPeersNodeAddressProperty().get().equals(nodeAddress)) + .map(Connection::getCapabilities) + .findAny(); + } } From 7af16d7af35a4a820e6de8c093dac8b472ff1281 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 14:47:49 -0500 Subject: [PATCH 045/123] Add getDateAsLong method, add setter for capabilities --- .../java/bisq/network/p2p/peers/peerexchange/Peer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java index 8c716c6787a..a7681f909ce 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java @@ -42,7 +42,8 @@ public final class Peer implements HasCapabilities, NetworkPayload, PersistableP private final long date; @Setter transient private int failedConnectionAttempts = 0; - private final Capabilities capabilities = new Capabilities(); + @Setter + private Capabilities capabilities = new Capabilities(); public Peer(NodeAddress nodeAddress, @Nullable Capabilities supportedCapabilities) { this(nodeAddress, new Date().getTime(), supportedCapabilities); @@ -91,6 +92,10 @@ public Date getDate() { return new Date(date); } + public long getDateAsLong() { + return date; + } + @Override public void onChanged(Capabilities supportedCapabilities) { if (!supportedCapabilities.isEmpty()) { From 25bfe2d6adf8e0b9e3ecfa180fa05359696f2b54 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 15:17:07 -0500 Subject: [PATCH 046/123] Add findPeersCapabilities method Impl. applyCapabilities Cleanups --- .../RepublishGovernanceDataHandler.java | 2 +- .../java/bisq/network/p2p/P2PService.java | 14 +- .../bisq/network/p2p/peers/PeerManager.java | 163 ++++++++++++------ 3 files changed, 118 insertions(+), 61 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java b/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java index 19192e543d0..ea73a102c26 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java @@ -170,7 +170,7 @@ private void connectToNextNode() { private void connectToAnyFullNode() { Capabilities required = new Capabilities(Capability.DAO_FULL_NODE); - List list = peerManager.getLivePeers(null).stream() + List list = peerManager.getLivePeers().stream() .filter(peer -> peer.getCapabilities().containsAll(required)) .collect(Collectors.toList()); diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index b127833fd4d..2004cf5e1b6 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -31,7 +31,6 @@ import bisq.network.p2p.peers.PeerManager; import bisq.network.p2p.peers.getdata.RequestDataManager; import bisq.network.p2p.peers.keepalive.KeepAliveManager; -import bisq.network.p2p.peers.peerexchange.Peer; import bisq.network.p2p.peers.peerexchange.PeerExchangeManager; import bisq.network.p2p.seed.SeedNodeRepository; import bisq.network.p2p.storage.HashMapChangedListener; @@ -46,6 +45,7 @@ import bisq.network.p2p.storage.payload.ProtectedStoragePayload; import bisq.common.UserThread; +import bisq.common.app.Capabilities; import bisq.common.app.Capability; import bisq.common.crypto.CryptoException; import bisq.common.crypto.KeyRing; @@ -671,17 +671,11 @@ private boolean capabilityRequiredAndCapabilityNotSupported(NodeAddress peersNod if (!(message instanceof CapabilityRequiringPayload)) return false; - Set allPeers = peerManager.getPersistedPeers(); - allPeers.addAll(peerManager.getReportedPeers()); - allPeers.addAll(peerManager.getLivePeers(null)); // We might have multiple entries of the same peer without the supportedCapabilities field set if we received // it from old versions, so we filter those. - Optional optionalPeer = allPeers.stream() - .filter(peer -> peer.getNodeAddress().equals(peersNodeAddress)) - .filter(peer -> !peer.getCapabilities().isEmpty()) - .findAny(); - if (optionalPeer.isPresent()) { - boolean result = optionalPeer.get().getCapabilities().containsAll(((CapabilityRequiringPayload) message).getRequiredCapabilities()); + Optional optionalCapabilities = peerManager.findPeersCapabilities(peersNodeAddress); + if (optionalCapabilities.isPresent()) { + boolean result = optionalCapabilities.get().containsAll(((CapabilityRequiringPayload) message).getRequiredCapabilities()); if (!result) log.warn("We don't send the message because the peer does not support the required capability. " + diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 9ec74dc2eb9..a45e7a975a2 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -60,6 +60,8 @@ import javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkArgument; + @Slf4j public class PeerManager implements ConnectionListener, PersistedDataHost { @@ -78,9 +80,6 @@ public class PeerManager implements ConnectionListener, PersistedDataHost { // Age of what we consider connected peers still as live peers private static final long MAX_AGE_LIVE_PEERS = TimeUnit.MINUTES.toMillis(30); private static final boolean PRINT_REPORTED_PEERS_DETAILS = true; - @Setter - private boolean allowDisconnectSeedNodes; - private Set latestLivePeers = new HashSet<>(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -102,19 +101,26 @@ public interface Listener { private final NetworkNode networkNode; private final ClockWatcher clockWatcher; - - private int maxConnections; private final Set seedNodeAddresses; - private final PersistenceManager persistenceManager; - private final PeerList peerList = new PeerList(); - private final HashSet persistedPeers = new HashSet<>(); - private final Set reportedPeers = new HashSet<>(); private final ClockWatcher.Listener listener; private final List listeners = new CopyOnWriteArrayList<>(); + + // Persistable peerList + private final PeerList peerList = new PeerList(); + // Peers we had persisted TODO use peerList instead + @Getter + private final Set persistedPeers = new HashSet<>(); + // Peers we got reported from other peers + @Getter + private final Set reportedPeers = new HashSet<>(); + // Peer of last 30 min. + private final Set latestLivePeers = new HashSet<>(); + private Timer checkMaxConnectionsTimer; private boolean stopped; private boolean lostAllConnections; + private int maxConnections; @Getter private int minConnections; @@ -122,6 +128,8 @@ public interface Listener { private int maxConnectionsPeer; private int maxConnectionsNonDirect; private int maxConnectionsAbsolute; + @Setter + private boolean allowDisconnectSeedNodes; /////////////////////////////////////////////////////////////////////////////////////////// @@ -132,8 +140,8 @@ public interface Listener { public PeerManager(NetworkNode networkNode, SeedNodeRepository seedNodeRepository, ClockWatcher clockWatcher, - @Named(Config.MAX_CONNECTIONS) int maxConnections, - PersistenceManager persistenceManager) { + PersistenceManager persistenceManager, + @Named(Config.MAX_CONNECTIONS) int maxConnections) { this.networkNode = networkNode; this.seedNodeAddresses = new HashSet<>(seedNodeRepository.getSeedNodeAddresses()); this.clockWatcher = clockWatcher; @@ -156,7 +164,8 @@ public void onMinuteTick() { @Override public void onAwakeFromStandby(long missedMs) { - // TODO is "stopped = false;" correct? + // We got probably stopped set to true when we got a longer interruption (e.g. lost all connections), + // now we get awake again, so set stopped to false. stopped = false; listeners.forEach(Listener::onAwakeFromStandby); } @@ -213,15 +222,40 @@ public boolean peerHasCapability(NodeAddress peersNodeAddress, Capability capabi .orElse(false); } - // TODO get Capabilities from peers - public Optional findPeersCapabilities(NodeAddress peersNodeAddress) { - return networkNode.getConfirmedConnections().stream() - .filter(c -> c.getPeersNodeAddressProperty().get() != null) - .filter(c -> c.getPeersNodeAddressProperty().get().equals(peersNodeAddress)) - .map(Connection::getCapabilities) + public Optional findPeersCapabilities(NodeAddress nodeAddress) { + // We look up first our connections as that is our own data. If not found there we look up the peers which + // include reported peers. + Optional optionalCapabilities = networkNode.findPeersCapabilities(nodeAddress); + if (optionalCapabilities.isPresent()) { + return optionalCapabilities; + } + + // Reported peers are not trusted data. We could get capabilities which miss the + // peers real capability or we could get maliciously altered capabilities telling us the peer supports a + // capability which is in fact not supported. This could lead to connection loss as we might send data not + // recognized by the peer. As we register a listener on connection if we don't have set the capability from our + // own sources we would get it fixed as soon we have a connection with that peer, rendering such an attack + // inefficient. + // Also this risk is only for not updated peers, so in case that would be abused for an + // attack all users have a strong incentive to update ;-). + return getAllPeers().stream() + .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) + .findAny().map(Peer::getCapabilities); + } + + public Optional findPeer(NodeAddress peersNodeAddress) { + return getAllPeers().stream() + .filter(peer -> peer.getNodeAddress().equals(peersNodeAddress)) .findAny(); } + public Set getAllPeers() { + Set allPeers = new HashSet<>(getLivePeers()); + allPeers.addAll(persistedPeers); + allPeers.addAll(reportedPeers); + return allPeers; + } + /////////////////////////////////////////////////////////////////////////////////////////// // ConnectionListener implementation /////////////////////////////////////////////////////////////////////////////////////////// @@ -460,15 +494,11 @@ private void removeTooOldReportedPeers() { reportedPeersToRemove.forEach(this::removeReportedPeer); } - public Set getReportedPeers() { - return reportedPeers; - } - public void addToReportedPeers(Set reportedPeersToAdd, Connection connection, - Capabilities supportedCapabilities) { + Capabilities capabilities) { - //TODO apply supportedCapabilities + applyCapabilities(connection, capabilities); printNewReportedPeers(reportedPeersToAdd); @@ -479,8 +509,7 @@ public void addToReportedPeers(Set reportedPeersToAdd, persistedPeers.addAll(reportedPeersToAdd); purgePersistedPeersIfExceeds(); - peerList.setAll(persistedPeers); - persistenceManager.requestPersistence(); + requestPersistence(); printReportedPeers(); } else { @@ -491,6 +520,17 @@ public void addToReportedPeers(Set reportedPeersToAdd, } } + private void applyCapabilities(Connection connection, Capabilities capabilities) { + if (capabilities != null && !capabilities.isEmpty()) { + connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { + getAllPeers().stream() + .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) + .forEach(peer -> peer.setCapabilities(capabilities)); + }); + requestPersistence(); + } + } + private void purgeReportedPeersIfExceeds() { int size = reportedPeers.size(); if (size > MAX_REPORTED_PEERS) { @@ -530,7 +570,7 @@ private void printNewReportedPeers(Set reportedPeers) { if (PRINT_REPORTED_PEERS_DETAILS) { StringBuilder result = new StringBuilder("We received new reportedPeers:"); List reportedPeersClone = new ArrayList<>(reportedPeers); - reportedPeersClone.stream().forEach(e -> result.append("\n\t").append(e)); + reportedPeersClone.forEach(e -> result.append("\n\t").append(e)); log.trace(result.toString()); } log.debug("Number of new arrived reported peers: {}", reportedPeers.size()); @@ -544,23 +584,28 @@ private void printNewReportedPeers(Set reportedPeers) { private boolean removePersistedPeer(Peer persistedPeer) { if (persistedPeers.contains(persistedPeer)) { persistedPeers.remove(persistedPeer); - peerList.setAll(persistedPeers); - persistenceManager.requestPersistence(); + requestPersistence(); return true; } else { return false; } } + private void requestPersistence() { + peerList.setAll(persistedPeers); + persistenceManager.requestPersistence(); + } + @SuppressWarnings("UnusedReturnValue") private boolean removePersistedPeer(NodeAddress nodeAddress) { - Optional persistedPeerOptional = getPersistedPeerOptional(nodeAddress); - return persistedPeerOptional.isPresent() && removePersistedPeer(persistedPeerOptional.get()); + Optional optionalPersistedPeer = getOptionalPersistedPeer(nodeAddress); + return optionalPersistedPeer.isPresent() && removePersistedPeer(optionalPersistedPeer.get()); } - private Optional getPersistedPeerOptional(NodeAddress nodeAddress) { + private Optional getOptionalPersistedPeer(NodeAddress nodeAddress) { return persistedPeers.stream() - .filter(e -> e.getNodeAddress().equals(nodeAddress)).findAny(); + .filter(e -> e.getNodeAddress().equals(nodeAddress)) + .findAny(); } private void removeTooOldPersistedPeers() { @@ -590,10 +635,6 @@ private void purgePersistedPeersIfExceeds() { } } - public Set getPersistedPeers() { - return persistedPeers; - } - /////////////////////////////////////////////////////////////////////////////////////////// // Misc @@ -644,7 +685,7 @@ public void handleConnectionFault(NodeAddress nodeAddress, @Nullable Connection log.debug("handleConnectionFault called: nodeAddress=" + nodeAddress); boolean doRemovePersistedPeer = false; removeReportedPeer(nodeAddress); - Optional persistedPeerOptional = getPersistedPeerOptional(nodeAddress); + Optional persistedPeerOptional = getOptionalPersistedPeer(nodeAddress); if (persistedPeerOptional.isPresent()) { Peer persistedPeer = persistedPeerOptional.get(); persistedPeer.increaseFailedConnectionAttempts(); @@ -674,17 +715,27 @@ public void shutDownConnection(NodeAddress peersNodeAddress, CloseConnectionReas // Delivers the live peers from the last 30 min (MAX_AGE_LIVE_PEERS) // We include older peers to avoid risks for network partitioning - public Set getLivePeers(NodeAddress excludedNodeAddress) { + public Set getLivePeers() { + return getLivePeers(null); + } + + public Set getLivePeers(@Nullable NodeAddress excludedNodeAddress) { int oldNumLatestLivePeers = latestLivePeers.size(); - Set currentLivePeers = new HashSet<>(getConnectedReportedPeers().stream() + + Set peers = new HashSet<>(latestLivePeers); + Set currentLivePeers = getConnectedReportedPeers().stream() .filter(e -> !isSeedNode(e)) .filter(e -> !e.getNodeAddress().equals(excludedNodeAddress)) - .collect(Collectors.toSet())); - latestLivePeers.addAll(currentLivePeers); + .collect(Collectors.toSet()); + peers.addAll(currentLivePeers); + long maxAge = new Date().getTime() - MAX_AGE_LIVE_PEERS; - latestLivePeers = latestLivePeers.stream() - .filter(peer -> peer.getDate().getTime() > maxAge) + latestLivePeers.clear(); + Set recentPeers = peers.stream() + .filter(peer -> peer.getDateAsLong() > maxAge) .collect(Collectors.toSet()); + latestLivePeers.addAll(recentPeers); + if (oldNumLatestLivePeers != latestLivePeers.size()) log.info("Num of latestLivePeers={}", latestLivePeers.size()); return latestLivePeers; @@ -703,17 +754,29 @@ private Set getConnectedReportedPeers() { // If we have a new connection the supportedCapabilities is empty. // We lookup if we have already stored the supportedCapabilities at the persisted or reported peers // and if so we use that. + Optional peersNodeAddressOptional = connection.getPeersNodeAddressOptional(); + checkArgument(peersNodeAddressOptional.isPresent()); // getConfirmedConnections delivers only connections where we know the address + boolean getCapabilitiesFromConnection = !supportedCapabilities.isEmpty(); if (supportedCapabilities.isEmpty()) { + // If not set we look up if we got the Capabilities set from any of the reported or persisted peers Set allPeers = new HashSet<>(getPersistedPeers()); allPeers.addAll(getReportedPeers()); - Optional ourPeer = allPeers.stream().filter(peer -> peer.getNodeAddress().equals(connection.getPeersNodeAddressOptional().get())) + Optional ourPeer = allPeers.stream() + .filter(peer -> peer.getNodeAddress().equals(peersNodeAddressOptional.get())) .filter(peer -> !peer.getCapabilities().isEmpty()) .findAny(); - if (ourPeer.isPresent()) + if (ourPeer.isPresent()) { supportedCapabilities = new Capabilities(ourPeer.get().getCapabilities()); + } + } + Peer peer = new Peer(peersNodeAddressOptional.get(), supportedCapabilities); + + // If we only got the capabilities from a reported peer of did not get any we add a listener, + // so once we get a connection with that peer and exchange a message containing the capabilities + // we get set the capabilities + if (!getCapabilitiesFromConnection) { + connection.addWeakCapabilitiesListener(peer); } - Peer peer = new Peer(connection.getPeersNodeAddressOptional().get(), supportedCapabilities); - connection.addWeakCapabilitiesListener(peer); return peer; }) .collect(Collectors.toSet()); @@ -730,8 +793,8 @@ private void printConnectedPeers() { if (!networkNode.getConfirmedConnections().isEmpty()) { StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" + "Connected peers for node " + networkNode.getNodeAddress() + ":"); - networkNode.getConfirmedConnections().stream().forEach(e -> result.append("\n") - .append(e.getPeersNodeAddressOptional().get()).append(" ").append(e.getPeerType())); + networkNode.getConfirmedConnections().forEach(e -> result.append("\n") + .append(e.getPeersNodeAddressOptional()).append(" ").append(e.getPeerType())); result.append("\n------------------------------------------------------------\n"); log.debug(result.toString()); } From 25526750a81dffd2b8c75fae4beb575a6f27b035 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 15:22:50 -0500 Subject: [PATCH 047/123] Replace persistedPeers with peerList --- .../bisq/network/p2p/peers/PeerManager.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index a45e7a975a2..57641fd2d23 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -43,6 +43,7 @@ import com.google.common.annotations.VisibleForTesting; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.HashSet; @@ -108,9 +109,6 @@ public interface Listener { // Persistable peerList private final PeerList peerList = new PeerList(); - // Peers we had persisted TODO use peerList instead - @Getter - private final Set persistedPeers = new HashSet<>(); // Peers we got reported from other peers @Getter private final Set reportedPeers = new HashSet<>(); @@ -188,7 +186,7 @@ public void shutDown() { public void readPersisted() { PeerList persisted = persistenceManager.getPersisted(); if (persisted != null) { - this.persistedPeers.addAll(persisted.getList()); + peerList.setAll(persisted.getList()); } } @@ -251,11 +249,16 @@ public Optional findPeer(NodeAddress peersNodeAddress) { public Set getAllPeers() { Set allPeers = new HashSet<>(getLivePeers()); - allPeers.addAll(persistedPeers); + allPeers.addAll(peerList.getList()); allPeers.addAll(reportedPeers); return allPeers; } + public Collection getPersistedPeers() { + return peerList.getList(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// // ConnectionListener implementation /////////////////////////////////////////////////////////////////////////////////////////// @@ -507,7 +510,7 @@ public void addToReportedPeers(Set reportedPeersToAdd, reportedPeers.addAll(reportedPeersToAdd); purgeReportedPeersIfExceeds(); - persistedPeers.addAll(reportedPeersToAdd); + peerList.getList().addAll(reportedPeersToAdd); purgePersistedPeersIfExceeds(); requestPersistence(); @@ -582,8 +585,8 @@ private void printNewReportedPeers(Set reportedPeers) { /////////////////////////////////////////////////////////////////////////////////////////// private boolean removePersistedPeer(Peer persistedPeer) { - if (persistedPeers.contains(persistedPeer)) { - persistedPeers.remove(persistedPeer); + if (peerList.getList().contains(persistedPeer)) { + peerList.getList().remove(persistedPeer); requestPersistence(); return true; } else { @@ -592,7 +595,6 @@ private boolean removePersistedPeer(Peer persistedPeer) { } private void requestPersistence() { - peerList.setAll(persistedPeers); persistenceManager.requestPersistence(); } @@ -603,27 +605,27 @@ private boolean removePersistedPeer(NodeAddress nodeAddress) { } private Optional getOptionalPersistedPeer(NodeAddress nodeAddress) { - return persistedPeers.stream() + return peerList.getList().stream() .filter(e -> e.getNodeAddress().equals(nodeAddress)) .findAny(); } private void removeTooOldPersistedPeers() { - Set persistedPeersToRemove = persistedPeers.stream() + Set persistedPeersToRemove = peerList.getList().stream() .filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE) .collect(Collectors.toSet()); persistedPeersToRemove.forEach(this::removePersistedPeer); } private void purgePersistedPeersIfExceeds() { - int size = persistedPeers.size(); + int size = peerList.getList().size(); int limit = MAX_PERSISTED_PEERS; if (size > limit) { log.trace("We have already {} persisted peers which exceeds our limit of {}." + "We remove random peers from the persisted peers list.", size, limit); int diff = size - limit; - List list = new ArrayList<>(persistedPeers); - // we dont use sorting by lastActivityDate to avoid attack vectors and keep it more random + List list = new ArrayList<>(peerList.getList()); + // we don't use sorting by lastActivityDate to avoid attack vectors and keep it more random for (int i = 0; i < diff; i++) { if (!list.isEmpty()) { Peer toRemove = list.remove(new Random().nextInt(list.size())); From bf674ea0cf3c67b4dcfd6a2ae437bdd30ed96066 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 15:34:31 -0500 Subject: [PATCH 048/123] Use getPersistedPeers for peerList.getList() calls Rename getOptionalPersistedPeer to findPersistedPeer Improve getConnectedReportedPeers method --- .../bisq/network/p2p/peers/PeerManager.java | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 57641fd2d23..8fa179a6f49 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -112,7 +112,7 @@ public interface Listener { // Peers we got reported from other peers @Getter private final Set reportedPeers = new HashSet<>(); - // Peer of last 30 min. + // Most recent peers with activity date of last 30 min. private final Set latestLivePeers = new HashSet<>(); private Timer checkMaxConnectionsTimer; @@ -249,7 +249,7 @@ public Optional findPeer(NodeAddress peersNodeAddress) { public Set getAllPeers() { Set allPeers = new HashSet<>(getLivePeers()); - allPeers.addAll(peerList.getList()); + allPeers.addAll(getPersistedPeers()); allPeers.addAll(reportedPeers); return allPeers; } @@ -510,7 +510,7 @@ public void addToReportedPeers(Set reportedPeersToAdd, reportedPeers.addAll(reportedPeersToAdd); purgeReportedPeersIfExceeds(); - peerList.getList().addAll(reportedPeersToAdd); + getPersistedPeers().addAll(reportedPeersToAdd); purgePersistedPeersIfExceeds(); requestPersistence(); @@ -585,8 +585,8 @@ private void printNewReportedPeers(Set reportedPeers) { /////////////////////////////////////////////////////////////////////////////////////////// private boolean removePersistedPeer(Peer persistedPeer) { - if (peerList.getList().contains(persistedPeer)) { - peerList.getList().remove(persistedPeer); + if (getPersistedPeers().contains(persistedPeer)) { + getPersistedPeers().remove(persistedPeer); requestPersistence(); return true; } else { @@ -600,31 +600,31 @@ private void requestPersistence() { @SuppressWarnings("UnusedReturnValue") private boolean removePersistedPeer(NodeAddress nodeAddress) { - Optional optionalPersistedPeer = getOptionalPersistedPeer(nodeAddress); + Optional optionalPersistedPeer = findPersistedPeer(nodeAddress); return optionalPersistedPeer.isPresent() && removePersistedPeer(optionalPersistedPeer.get()); } - private Optional getOptionalPersistedPeer(NodeAddress nodeAddress) { - return peerList.getList().stream() + private Optional findPersistedPeer(NodeAddress nodeAddress) { + return getPersistedPeers().stream() .filter(e -> e.getNodeAddress().equals(nodeAddress)) .findAny(); } private void removeTooOldPersistedPeers() { - Set persistedPeersToRemove = peerList.getList().stream() + Set persistedPeersToRemove = getPersistedPeers().stream() .filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE) .collect(Collectors.toSet()); persistedPeersToRemove.forEach(this::removePersistedPeer); } private void purgePersistedPeersIfExceeds() { - int size = peerList.getList().size(); + int size = getPersistedPeers().size(); int limit = MAX_PERSISTED_PEERS; if (size > limit) { log.trace("We have already {} persisted peers which exceeds our limit of {}." + "We remove random peers from the persisted peers list.", size, limit); int diff = size - limit; - List list = new ArrayList<>(peerList.getList()); + List list = new ArrayList<>(getPersistedPeers()); // we don't use sorting by lastActivityDate to avoid attack vectors and keep it more random for (int i = 0; i < diff; i++) { if (!list.isEmpty()) { @@ -687,7 +687,7 @@ public void handleConnectionFault(NodeAddress nodeAddress, @Nullable Connection log.debug("handleConnectionFault called: nodeAddress=" + nodeAddress); boolean doRemovePersistedPeer = false; removeReportedPeer(nodeAddress); - Optional persistedPeerOptional = getOptionalPersistedPeer(nodeAddress); + Optional persistedPeerOptional = findPersistedPeer(nodeAddress); if (persistedPeerOptional.isPresent()) { Peer persistedPeer = persistedPeerOptional.get(); persistedPeer.increaseFailedConnectionAttempts(); @@ -758,25 +758,27 @@ private Set getConnectedReportedPeers() { // and if so we use that. Optional peersNodeAddressOptional = connection.getPeersNodeAddressOptional(); checkArgument(peersNodeAddressOptional.isPresent()); // getConfirmedConnections delivers only connections where we know the address - boolean getCapabilitiesFromConnection = !supportedCapabilities.isEmpty(); - if (supportedCapabilities.isEmpty()) { - // If not set we look up if we got the Capabilities set from any of the reported or persisted peers - Set allPeers = new HashSet<>(getPersistedPeers()); - allPeers.addAll(getReportedPeers()); - Optional ourPeer = allPeers.stream() - .filter(peer -> peer.getNodeAddress().equals(peersNodeAddressOptional.get())) + NodeAddress peersNodeAddress = peersNodeAddressOptional.get(); + boolean capabilitiesNotFoundInConnection = supportedCapabilities.isEmpty(); + if (capabilitiesNotFoundInConnection) { + // If not found in connection we look up if we got the Capabilities set from any of the + // reported or persisted peers + Set persistedAndReported = new HashSet<>(getPersistedPeers()); + persistedAndReported.addAll(getReportedPeers()); + Optional candidate = persistedAndReported.stream() + .filter(peer -> peer.getNodeAddress().equals(peersNodeAddress)) .filter(peer -> !peer.getCapabilities().isEmpty()) .findAny(); - if (ourPeer.isPresent()) { - supportedCapabilities = new Capabilities(ourPeer.get().getCapabilities()); + if (candidate.isPresent()) { + supportedCapabilities = new Capabilities(candidate.get().getCapabilities()); } } - Peer peer = new Peer(peersNodeAddressOptional.get(), supportedCapabilities); + Peer peer = new Peer(peersNodeAddress, supportedCapabilities); - // If we only got the capabilities from a reported peer of did not get any we add a listener, + // If we did not found the capability from our own connection we add a listener, // so once we get a connection with that peer and exchange a message containing the capabilities - // we get set the capabilities - if (!getCapabilitiesFromConnection) { + // we get set the capabilities. + if (capabilitiesNotFoundInConnection) { connection.addWeakCapabilitiesListener(peer); } return peer; From cf0693098f5aee9d2a795ced5a241652882e0709 Mon Sep 17 00:00:00 2001 From: chimp1984 <54558767+chimp1984@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:05:39 -0500 Subject: [PATCH 049/123] Update common/src/main/java/bisq/common/app/Capability.java Co-authored-by: sqrrm --- common/src/main/java/bisq/common/app/Capability.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/bisq/common/app/Capability.java b/common/src/main/java/bisq/common/app/Capability.java index 9379c9dd392..8ffb042844d 100644 --- a/common/src/main/java/bisq/common/app/Capability.java +++ b/common/src/main/java/bisq/common/app/Capability.java @@ -41,5 +41,5 @@ public enum Capability { MEDIATION, // Supports mediation feature REFUND_AGENT, // Supports refund agents TRADE_STATISTICS_HASH_UPDATE, // We changed the hash method in 1.2.0 and that requires update to 1.2.2 for handling it correctly, otherwise the seed nodes have to process too much data. - NO_ADDRESS_PRE_FIX // At 1.4.0 we removed the prefix filter for mailbox messages. If a peer has that capability we do not sent the prefix. + NO_ADDRESS_PRE_FIX // At 1.4.0 we removed the prefix filter for mailbox messages. If a peer has that capability we do not sent the prefix. } From 983f610e730c2417773220deae779b6bfe014cc0 Mon Sep 17 00:00:00 2001 From: chimp1984 <54558767+chimp1984@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:14:49 -0500 Subject: [PATCH 050/123] Update p2p/src/main/java/bisq/network/p2p/network/Connection.java Co-authored-by: sqrrm --- p2p/src/main/java/bisq/network/p2p/network/Connection.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/Connection.java b/p2p/src/main/java/bisq/network/p2p/network/Connection.java index 6a29c7e0343..dca03c133de 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Connection.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Connection.java @@ -900,11 +900,10 @@ protected boolean handleSupportedCapabilitiesMessage(NetworkEnvelope networkEnve @Nullable private NodeAddress getSenderNodeAddress(NetworkEnvelope networkEnvelope) { - return getPeersNodeAddressOptional().isPresent() ? - getPeersNodeAddressOptional().get() : + return getPeersNodeAddressOptional().orElse( networkEnvelope instanceof SendersNodeAddressMessage ? ((SendersNodeAddressMessage) networkEnvelope).getSenderNodeAddress() : - null; + null); } private String getSenderNodeAddressAsString(NetworkEnvelope networkEnvelope) { From 31e7e2655792b656ee6747919c7fd449a2d96850 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 18:16:34 -0500 Subject: [PATCH 051/123] Fix incorrect handling of decryptedEntries size --- p2p/src/main/java/bisq/network/p2p/P2PService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 2004cf5e1b6..0e60769ea66 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -450,9 +450,11 @@ public void onAdded(Collection protectedStorageEntries) { } private void processSingleMailboxEntry(Collection protectedMailboxStorageEntries) { + checkArgument(protectedMailboxStorageEntries.size() == 1); var decryptedEntries = new ArrayList<>(getDecryptedEntries(protectedMailboxStorageEntries)); - checkArgument(decryptedEntries.size() == 1); - storeMailboxDataAndNotifyListeners(decryptedEntries.get(0)); + if (protectedMailboxStorageEntries.size() == 1) { + storeMailboxDataAndNotifyListeners(decryptedEntries.get(0)); + } } // We run the batch processing of all mailbox messages we have received at startup in a thread to not block the UI. From 4575516d19293fe535eddbd54ef9146d69e6e349 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 18:16:52 -0500 Subject: [PATCH 052/123] Refactor: Return early --- .../java/bisq/network/p2p/peers/PeerManager.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 8fa179a6f49..167d01a7da9 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -524,14 +524,16 @@ public void addToReportedPeers(Set reportedPeersToAdd, } private void applyCapabilities(Connection connection, Capabilities capabilities) { - if (capabilities != null && !capabilities.isEmpty()) { - connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { - getAllPeers().stream() - .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) - .forEach(peer -> peer.setCapabilities(capabilities)); - }); - requestPersistence(); + if (capabilities == null || capabilities.isEmpty()) { + return; } + + connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { + getAllPeers().stream() + .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) + .forEach(peer -> peer.setCapabilities(capabilities)); + }); + requestPersistence(); } private void purgeReportedPeersIfExceeds() { From ed960aba3fc7a60233c08402cff2ad4d73bc7b01 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 18:44:04 -0500 Subject: [PATCH 053/123] Refactor: Rearrange code, remove unused methods, renamings (no functional change) --- .../bisq/network/p2p/peers/PeerManager.java | 454 +++++++++--------- 1 file changed, 216 insertions(+), 238 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 167d01a7da9..22712ffb3d9 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -64,7 +64,7 @@ import static com.google.common.base.Preconditions.checkArgument; @Slf4j -public class PeerManager implements ConnectionListener, PersistedDataHost { +public final class PeerManager implements ConnectionListener, PersistedDataHost { /////////////////////////////////////////////////////////////////////////////////////////// // Static @@ -104,7 +104,7 @@ public interface Listener { private final ClockWatcher clockWatcher; private final Set seedNodeAddresses; private final PersistenceManager persistenceManager; - private final ClockWatcher.Listener listener; + private final ClockWatcher.Listener clockWatcherListener; private final List listeners = new CopyOnWriteArrayList<>(); // Persistable peerList @@ -151,7 +151,7 @@ public PeerManager(NetworkNode networkNode, setConnectionLimits(maxConnections); // we check if app was idle for more then 5 sec. - listener = new ClockWatcher.Listener() { + clockWatcherListener = new ClockWatcher.Listener() { @Override public void onSecondTick() { } @@ -168,18 +168,18 @@ public void onAwakeFromStandby(long missedMs) { listeners.forEach(Listener::onAwakeFromStandby); } }; - clockWatcher.addListener(listener); + clockWatcher.addListener(clockWatcherListener); } public void shutDown() { networkNode.removeConnectionListener(this); - clockWatcher.removeListener(listener); + clockWatcher.removeListener(clockWatcherListener); stopCheckMaxConnectionsTimer(); } /////////////////////////////////////////////////////////////////////////////////////////// - // API + // PersistedDataHost implementation /////////////////////////////////////////////////////////////////////////////////////////// @Override @@ -190,57 +190,123 @@ public void readPersisted() { } } - public int getMaxConnections() { - return maxConnectionsAbsolute; + + /////////////////////////////////////////////////////////////////////////////////////////// + // ConnectionListener implementation + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onConnection(Connection connection) { + if (isSeedNode(connection)) { + connection.setPeerType(Connection.PeerType.SEED_NODE); + } + + doHouseKeeping(); + + if (lostAllConnections) { + lostAllConnections = false; + stopped = false; + listeners.forEach(Listener::onNewConnectionAfterAllConnectionsLost); + } } - public void addListener(Listener listener) { - listeners.add(listener); + @Override + public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { + log.info("onDisconnect called: nodeAddress={}, closeConnectionReason={}", + connection.getPeersNodeAddressOptional(), closeConnectionReason); + handleConnectionFault(connection); + lostAllConnections = networkNode.getAllConnections().isEmpty(); + if (lostAllConnections) { + stopped = true; + log.warn("\n------------------------------------------------------------\n" + + "All connections lost\n" + + "------------------------------------------------------------"); + listeners.forEach(Listener::onAllConnectionsLost); + } + maybeRemoveBannedPeer(closeConnectionReason, connection); } - public void removeListener(Listener listener) { - listeners.remove(listener); + @Override + public void onError(Throwable throwable) { } - // Modify this to change the relationships between connection limits. - // maxConnections default 12 - private void setConnectionLimits(int maxConnections) { - this.maxConnections = maxConnections; // app node 12; seedNode 30 - minConnections = Math.max(1, (int) Math.round(maxConnections * 0.7)); // app node 1-8; seedNode 21 - disconnectFromSeedNode = maxConnections; // app node 12; seedNode 30 - maxConnectionsPeer = Math.max(4, (int) Math.round(maxConnections * 1.3)); // app node 16; seedNode 39 - maxConnectionsNonDirect = Math.max(8, (int) Math.round(maxConnections * 1.7)); // app node 20; seedNode 51 - maxConnectionsAbsolute = Math.max(12, (int) Math.round(maxConnections * 2.5)); // app node 30; seedNode 66 + + /////////////////////////////////////////////////////////////////////////////////////////// + // Connection + /////////////////////////////////////////////////////////////////////////////////////////// + + public boolean hasSufficientConnections() { + return networkNode.getConfirmedConnections().size() >= minConnections; } + // Checks if that connection has the peers node address + public boolean isConfirmed(NodeAddress nodeAddress) { + return networkNode.getNodeAddressesOfConfirmedConnections().contains(nodeAddress); + } - public boolean peerHasCapability(NodeAddress peersNodeAddress, Capability capability) { - return findPeersCapabilities(peersNodeAddress) - .map(capabilities -> capabilities.contains(capability)) - .orElse(false); + public void handleConnectionFault(Connection connection) { + connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> handleConnectionFault(nodeAddress, connection)); } - public Optional findPeersCapabilities(NodeAddress nodeAddress) { - // We look up first our connections as that is our own data. If not found there we look up the peers which - // include reported peers. - Optional optionalCapabilities = networkNode.findPeersCapabilities(nodeAddress); - if (optionalCapabilities.isPresent()) { - return optionalCapabilities; + public void handleConnectionFault(NodeAddress nodeAddress) { + handleConnectionFault(nodeAddress, null); + } + + public void handleConnectionFault(NodeAddress nodeAddress, @Nullable Connection connection) { + log.debug("handleConnectionFault called: nodeAddress=" + nodeAddress); + boolean doRemovePersistedPeer = false; + removeReportedPeer(nodeAddress); + Optional persistedPeerOptional = findPersistedPeer(nodeAddress); + if (persistedPeerOptional.isPresent()) { + Peer persistedPeer = persistedPeerOptional.get(); + persistedPeer.increaseFailedConnectionAttempts(); + doRemovePersistedPeer = persistedPeer.tooManyFailedConnectionAttempts(); } + doRemovePersistedPeer = doRemovePersistedPeer || (connection != null && connection.getRuleViolation() != null); - // Reported peers are not trusted data. We could get capabilities which miss the - // peers real capability or we could get maliciously altered capabilities telling us the peer supports a - // capability which is in fact not supported. This could lead to connection loss as we might send data not - // recognized by the peer. As we register a listener on connection if we don't have set the capability from our - // own sources we would get it fixed as soon we have a connection with that peer, rendering such an attack - // inefficient. - // Also this risk is only for not updated peers, so in case that would be abused for an - // attack all users have a strong incentive to update ;-). - return getAllPeers().stream() - .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) - .findAny().map(Peer::getCapabilities); + if (doRemovePersistedPeer) + removePersistedPeer(nodeAddress); + else + removeTooOldPersistedPeers(); } + public boolean isSeedNode(Connection connection) { + //TODO + return connection.hasPeersNodeAddress() && seedNodeAddresses.contains(connection.getPeersNodeAddressOptional().get()); + } + + public boolean isSelf(NodeAddress nodeAddress) { + return nodeAddress.equals(networkNode.getNodeAddress()); + } + + private boolean isSeedNode(Peer peer) { + return seedNodeAddresses.contains(peer.getNodeAddress()); + } + + public boolean isSeedNode(NodeAddress nodeAddress) { + return seedNodeAddresses.contains(nodeAddress); + } + + //TODO rename + public boolean isNodeBanned(CloseConnectionReason closeConnectionReason, Connection connection) { + return closeConnectionReason == CloseConnectionReason.PEER_BANNED && + connection.getPeersNodeAddressOptional().isPresent(); + } + + private void maybeRemoveBannedPeer(CloseConnectionReason closeConnectionReason, Connection connection) { + if (connection.getPeersNodeAddressOptional().isPresent() && isNodeBanned(closeConnectionReason, connection)) { + NodeAddress nodeAddress = connection.getPeersNodeAddressOptional().get(); + seedNodeAddresses.remove(nodeAddress); + removePersistedPeer(nodeAddress); + removeReportedPeer(nodeAddress); + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Peer + /////////////////////////////////////////////////////////////////////////////////////////// + public Optional findPeer(NodeAddress peersNodeAddress) { return getAllPeers().stream() .filter(peer -> peer.getNodeAddress().equals(peersNodeAddress)) @@ -258,71 +324,102 @@ public Collection getPersistedPeers() { return peerList.getList(); } + public void addToReportedPeers(Set reportedPeersToAdd, + Connection connection, + Capabilities capabilities) { + applyCapabilities(connection, capabilities); - /////////////////////////////////////////////////////////////////////////////////////////// - // ConnectionListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// + printNewReportedPeers(reportedPeersToAdd); - @Override - public void onConnection(Connection connection) { - boolean seedNode = isSeedNode(connection); - Optional addressOptional = connection.getPeersNodeAddressOptional(); - if (log.isDebugEnabled()) { - String peer = addressOptional.map(NodeAddress::getFullAddress).orElseGet(() -> - "not known yet (connection id=" + connection.getUid() + ")"); - log.debug("onConnection: peer = {}{}", - peer, - seedNode ? " (SeedNode)" : ""); + // We check if the reported msg is not violating our rules + if (reportedPeersToAdd.size() <= (MAX_REPORTED_PEERS + maxConnectionsAbsolute + 10)) { + reportedPeers.addAll(reportedPeersToAdd); + purgeReportedPeersIfExceeds(); + + getPersistedPeers().addAll(reportedPeersToAdd); + purgePersistedPeersIfExceeds(); + requestPersistence(); + + printReportedPeers(); + } else { + // If a node is trying to send too many list we treat it as rule violation. + // Reported list include the connected list. We use the max value and give some extra headroom. + // Will trigger a shutdown after 2nd time sending too much + connection.reportInvalidRequest(RuleViolation.TOO_MANY_REPORTED_PEERS_SENT); } + } - if (seedNode) - connection.setPeerType(Connection.PeerType.SEED_NODE); + // Delivers the live peers from the last 30 min (MAX_AGE_LIVE_PEERS) + // We include older peers to avoid risks for network partitioning + public Set getLivePeers() { + return getLivePeers(null); + } - doHouseKeeping(); + public Set getLivePeers(@Nullable NodeAddress excludedNodeAddress) { + int oldNumLatestLivePeers = latestLivePeers.size(); - if (lostAllConnections) { - lostAllConnections = false; - stopped = false; - listeners.stream().forEach(Listener::onNewConnectionAfterAllConnectionsLost); - } + Set peers = new HashSet<>(latestLivePeers); + Set currentLivePeers = getConnectedReportedPeers().stream() + .filter(e -> !isSeedNode(e)) + .filter(e -> !e.getNodeAddress().equals(excludedNodeAddress)) + .collect(Collectors.toSet()); + peers.addAll(currentLivePeers); + + long maxAge = new Date().getTime() - MAX_AGE_LIVE_PEERS; + latestLivePeers.clear(); + Set recentPeers = peers.stream() + .filter(peer -> peer.getDateAsLong() > maxAge) + .collect(Collectors.toSet()); + latestLivePeers.addAll(recentPeers); + + if (oldNumLatestLivePeers != latestLivePeers.size()) + log.info("Num of latestLivePeers={}", latestLivePeers.size()); + return latestLivePeers; } - @Override - public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - log.info("onDisconnect called: nodeAddress={}, closeConnectionReason={}", connection.getPeersNodeAddressOptional(), closeConnectionReason); - final Optional addressOptional = connection.getPeersNodeAddressOptional(); - log.debug("onDisconnect: peer = {}{} / closeConnectionReason: {}", - (addressOptional.isPresent() ? addressOptional.get().getFullAddress() : "not known yet (connection id=" + connection.getUid() + ")"), - isSeedNode(connection) ? " (SeedNode)" : "", - closeConnectionReason); + /////////////////////////////////////////////////////////////////////////////////////////// + // Capabilities + /////////////////////////////////////////////////////////////////////////////////////////// - handleConnectionFault(connection); + public boolean peerHasCapability(NodeAddress peersNodeAddress, Capability capability) { + return findPeersCapabilities(peersNodeAddress) + .map(capabilities -> capabilities.contains(capability)) + .orElse(false); + } - lostAllConnections = networkNode.getAllConnections().isEmpty(); - if (lostAllConnections) { - stopped = true; - log.warn("\n------------------------------------------------------------\n" + - "All connections lost\n" + - "------------------------------------------------------------"); - listeners.stream().forEach(Listener::onAllConnectionsLost); + public Optional findPeersCapabilities(NodeAddress nodeAddress) { + // We look up first our connections as that is our own data. If not found there we look up the peers which + // include reported peers. + Optional optionalCapabilities = networkNode.findPeersCapabilities(nodeAddress); + if (optionalCapabilities.isPresent()) { + return optionalCapabilities; } - if (connection.getPeersNodeAddressOptional().isPresent() && isNodeBanned(closeConnectionReason, connection)) { - final NodeAddress nodeAddress = connection.getPeersNodeAddressOptional().get(); - seedNodeAddresses.remove(nodeAddress); - removePersistedPeer(nodeAddress); - removeReportedPeer(nodeAddress); - } + // Reported peers are not trusted data. We could get capabilities which miss the + // peers real capability or we could get maliciously altered capabilities telling us the peer supports a + // capability which is in fact not supported. This could lead to connection loss as we might send data not + // recognized by the peer. As we register a listener on connection if we don't have set the capability from our + // own sources we would get it fixed as soon we have a connection with that peer, rendering such an attack + // inefficient. + // Also this risk is only for not updated peers, so in case that would be abused for an + // attack all users have a strong incentive to update ;-). + return getAllPeers().stream() + .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) + .findAny().map(Peer::getCapabilities); } - public boolean isNodeBanned(CloseConnectionReason closeConnectionReason, Connection connection) { - return closeConnectionReason == CloseConnectionReason.PEER_BANNED && - connection.getPeersNodeAddressOptional().isPresent(); - } + private void applyCapabilities(Connection connection, Capabilities capabilities) { + if (capabilities == null || capabilities.isEmpty()) { + return; + } - @Override - public void onError(Throwable throwable) { + connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { + getAllPeers().stream() + .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) + .forEach(peer -> peer.setCapabilities(capabilities)); + }); + requestPersistence(); } @@ -463,29 +560,22 @@ private void removeSuperfluousSeedNodes() { } } - /////////////////////////////////////////////////////////////////////////////////////////// // Reported peers /////////////////////////////////////////////////////////////////////////////////////////// - private boolean removeReportedPeer(Peer reportedPeer) { - boolean contained = reportedPeers.remove(reportedPeer); + private void removeReportedPeer(Peer reportedPeer) { + reportedPeers.remove(reportedPeer); printReportedPeers(); - return contained; } - @SuppressWarnings("UnusedReturnValue") - @Nullable - private Peer removeReportedPeer(NodeAddress nodeAddress) { + private void removeReportedPeer(NodeAddress nodeAddress) { List reportedPeersClone = new ArrayList<>(reportedPeers); Optional reportedPeerOptional = reportedPeersClone.stream() .filter(e -> e.getNodeAddress().equals(nodeAddress)).findAny(); if (reportedPeerOptional.isPresent()) { Peer reportedPeer = reportedPeerOptional.get(); removeReportedPeer(reportedPeer); - return reportedPeer; - } else { - return null; } } @@ -497,44 +587,6 @@ private void removeTooOldReportedPeers() { reportedPeersToRemove.forEach(this::removeReportedPeer); } - public void addToReportedPeers(Set reportedPeersToAdd, - Connection connection, - Capabilities capabilities) { - - applyCapabilities(connection, capabilities); - - printNewReportedPeers(reportedPeersToAdd); - - // We check if the reported msg is not violating our rules - if (reportedPeersToAdd.size() <= (MAX_REPORTED_PEERS + maxConnectionsAbsolute + 10)) { - reportedPeers.addAll(reportedPeersToAdd); - purgeReportedPeersIfExceeds(); - - getPersistedPeers().addAll(reportedPeersToAdd); - purgePersistedPeersIfExceeds(); - requestPersistence(); - - printReportedPeers(); - } else { - // If a node is trying to send too many list we treat it as rule violation. - // Reported list include the connected list. We use the max value and give some extra headroom. - // Will trigger a shutdown after 2nd time sending too much - connection.reportInvalidRequest(RuleViolation.TOO_MANY_REPORTED_PEERS_SENT); - } - } - - private void applyCapabilities(Connection connection, Capabilities capabilities) { - if (capabilities == null || capabilities.isEmpty()) { - return; - } - - connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { - getAllPeers().stream() - .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) - .forEach(peer -> peer.setCapabilities(capabilities)); - }); - requestPersistence(); - } private void purgeReportedPeersIfExceeds() { int size = reportedPeers.size(); @@ -543,7 +595,7 @@ private void purgeReportedPeersIfExceeds() { "We remove random peers from the reported peers list.", size, MAX_REPORTED_PEERS); int diff = size - MAX_REPORTED_PEERS; List list = new ArrayList<>(reportedPeers); - // we dont use sorting by lastActivityDate to keep it more random + // we don't use sorting by lastActivityDate to keep it more random for (int i = 0; i < diff; i++) { if (!list.isEmpty()) { Peer toRemove = list.remove(new Random().nextInt(list.size())); @@ -557,12 +609,11 @@ private void purgeReportedPeersIfExceeds() { private void printReportedPeers() { if (!reportedPeers.isEmpty()) { - //noinspection ConstantConditions if (PRINT_REPORTED_PEERS_DETAILS) { StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" + "Collected reported peers:"); List reportedPeersClone = new ArrayList<>(reportedPeers); - reportedPeersClone.stream().forEach(e -> result.append("\n").append(e)); + reportedPeersClone.forEach(e -> result.append("\n").append(e)); result.append("\n------------------------------------------------------------\n"); log.trace(result.toString()); } @@ -571,7 +622,6 @@ private void printReportedPeers() { } private void printNewReportedPeers(Set reportedPeers) { - //noinspection ConstantConditions if (PRINT_REPORTED_PEERS_DETAILS) { StringBuilder result = new StringBuilder("We received new reportedPeers:"); List reportedPeersClone = new ArrayList<>(reportedPeers); @@ -583,7 +633,7 @@ private void printNewReportedPeers(Set reportedPeers) { /////////////////////////////////////////////////////////////////////////////////////////// - // Persisted list + // Persisted peers /////////////////////////////////////////////////////////////////////////////////////////// private boolean removePersistedPeer(Peer persistedPeer) { @@ -641,114 +691,42 @@ private void purgePersistedPeersIfExceeds() { /////////////////////////////////////////////////////////////////////////////////////////// - // Misc + // Getters /////////////////////////////////////////////////////////////////////////////////////////// - public boolean hasSufficientConnections() { - return networkNode.getNodeAddressesOfConfirmedConnections().size() >= minConnections; - } - - private boolean isSeedNode(Peer reportedPeer) { - return seedNodeAddresses.contains(reportedPeer.getNodeAddress()); - } - - public boolean isSeedNode(NodeAddress nodeAddress) { - return seedNodeAddresses.contains(nodeAddress); - } - - public boolean isSeedNode(Connection connection) { - return connection.hasPeersNodeAddress() && seedNodeAddresses.contains(connection.getPeersNodeAddressOptional().get()); - } - - public boolean isSelf(Peer reportedPeer) { - return isSelf(reportedPeer.getNodeAddress()); - } - - public boolean isSelf(NodeAddress nodeAddress) { - return nodeAddress.equals(networkNode.getNodeAddress()); - } - - public boolean isConfirmed(Peer reportedPeer) { - return isConfirmed(reportedPeer.getNodeAddress()); - } - - // Checks if that connection has the peers node address - public boolean isConfirmed(NodeAddress nodeAddress) { - return networkNode.getNodeAddressesOfConfirmedConnections().contains(nodeAddress); - } - - public void handleConnectionFault(Connection connection) { - connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> handleConnectionFault(nodeAddress, connection)); - } - - public void handleConnectionFault(NodeAddress nodeAddress) { - handleConnectionFault(nodeAddress, null); + public int getMaxConnections() { + return maxConnectionsAbsolute; } - public void handleConnectionFault(NodeAddress nodeAddress, @Nullable Connection connection) { - log.debug("handleConnectionFault called: nodeAddress=" + nodeAddress); - boolean doRemovePersistedPeer = false; - removeReportedPeer(nodeAddress); - Optional persistedPeerOptional = findPersistedPeer(nodeAddress); - if (persistedPeerOptional.isPresent()) { - Peer persistedPeer = persistedPeerOptional.get(); - persistedPeer.increaseFailedConnectionAttempts(); - doRemovePersistedPeer = persistedPeer.tooManyFailedConnectionAttempts(); - } - doRemovePersistedPeer = doRemovePersistedPeer || (connection != null && connection.getRuleViolation() != null); - - if (doRemovePersistedPeer) - removePersistedPeer(nodeAddress); - else - removeTooOldPersistedPeers(); - } - public void shutDownConnection(Connection connection, CloseConnectionReason closeConnectionReason) { - if (connection.getPeerType() != Connection.PeerType.DIRECT_MSG_PEER) - connection.shutDown(closeConnectionReason); - } + /////////////////////////////////////////////////////////////////////////////////////////// + // Listeners + /////////////////////////////////////////////////////////////////////////////////////////// - public void shutDownConnection(NodeAddress peersNodeAddress, CloseConnectionReason closeConnectionReason) { - networkNode.getAllConnections().stream() - .filter(connection -> connection.getPeersNodeAddressOptional().isPresent() && - connection.getPeersNodeAddressOptional().get().equals(peersNodeAddress) && - connection.getPeerType() != Connection.PeerType.DIRECT_MSG_PEER) - .findAny() - .ifPresent(connection -> connection.shutDown(closeConnectionReason)); + public void addListener(Listener listener) { + listeners.add(listener); } - // Delivers the live peers from the last 30 min (MAX_AGE_LIVE_PEERS) - // We include older peers to avoid risks for network partitioning - public Set getLivePeers() { - return getLivePeers(null); + public void removeListener(Listener listener) { + listeners.remove(listener); } - public Set getLivePeers(@Nullable NodeAddress excludedNodeAddress) { - int oldNumLatestLivePeers = latestLivePeers.size(); - - Set peers = new HashSet<>(latestLivePeers); - Set currentLivePeers = getConnectedReportedPeers().stream() - .filter(e -> !isSeedNode(e)) - .filter(e -> !e.getNodeAddress().equals(excludedNodeAddress)) - .collect(Collectors.toSet()); - peers.addAll(currentLivePeers); - - long maxAge = new Date().getTime() - MAX_AGE_LIVE_PEERS; - latestLivePeers.clear(); - Set recentPeers = peers.stream() - .filter(peer -> peer.getDateAsLong() > maxAge) - .collect(Collectors.toSet()); - latestLivePeers.addAll(recentPeers); - - if (oldNumLatestLivePeers != latestLivePeers.size()) - log.info("Num of latestLivePeers={}", latestLivePeers.size()); - return latestLivePeers; - } /////////////////////////////////////////////////////////////////////////////////////////// - // Private + // Private misc /////////////////////////////////////////////////////////////////////////////////////////// + // Modify this to change the relationships between connection limits. + // maxConnections default 12 + private void setConnectionLimits(int maxConnections) { + this.maxConnections = maxConnections; // app node 12; seedNode 30 + minConnections = Math.max(1, (int) Math.round(maxConnections * 0.7)); // app node 1-8; seedNode 21 + disconnectFromSeedNode = maxConnections; // app node 12; seedNode 30 + maxConnectionsPeer = Math.max(4, (int) Math.round(maxConnections * 1.3)); // app node 16; seedNode 39 + maxConnectionsNonDirect = Math.max(8, (int) Math.round(maxConnections * 1.7)); // app node 20; seedNode 51 + maxConnectionsAbsolute = Math.max(12, (int) Math.round(maxConnections * 2.5)); // app node 30; seedNode 66 + } + private Set getConnectedReportedPeers() { // networkNode.getConfirmedConnections includes: // filter(connection -> connection.getPeersNodeAddressOptional().isPresent()) From 0686079946fe35f15f1ba8d6f8892142c9069550 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 18:46:08 -0500 Subject: [PATCH 054/123] Refactor: Rename method --- .../core/dao/node/lite/network/LiteNodeNetworkService.java | 2 +- p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java | 5 ++--- .../bisq/network/p2p/peers/getdata/RequestDataManager.java | 4 ++-- .../network/p2p/peers/peerexchange/PeerExchangeManager.java | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java b/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java index 5c7a861a7c4..0b40e80b96c 100644 --- a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java +++ b/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java @@ -180,7 +180,7 @@ public void onConnection(Connection connection) { public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { closeHandler(connection); - if (peerManager.isNodeBanned(closeConnectionReason, connection)) { + if (peerManager.isPeerBanned(closeConnectionReason, connection)) { connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { seedNodeAddresses.remove(nodeAddress); removeFromRequestBlocksHandlerMap(nodeAddress); diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 22712ffb3d9..85b9783bf92 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -287,14 +287,13 @@ public boolean isSeedNode(NodeAddress nodeAddress) { return seedNodeAddresses.contains(nodeAddress); } - //TODO rename - public boolean isNodeBanned(CloseConnectionReason closeConnectionReason, Connection connection) { + public boolean isPeerBanned(CloseConnectionReason closeConnectionReason, Connection connection) { return closeConnectionReason == CloseConnectionReason.PEER_BANNED && connection.getPeersNodeAddressOptional().isPresent(); } private void maybeRemoveBannedPeer(CloseConnectionReason closeConnectionReason, Connection connection) { - if (connection.getPeersNodeAddressOptional().isPresent() && isNodeBanned(closeConnectionReason, connection)) { + if (connection.getPeersNodeAddressOptional().isPresent() && isPeerBanned(closeConnectionReason, connection)) { NodeAddress nodeAddress = connection.getPeersNodeAddressOptional().get(); seedNodeAddresses.remove(nodeAddress); removePersistedPeer(nodeAddress); diff --git a/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java b/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java index 48433f3e10d..38d2bd3ca4b 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java @@ -210,8 +210,8 @@ public void onConnection(Connection connection) { public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { closeHandler(connection); - if (peerManager.isNodeBanned(closeConnectionReason, connection) && connection.getPeersNodeAddressOptional().isPresent()) { - final NodeAddress nodeAddress = connection.getPeersNodeAddressOptional().get(); + if (peerManager.isPeerBanned(closeConnectionReason, connection) && connection.getPeersNodeAddressOptional().isPresent()) { + NodeAddress nodeAddress = connection.getPeersNodeAddressOptional().get(); seedNodeAddresses.remove(nodeAddress); handlerMap.remove(nodeAddress); } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java index 8d62ead5a59..2d85f79982f 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java @@ -147,7 +147,7 @@ public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection }, RETRY_DELAY_SEC); } - if (peerManager.isNodeBanned(closeConnectionReason, connection)) + if (peerManager.isPeerBanned(closeConnectionReason, connection)) seedNodeAddresses.remove(connection.getPeersNodeAddressOptional().get()); } From 31ce5ccf671c99adc222c564098a95ee745b53b0 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 18:47:43 -0500 Subject: [PATCH 055/123] Use connection.getPeersNodeAddressOptional().isPresent() instead of connection.hasPeersNodeAddress() which does the same internally --- p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 85b9783bf92..30c5157768c 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -271,8 +271,8 @@ public void handleConnectionFault(NodeAddress nodeAddress, @Nullable Connection } public boolean isSeedNode(Connection connection) { - //TODO - return connection.hasPeersNodeAddress() && seedNodeAddresses.contains(connection.getPeersNodeAddressOptional().get()); + return connection.getPeersNodeAddressOptional().isPresent() && + seedNodeAddresses.contains(connection.getPeersNodeAddressOptional().get()); } public boolean isSelf(NodeAddress nodeAddress) { @@ -306,6 +306,7 @@ private void maybeRemoveBannedPeer(CloseConnectionReason closeConnectionReason, // Peer /////////////////////////////////////////////////////////////////////////////////////////// + @SuppressWarnings("unused") public Optional findPeer(NodeAddress peersNodeAddress) { return getAllPeers().stream() .filter(peer -> peer.getNodeAddress().equals(peersNodeAddress)) From cfda0aff68f314b14d6d5eb9a59c7c10dfbc4d3c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 19:17:40 -0500 Subject: [PATCH 056/123] Fix tests --- monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java | 2 +- p2p/src/test/java/bisq/network/p2p/MockNode.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java b/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java index b9fbefb5551..4a231d8da61 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java @@ -133,7 +133,7 @@ protected void execute() { networkProtoResolver); DefaultSeedNodeRepository seedNodeRepository = new DefaultSeedNodeRepository(config); PeerManager peerManager = new PeerManager(networkNode, seedNodeRepository, new ClockWatcher(), - maxConnections, new PersistenceManager<>(storageDir, persistenceProtoResolver, corruptedStorageFileHandler)); + new PersistenceManager<>(storageDir, persistenceProtoResolver, corruptedStorageFileHandler), maxConnections); // init file storage peerManager.readPersisted(); diff --git a/p2p/src/test/java/bisq/network/p2p/MockNode.java b/p2p/src/test/java/bisq/network/p2p/MockNode.java index 9520cae156a..2112bcc39b9 100644 --- a/p2p/src/test/java/bisq/network/p2p/MockNode.java +++ b/p2p/src/test/java/bisq/network/p2p/MockNode.java @@ -60,7 +60,7 @@ public MockNode(int maxConnections) throws IOException { networkNode = mock(NetworkNode.class); File storageDir = Files.createTempDirectory("storage").toFile(); PersistenceManager persistenceManager = new PersistenceManager<>(storageDir, mock(PersistenceProtoResolver.class), mock(CorruptedStorageFileHandler.class)); - peerManager = new PeerManager(networkNode, mock(SeedNodeRepository.class), new ClockWatcher(), maxConnections, persistenceManager); + peerManager = new PeerManager(networkNode, mock(SeedNodeRepository.class), new ClockWatcher(), persistenceManager, maxConnections); connections = new HashSet<>(); when(networkNode.getAllConnections()).thenReturn(connections); } From 351db88992e65eaccb5a545b35c7f1b274e49156 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 21:02:44 -0500 Subject: [PATCH 057/123] Use a hashset instead of list to avoid duplicates. Filter out my own node from persisted peers. Remove log in DisputeAgentManager which gets called repeatedly --- .../dispute/agent/DisputeAgentManager.java | 2 -- .../bisq/network/p2p/peers/PeerManager.java | 8 +++-- .../p2p/peers/peerexchange/PeerList.java | 31 ++++++++++++------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/bisq/core/support/dispute/agent/DisputeAgentManager.java b/core/src/main/java/bisq/core/support/dispute/agent/DisputeAgentManager.java index ac6480bb1b2..73a73110d4c 100644 --- a/core/src/main/java/bisq/core/support/dispute/agent/DisputeAgentManager.java +++ b/core/src/main/java/bisq/core/support/dispute/agent/DisputeAgentManager.java @@ -214,8 +214,6 @@ public void updateMap() { observableMap.putAll(filtered); observableMap.values().forEach(this::addAcceptedDisputeAgentToUser); - - log.info("Available disputeAgents: {}", observableMap.keySet()); } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 30c5157768c..3df99fac298 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -186,7 +186,7 @@ public void shutDown() { public void readPersisted() { PeerList persisted = persistenceManager.getPersisted(); if (persisted != null) { - peerList.setAll(persisted.getList()); + peerList.setAll(persisted.getSet()); } } @@ -321,7 +321,7 @@ public Set getAllPeers() { } public Collection getPersistedPeers() { - return peerList.getList(); + return peerList.getSet(); } public void addToReportedPeers(Set reportedPeersToAdd, @@ -329,6 +329,10 @@ public void addToReportedPeers(Set reportedPeersToAdd, Capabilities capabilities) { applyCapabilities(connection, capabilities); + reportedPeersToAdd = reportedPeersToAdd.stream() + .filter(peer -> !isSelf(peer.getNodeAddress())) + .collect(Collectors.toSet()); + printNewReportedPeers(reportedPeersToAdd); // We check if the reported msg is not violating our rules diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerList.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerList.java index d32a4952c03..37112ec96ea 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerList.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerList.java @@ -21,46 +21,55 @@ import com.google.protobuf.Message; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Collectors; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +@Slf4j @EqualsAndHashCode public class PeerList implements PersistableEnvelope { @Getter - private final List list = new ArrayList<>(); + private final Set set = new HashSet<>(); public PeerList() { } - public PeerList(List list) { - setAll(list); + public PeerList(Set set) { + setAll(set); } public int size() { - return list.size(); + return set.size(); } @Override public Message toProtoMessage() { return protobuf.PersistableEnvelope.newBuilder() .setPeerList(protobuf.PeerList.newBuilder() - .addAllPeer(list.stream().map(Peer::toProtoMessage).collect(Collectors.toList()))) + .addAllPeer(set.stream().map(Peer::toProtoMessage).collect(Collectors.toList()))) .build(); } public static PeerList fromProto(protobuf.PeerList proto) { - return new PeerList(new ArrayList<>(proto.getPeerList().stream() + return new PeerList(proto.getPeerList().stream() .map(Peer::fromProto) - .collect(Collectors.toList()))); + .collect(Collectors.toSet())); } public void setAll(Collection collection) { - this.list.clear(); - this.list.addAll(collection); + this.set.clear(); + this.set.addAll(collection); + } + + @Override + public String toString() { + return "PeerList{" + + "\n set=" + set + + "\n}"; } } From 2fd010498d379eee40796196e06811a8c6844148 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 21:18:28 -0500 Subject: [PATCH 058/123] Decrease failedConnectionAttempts onConnection Rename method --- .../main/java/bisq/network/p2p/peers/PeerManager.java | 11 ++++++++--- .../bisq/network/p2p/peers/peerexchange/Peer.java | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 3df99fac298..9b82beaf29f 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -208,6 +208,11 @@ public void onConnection(Connection connection) { stopped = false; listeners.forEach(Listener::onNewConnectionAfterAllConnectionsLost); } + + if (connection.getPeersNodeAddressOptional().isPresent()) { + findPeer(connection.getPeersNodeAddressOptional().get()) + .ifPresent(Peer::onConnection); + } } @Override @@ -253,16 +258,16 @@ public void handleConnectionFault(NodeAddress nodeAddress) { } public void handleConnectionFault(NodeAddress nodeAddress, @Nullable Connection connection) { - log.debug("handleConnectionFault called: nodeAddress=" + nodeAddress); boolean doRemovePersistedPeer = false; removeReportedPeer(nodeAddress); Optional persistedPeerOptional = findPersistedPeer(nodeAddress); if (persistedPeerOptional.isPresent()) { Peer persistedPeer = persistedPeerOptional.get(); - persistedPeer.increaseFailedConnectionAttempts(); + persistedPeer.onDisconnect(); doRemovePersistedPeer = persistedPeer.tooManyFailedConnectionAttempts(); } - doRemovePersistedPeer = doRemovePersistedPeer || (connection != null && connection.getRuleViolation() != null); + boolean ruleViolation = connection != null && connection.getRuleViolation() != null; + doRemovePersistedPeer = doRemovePersistedPeer || ruleViolation; if (doRemovePersistedPeer) removePersistedPeer(nodeAddress); diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java index a7681f909ce..99678ad6053 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/Peer.java @@ -80,10 +80,14 @@ public static Peer fromProto(protobuf.Peer proto) { // API /////////////////////////////////////////////////////////////////////////////////////////// - public void increaseFailedConnectionAttempts() { + public void onDisconnect() { this.failedConnectionAttempts++; } + public void onConnection() { + this.failedConnectionAttempts--; + } + public boolean tooManyFailedConnectionAttempts() { return failedConnectionAttempts >= MAX_FAILED_CONNECTION_ATTEMPTS; } From f36a17389e735ec4b80e75f5287d7e5d38c6c112 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 21:30:07 -0500 Subject: [PATCH 059/123] Fix incorrect collection used in == 1 check --- p2p/src/main/java/bisq/network/p2p/P2PService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 0e60769ea66..1940f8b4479 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -452,7 +452,7 @@ public void onAdded(Collection protectedStorageEntries) { private void processSingleMailboxEntry(Collection protectedMailboxStorageEntries) { checkArgument(protectedMailboxStorageEntries.size() == 1); var decryptedEntries = new ArrayList<>(getDecryptedEntries(protectedMailboxStorageEntries)); - if (protectedMailboxStorageEntries.size() == 1) { + if (decryptedEntries.size() == 1) { storeMailboxDataAndNotifyListeners(decryptedEntries.get(0)); } } From b748bffbfe31aa81a524affe997124aad9044dbc Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 21:30:23 -0500 Subject: [PATCH 060/123] Add isPresent check --- .../network/p2p/peers/peerexchange/PeerExchangeManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java index 2d85f79982f..9cd7d8686b7 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java @@ -147,7 +147,8 @@ public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection }, RETRY_DELAY_SEC); } - if (peerManager.isPeerBanned(closeConnectionReason, connection)) + if (peerManager.isPeerBanned(closeConnectionReason, connection) && + connection.getPeersNodeAddressOptional().isPresent()) seedNodeAddresses.remove(connection.getPeersNodeAddressOptional().get()); } From c7f23e8deb3266502607b682a75afbccba727a1a Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 22:29:22 -0500 Subject: [PATCH 061/123] Do not log size as we don't want to call potentially expensive toProtoMessage method --- .../bisq/network/p2p/storage/persistence/StoreService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java b/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java index adc1688593e..5d1b57d9120 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java @@ -117,12 +117,11 @@ protected T getStore(String fileName) { T persisted = persistenceManager.getPersisted(fileName); if (persisted != null) { store = persisted; - - int length = store.toProtoMessage().toByteArray().length; + /* int length = store.toProtoMessage().getSerializedSize(); double size = length > 1_000_000D ? length / 1_000_000D : length / 1_000D; String unit = length > 1_000_000D ? "MB" : "KB"; log.info("{}: size of {}: {} {}", this.getClass().getSimpleName(), - persisted.getClass().getSimpleName(), size, unit); + persisted.getClass().getSimpleName(), size, unit);*/ } else { store = createStore(); } From f53290b8174614efbd031a7e6db69f4e18077bcf Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 22:31:03 -0500 Subject: [PATCH 062/123] Copy peers in a new hashset to avoid concurrent modification exc at serialisation Remove final Cleanups --- .../p2p/network/SynchronizedProtoOutputStream.java | 6 +++--- .../main/java/bisq/network/p2p/peers/PeerManager.java | 7 ++++--- .../p2p/peers/peerexchange/GetPeersRequestHandler.java | 5 +++-- .../p2p/peers/peerexchange/PeerExchangeHandler.java | 5 ++++- .../peers/peerexchange/messages/GetPeersRequest.java | 10 ++++++++-- .../peers/peerexchange/messages/GetPeersResponse.java | 8 ++++++-- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/SynchronizedProtoOutputStream.java b/p2p/src/main/java/bisq/network/p2p/network/SynchronizedProtoOutputStream.java index 4614e20f79b..96d944c75fa 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/SynchronizedProtoOutputStream.java +++ b/p2p/src/main/java/bisq/network/p2p/network/SynchronizedProtoOutputStream.java @@ -50,11 +50,11 @@ void writeEnvelope(NetworkEnvelope envelope) { } catch (InterruptedException e) { Thread currentThread = Thread.currentThread(); currentThread.interrupt(); - final String msg = "Thread " + currentThread + " was interrupted. InterruptedException=" + e; + String msg = "Thread " + currentThread + " was interrupted. InterruptedException=" + e; log.error(msg); throw new BisqRuntimeException(msg, e); } catch (ExecutionException e) { - final String msg = "Failed to write envelope. ExecutionException " + e; + String msg = "Failed to write envelope. ExecutionException " + e; log.error(msg); throw new BisqRuntimeException(msg, e); } @@ -65,7 +65,7 @@ void onConnectionShutdown() { executorService.shutdownNow(); super.onConnectionShutdown(); } catch (Throwable t) { - log.error("Failed to handle connection shutdown. Throwable={}", t); + log.error("Failed to handle connection shutdown. Throwable={}", t.toString()); } } } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 9b82beaf29f..737f7d0d80d 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -401,7 +401,7 @@ public Optional findPeersCapabilities(NodeAddress nodeAddress) { // We look up first our connections as that is our own data. If not found there we look up the peers which // include reported peers. Optional optionalCapabilities = networkNode.findPeersCapabilities(nodeAddress); - if (optionalCapabilities.isPresent()) { + if (optionalCapabilities.isPresent() && !optionalCapabilities.get().isEmpty()) { return optionalCapabilities; } @@ -415,7 +415,8 @@ public Optional findPeersCapabilities(NodeAddress nodeAddress) { // attack all users have a strong incentive to update ;-). return getAllPeers().stream() .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) - .findAny().map(Peer::getCapabilities); + .findAny() + .map(Peer::getCapabilities); } private void applyCapabilities(Connection connection, Capabilities capabilities) { @@ -647,7 +648,7 @@ private void printNewReportedPeers(Set reportedPeers) { private boolean removePersistedPeer(Peer persistedPeer) { if (getPersistedPeers().contains(persistedPeer)) { - getPersistedPeers().remove(persistedPeer); + //getPersistedPeers().remove(persistedPeer); requestPersistence(); return true; } else { diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java index f280587c86b..2b11b0c64a9 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/GetPeersRequestHandler.java @@ -32,6 +32,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; +import java.util.HashSet; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @@ -83,11 +84,11 @@ public GetPeersRequestHandler(NetworkNode networkNode, PeerManager peerManager, // API /////////////////////////////////////////////////////////////////////////////////////////// - public void handle(GetPeersRequest getPeersRequest, final Connection connection) { + public void handle(GetPeersRequest getPeersRequest, Connection connection) { checkArgument(connection.getPeersNodeAddressOptional().isPresent(), "The peers address must have been already set at the moment"); GetPeersResponse getPeersResponse = new GetPeersResponse(getPeersRequest.getNonce(), - peerManager.getLivePeers(connection.getPeersNodeAddressOptional().get())); + new HashSet<>(peerManager.getLivePeers(connection.getPeersNodeAddressOptional().get()))); checkArgument(timeoutTimer == null, "onGetPeersRequest must not be called twice."); timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java index df6b685dec7..384f05c34c5 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeHandler.java @@ -35,6 +35,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; +import java.util.HashSet; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -104,7 +105,9 @@ private void sendGetPeersRequest(NodeAddress nodeAddress) { log.debug("sendGetPeersRequest to nodeAddress={}", nodeAddress); if (!stopped) { if (networkNode.getNodeAddress() != null) { - GetPeersRequest getPeersRequest = new GetPeersRequest(networkNode.getNodeAddress(), nonce, peerManager.getLivePeers(nodeAddress)); + GetPeersRequest getPeersRequest = new GetPeersRequest(networkNode.getNodeAddress(), + nonce, + new HashSet<>(peerManager.getLivePeers(nodeAddress))); if (timeoutTimer == null) { timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions if (!stopped) { diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersRequest.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersRequest.java index 2f23ef4308b..90f31b97dae 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersRequest.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersRequest.java @@ -47,8 +47,14 @@ public final class GetPeersRequest extends NetworkEnvelope implements PeerExchan @Nullable private final Capabilities supportedCapabilities; - public GetPeersRequest(NodeAddress senderNodeAddress, int nonce, Set reportedPeers) { - this(senderNodeAddress, nonce, reportedPeers, Capabilities.app, Version.getP2PMessageVersion()); + public GetPeersRequest(NodeAddress senderNodeAddress, + int nonce, + Set reportedPeers) { + this(senderNodeAddress, + nonce, + reportedPeers, + Capabilities.app, + Version.getP2PMessageVersion()); } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersResponse.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersResponse.java index 5a41fe91f40..149bcb4946e 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersResponse.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/messages/GetPeersResponse.java @@ -43,8 +43,12 @@ public final class GetPeersResponse extends NetworkEnvelope implements PeerExcha @Nullable private final Capabilities supportedCapabilities; - public GetPeersResponse(int requestNonce, Set reportedPeers) { - this(requestNonce, reportedPeers, Capabilities.app, Version.getP2PMessageVersion()); + public GetPeersResponse(int requestNonce, + Set reportedPeers) { + this(requestNonce, + reportedPeers, + Capabilities.app, + Version.getP2PMessageVersion()); } From 447235c2afe2ed1d50bd68ca941e6c41df548ffc Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 22:48:10 -0500 Subject: [PATCH 063/123] Dont reassign param --- .../main/java/bisq/network/p2p/peers/PeerManager.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 737f7d0d80d..8f81fef5e0b 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -334,18 +334,18 @@ public void addToReportedPeers(Set reportedPeersToAdd, Capabilities capabilities) { applyCapabilities(connection, capabilities); - reportedPeersToAdd = reportedPeersToAdd.stream() + Set peers = reportedPeersToAdd.stream() .filter(peer -> !isSelf(peer.getNodeAddress())) .collect(Collectors.toSet()); - printNewReportedPeers(reportedPeersToAdd); + printNewReportedPeers(peers); // We check if the reported msg is not violating our rules - if (reportedPeersToAdd.size() <= (MAX_REPORTED_PEERS + maxConnectionsAbsolute + 10)) { - reportedPeers.addAll(reportedPeersToAdd); + if (peers.size() <= (MAX_REPORTED_PEERS + maxConnectionsAbsolute + 10)) { + reportedPeers.addAll(peers); purgeReportedPeersIfExceeds(); - getPersistedPeers().addAll(reportedPeersToAdd); + getPersistedPeers().addAll(peers); purgePersistedPeersIfExceeds(); requestPersistence(); From c88bc1c99c918997b7b2f1ad18330c171a5b3f1c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 7 Oct 2020 23:24:33 -0500 Subject: [PATCH 064/123] Use custom class MailboxItem instead of Tuple Rename mailboxMap to mailboxItemsByUid --- .../core/trade/protocol/TradeProtocol.java | 5 +- .../java/bisq/network/p2p/P2PService.java | 48 ++++++++++++------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java index f27fa0804af..af259e25c74 100644 --- a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java @@ -30,6 +30,7 @@ import bisq.network.p2p.DecryptedMessageWithPubKey; import bisq.network.p2p.MailboxMessage; import bisq.network.p2p.NodeAddress; +import bisq.network.p2p.P2PService; import bisq.network.p2p.SendMailboxMessageListener; import bisq.network.p2p.messaging.DecryptedMailboxListener; @@ -77,8 +78,8 @@ protected void onInitialized() { processModel.getP2PService().addDecryptedDirectMessageListener(this); } processModel.getP2PService().addDecryptedMailboxListener(this); - processModel.getP2PService().getMailboxMap().values() - .stream().map(e -> e.second) + processModel.getP2PService().getMailboxItemsByUid().values() + .stream().map(P2PService.MailboxItem::getDecryptedMessageWithPubKey) .forEach(this::handleDecryptedMessageWithPubKey); } diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 1940f8b4479..95b68f0c611 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -53,7 +53,6 @@ import bisq.common.proto.ProtobufferException; import bisq.common.proto.network.NetworkEnvelope; import bisq.common.proto.persistable.PersistedDataHost; -import bisq.common.util.Tuple2; import bisq.common.util.Utilities; import com.google.inject.Inject; @@ -97,6 +96,7 @@ import org.slf4j.LoggerFactory; import lombok.Getter; +import lombok.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -126,7 +126,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis private final Set decryptedMailboxListeners = new CopyOnWriteArraySet<>(); private final Set p2pServiceListeners = new CopyOnWriteArraySet<>(); @Getter - private final Map> mailboxMap = new HashMap<>(); + private final Map mailboxItemsByUid = new HashMap<>(); private final Set shutDownResultHandlers = new CopyOnWriteArraySet<>(); private final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty(); private final BooleanProperty preliminaryDataReceived = new SimpleBooleanProperty(); @@ -462,7 +462,7 @@ private void processSingleMailboxEntry(Collection private void threadedBatchProcessMailboxEntries(Collection protectedMailboxStorageEntries) { ListeningExecutorService executor = Utilities.getSingleThreadListeningExecutor("processMailboxEntry-" + new Random().nextInt(1000)); long ts = System.currentTimeMillis(); - ListenableFuture>> future = executor.submit(() -> { + ListenableFuture> future = executor.submit(() -> { var decryptedEntries = getDecryptedEntries(protectedMailboxStorageEntries); log.info("Batch processing of {} mailbox entries took {} ms", protectedMailboxStorageEntries.size(), @@ -471,8 +471,8 @@ private void threadedBatchProcessMailboxEntries(Collection() { - public void onSuccess(Set> decryptedEntries) { - UserThread.execute(() -> decryptedEntries.forEach(e -> storeMailboxDataAndNotifyListeners(e))); + public void onSuccess(Set decryptedMailboxMessageWithEntries) { + UserThread.execute(() -> decryptedMailboxMessageWithEntries.forEach(e -> storeMailboxDataAndNotifyListeners(e))); } public void onFailure(@NotNull Throwable throwable) { @@ -481,25 +481,24 @@ public void onFailure(@NotNull Throwable throwable) { }, MoreExecutors.directExecutor()); } - private Set> getDecryptedEntries(Collection protectedMailboxStorageEntries) { - Set> decryptedEntries = new HashSet<>(); + private Set getDecryptedEntries(Collection protectedMailboxStorageEntries) { + Set decryptedMailboxMessageWithEntries = new HashSet<>(); protectedMailboxStorageEntries.stream() .map(this::decryptProtectedMailboxStorageEntry) .filter(Objects::nonNull) - .forEach(decryptedEntries::add); - return decryptedEntries; + .forEach(decryptedMailboxMessageWithEntries::add); + return decryptedMailboxMessageWithEntries; } @Nullable - private Tuple2 decryptProtectedMailboxStorageEntry( - ProtectedMailboxStorageEntry protectedMailboxStorageEntry) { + private MailboxItem decryptProtectedMailboxStorageEntry(ProtectedMailboxStorageEntry protectedMailboxStorageEntry) { try { DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify(protectedMailboxStorageEntry .getMailboxStoragePayload() .getPrefixedSealedAndSignedMessage() .getSealedAndSigned()); checkArgument(decryptedMessageWithPubKey.getNetworkEnvelope() instanceof MailboxMessage); - return new Tuple2<>(protectedMailboxStorageEntry, decryptedMessageWithPubKey); + return new MailboxItem(protectedMailboxStorageEntry, decryptedMessageWithPubKey); } catch (CryptoException ignore) { // Expected if message was not intended for us } catch (ProtobufferException e) { @@ -509,11 +508,11 @@ private Tuple2 decrypt return null; } - private void storeMailboxDataAndNotifyListeners(Tuple2 tuple2) { - DecryptedMessageWithPubKey decryptedMessageWithPubKey = tuple2.second; + private void storeMailboxDataAndNotifyListeners(MailboxItem mailboxItem) { + DecryptedMessageWithPubKey decryptedMessageWithPubKey = mailboxItem.getDecryptedMessageWithPubKey(); MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope(); NodeAddress sender = mailboxMessage.getSenderNodeAddress(); - mailboxMap.put(mailboxMessage.getUid(), tuple2); + mailboxItemsByUid.put(mailboxMessage.getUid(), mailboxItem); log.info("Received a {} mailbox message with uid {} and senderAddress {}", mailboxMessage.getClass().getSimpleName(), mailboxMessage.getUid(), sender); decryptedMailboxListeners.forEach(e -> e.onMailboxMessageAdded(decryptedMessageWithPubKey, sender)); @@ -771,8 +770,8 @@ private void delayedRemoveEntryFromMailbox(DecryptedMessageWithPubKey decryptedM MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope(); String uid = mailboxMessage.getUid(); - if (mailboxMap.containsKey(uid)) { - ProtectedMailboxStorageEntry mailboxData = mailboxMap.get(uid).first; + if (mailboxItemsByUid.containsKey(uid)) { + ProtectedMailboxStorageEntry mailboxData = mailboxItemsByUid.get(uid).getProtectedMailboxStorageEntry(); if (mailboxData != null && mailboxData.getProtectedStoragePayload() instanceof MailboxStoragePayload) { MailboxStoragePayload expirableMailboxStoragePayload = (MailboxStoragePayload) mailboxData.getProtectedStoragePayload(); PublicKey receiversPubKey = mailboxData.getReceiversPubKey(); @@ -788,7 +787,7 @@ private void delayedRemoveEntryFromMailbox(DecryptedMessageWithPubKey decryptedM log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); } - mailboxMap.remove(uid); + mailboxItemsByUid.remove(uid); log.info("Removed successfully decryptedMsgWithPubKey. uid={}", uid); } } else { @@ -920,4 +919,17 @@ public PeerManager getPeerManager() { public KeyRing getKeyRing() { return keyRing; } + + + @Value + public class MailboxItem { + private final ProtectedMailboxStorageEntry protectedMailboxStorageEntry; + private final DecryptedMessageWithPubKey decryptedMessageWithPubKey; + + public MailboxItem(ProtectedMailboxStorageEntry protectedMailboxStorageEntry, + DecryptedMessageWithPubKey decryptedMessageWithPubKey) { + this.protectedMailboxStorageEntry = protectedMailboxStorageEntry; + this.decryptedMessageWithPubKey = decryptedMessageWithPubKey; + } + } } From c8feef150e002803799b95b9e121f7854ad699de Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 11:16:54 -0500 Subject: [PATCH 065/123] Apply code review suggestions Fix incorrectly commented out code (was for dev testing commented out) --- .../bisq/network/p2p/peers/PeerManager.java | 20 ++++++++----------- .../peerexchange/PeerExchangeManager.java | 6 +++--- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 8f81fef5e0b..07b16336e41 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -208,11 +208,9 @@ public void onConnection(Connection connection) { stopped = false; listeners.forEach(Listener::onNewConnectionAfterAllConnectionsLost); } - - if (connection.getPeersNodeAddressOptional().isPresent()) { - findPeer(connection.getPeersNodeAddressOptional().get()) - .ifPresent(Peer::onConnection); - } + connection.getPeersNodeAddressOptional() + .flatMap(this::findPeer) + .ifPresent(Peer::onConnection); } @Override @@ -581,12 +579,10 @@ private void removeReportedPeer(Peer reportedPeer) { private void removeReportedPeer(NodeAddress nodeAddress) { List reportedPeersClone = new ArrayList<>(reportedPeers); - Optional reportedPeerOptional = reportedPeersClone.stream() - .filter(e -> e.getNodeAddress().equals(nodeAddress)).findAny(); - if (reportedPeerOptional.isPresent()) { - Peer reportedPeer = reportedPeerOptional.get(); - removeReportedPeer(reportedPeer); - } + reportedPeersClone.stream() + .filter(e -> e.getNodeAddress().equals(nodeAddress)) + .findAny() + .ifPresent(this::removeReportedPeer); } private void removeTooOldReportedPeers() { @@ -648,7 +644,7 @@ private void printNewReportedPeers(Set reportedPeers) { private boolean removePersistedPeer(Peer persistedPeer) { if (getPersistedPeers().contains(persistedPeer)) { - //getPersistedPeers().remove(persistedPeer); + getPersistedPeers().remove(persistedPeer); requestPersistence(); return true; } else { diff --git a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java index 9cd7d8686b7..a843abf92d8 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/peerexchange/PeerExchangeManager.java @@ -147,9 +147,9 @@ public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection }, RETRY_DELAY_SEC); } - if (peerManager.isPeerBanned(closeConnectionReason, connection) && - connection.getPeersNodeAddressOptional().isPresent()) - seedNodeAddresses.remove(connection.getPeersNodeAddressOptional().get()); + if (peerManager.isPeerBanned(closeConnectionReason, connection)) { + connection.getPeersNodeAddressOptional().ifPresent(seedNodeAddresses::remove); + } } @Override From 4f685f8f4becfc77e2e3be61645c2cff857668ee Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 13:24:51 -0500 Subject: [PATCH 066/123] When updating the capability from a reported peer we check if the reported one has higher capabilities, otherwise we ignore it. --- .../java/bisq/common/app/Capabilities.java | 14 +++++++ .../bisq/common/app/CapabilitiesTest.java | 42 +++++++++++++++++++ .../bisq/network/p2p/peers/PeerManager.java | 7 ++-- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/bisq/common/app/Capabilities.java b/common/src/main/java/bisq/common/app/Capabilities.java index bf95c15c48d..d0b3e50a3f8 100644 --- a/common/src/main/java/bisq/common/app/Capabilities.java +++ b/common/src/main/java/bisq/common/app/Capabilities.java @@ -184,4 +184,18 @@ public String prettyPrint() { public int size() { return capabilities.size(); } + + // We return true if our capabilities have less capabilities than the parameter value + public boolean hasLess(Capabilities other) { + return findHighestCapability(this) < findHighestCapability(other); + } + + // We use the sum of all capabilities. Alternatively we could use the highest entry. + // Neither would support removal of past capabilities, a use case we never had so far and which might have + // backward compatibility issues, so we should treat capabilities as an append-only data structure. + public int findHighestCapability(Capabilities capabilities) { + return (int) capabilities.capabilities.stream() + .mapToLong(e -> (long) e.ordinal()) + .sum(); + } } diff --git a/common/src/test/java/bisq/common/app/CapabilitiesTest.java b/common/src/test/java/bisq/common/app/CapabilitiesTest.java index 735fc899eaf..2595ea8b180 100644 --- a/common/src/test/java/bisq/common/app/CapabilitiesTest.java +++ b/common/src/test/java/bisq/common/app/CapabilitiesTest.java @@ -23,6 +23,7 @@ import org.junit.Test; +import static bisq.common.app.Capability.DAO_FULL_NODE; import static bisq.common.app.Capability.SEED_NODE; import static bisq.common.app.Capability.TRADE_STATISTICS; import static bisq.common.app.Capability.TRADE_STATISTICS_2; @@ -40,6 +41,47 @@ public void testNoCapabilitiesAvailable() { assertFalse(DUT.containsAll(new Capabilities(SEED_NODE))); } + @Test + public void testHasLess() { + assertTrue(new Capabilities().hasLess(new Capabilities(SEED_NODE))); + assertFalse(new Capabilities().hasLess(new Capabilities())); + assertFalse(new Capabilities(SEED_NODE).hasLess(new Capabilities())); + assertTrue(new Capabilities(SEED_NODE).hasLess(new Capabilities(DAO_FULL_NODE))); + assertFalse(new Capabilities(DAO_FULL_NODE).hasLess(new Capabilities(SEED_NODE))); + + Capabilities all = new Capabilities( + Capability.TRADE_STATISTICS, + Capability.TRADE_STATISTICS_2, + Capability.ACCOUNT_AGE_WITNESS, + Capability.ACK_MSG, + Capability.PROPOSAL, + Capability.BLIND_VOTE, + Capability.DAO_STATE, + Capability.BUNDLE_OF_ENVELOPES, + Capability.MEDIATION, + Capability.SIGNED_ACCOUNT_AGE_WITNESS, + Capability.REFUND_AGENT, + Capability.TRADE_STATISTICS_HASH_UPDATE + ); + Capabilities other = new Capabilities( + Capability.TRADE_STATISTICS, + Capability.TRADE_STATISTICS_2, + Capability.ACCOUNT_AGE_WITNESS, + Capability.ACK_MSG, + Capability.PROPOSAL, + Capability.BLIND_VOTE, + Capability.DAO_STATE, + Capability.BUNDLE_OF_ENVELOPES, + Capability.MEDIATION, + Capability.SIGNED_ACCOUNT_AGE_WITNESS, + Capability.REFUND_AGENT, + Capability.TRADE_STATISTICS_HASH_UPDATE, + Capability.NO_ADDRESS_PRE_FIX + ); + + assertTrue(all.hasLess(other)); + } + @Test public void testO() { Capabilities DUT = new Capabilities(TRADE_STATISTICS); diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 07b16336e41..20af0453194 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -417,15 +417,16 @@ public Optional findPeersCapabilities(NodeAddress nodeAddress) { .map(Peer::getCapabilities); } - private void applyCapabilities(Connection connection, Capabilities capabilities) { - if (capabilities == null || capabilities.isEmpty()) { + private void applyCapabilities(Connection connection, Capabilities newCapabilities) { + if (newCapabilities == null || newCapabilities.isEmpty()) { return; } connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { getAllPeers().stream() .filter(peer -> peer.getNodeAddress().equals(nodeAddress)) - .forEach(peer -> peer.setCapabilities(capabilities)); + .filter(peer -> peer.getCapabilities().hasLess(newCapabilities)) + .forEach(peer -> peer.setCapabilities(newCapabilities)); }); requestPersistence(); } From 6e3fdbc96ab5c2ab2c58a452b5e42031dca262d6 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 13:36:32 -0500 Subject: [PATCH 067/123] Apply codacy suggestions --- .../src/test/java/bisq/common/app/CapabilitiesTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/test/java/bisq/common/app/CapabilitiesTest.java b/common/src/test/java/bisq/common/app/CapabilitiesTest.java index 2595ea8b180..f939ff9cd83 100644 --- a/common/src/test/java/bisq/common/app/CapabilitiesTest.java +++ b/common/src/test/java/bisq/common/app/CapabilitiesTest.java @@ -50,8 +50,8 @@ public void testHasLess() { assertFalse(new Capabilities(DAO_FULL_NODE).hasLess(new Capabilities(SEED_NODE))); Capabilities all = new Capabilities( - Capability.TRADE_STATISTICS, - Capability.TRADE_STATISTICS_2, + TRADE_STATISTICS, + TRADE_STATISTICS_2, Capability.ACCOUNT_AGE_WITNESS, Capability.ACK_MSG, Capability.PROPOSAL, @@ -64,8 +64,8 @@ public void testHasLess() { Capability.TRADE_STATISTICS_HASH_UPDATE ); Capabilities other = new Capabilities( - Capability.TRADE_STATISTICS, - Capability.TRADE_STATISTICS_2, + TRADE_STATISTICS, + TRADE_STATISTICS_2, Capability.ACCOUNT_AGE_WITNESS, Capability.ACK_MSG, Capability.PROPOSAL, From 00bed02839a0be741dad3eef578c7ad530cda093 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 15:21:53 -0500 Subject: [PATCH 068/123] Add TradeStatistics3 and related classes Add TRADE_STATISTICS_3 Capability Add TradeStatistics3 to proto resolvers Make message TradeStatistics2 deprecated --- .../main/java/bisq/common/app/Capability.java | 3 +- .../bisq/core/proto/CoreProtoResolver.java | 3 + .../CorePersistenceProtoResolver.java | 3 + .../core/setup/CoreNetworkCapabilities.java | 3 +- .../trade/statistics/TradeStatistics3.java | 320 ++++++++++++++++++ .../TradeStatistics3StorageService.java | 84 +++++ .../statistics/TradeStatistics3Store.java | 73 ++++ .../statistics/TradeStatisticsConverter.java | 147 ++++++++ .../src/main/java/bisq/monitor/Monitor.java | 3 +- proto/src/main/proto/pb.proto | 57 ++-- 10 files changed, 674 insertions(+), 22 deletions(-) create mode 100644 core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java create mode 100644 core/src/main/java/bisq/core/trade/statistics/TradeStatistics3StorageService.java create mode 100644 core/src/main/java/bisq/core/trade/statistics/TradeStatistics3Store.java create mode 100644 core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java diff --git a/common/src/main/java/bisq/common/app/Capability.java b/common/src/main/java/bisq/common/app/Capability.java index 8ffb042844d..1c9aebd8898 100644 --- a/common/src/main/java/bisq/common/app/Capability.java +++ b/common/src/main/java/bisq/common/app/Capability.java @@ -41,5 +41,6 @@ public enum Capability { MEDIATION, // Supports mediation feature REFUND_AGENT, // Supports refund agents TRADE_STATISTICS_HASH_UPDATE, // We changed the hash method in 1.2.0 and that requires update to 1.2.2 for handling it correctly, otherwise the seed nodes have to process too much data. - NO_ADDRESS_PRE_FIX // At 1.4.0 we removed the prefix filter for mailbox messages. If a peer has that capability we do not sent the prefix. + NO_ADDRESS_PRE_FIX, // At 1.4.0 we removed the prefix filter for mailbox messages. If a peer has that capability we do not sent the prefix. + TRADE_STATISTICS_3 // We used a new reduced trade statistics model from v1.4.0 on } diff --git a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java index 999f4e8e5f1..921a1fa5a4a 100644 --- a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java @@ -54,6 +54,7 @@ import bisq.core.payment.payload.WeChatPayAccountPayload; import bisq.core.payment.payload.WesternUnionAccountPayload; import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.common.proto.ProtoResolver; import bisq.common.proto.ProtobufferRuntimeException; @@ -178,6 +179,8 @@ public PersistablePayload fromProto(protobuf.PersistableNetworkPayload proto) { return BlindVotePayload.fromProto(proto.getBlindVotePayload()); case SIGNED_WITNESS: return SignedWitness.fromProto(proto.getSignedWitness()); + case TRADE_STATISTICS3: + return TradeStatistics3.fromProto(proto.getTradeStatistics3()); default: throw new ProtobufferRuntimeException("Unknown proto message case (PB.PersistableNetworkPayload). messageCase=" + proto.getMessageCase()); } diff --git a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java index efefffe881a..26addc24b0e 100644 --- a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java @@ -39,6 +39,7 @@ import bisq.core.support.dispute.refund.RefundDisputeList; import bisq.core.trade.TradableList; import bisq.core.trade.statistics.TradeStatistics2Store; +import bisq.core.trade.statistics.TradeStatistics3Store; import bisq.core.user.PreferencesPayload; import bisq.core.user.UserPayload; @@ -126,6 +127,8 @@ public PersistableEnvelope fromProto(protobuf.PersistableEnvelope proto) { return UnconfirmedBsqChangeOutputList.fromProto(proto.getUnconfirmedBsqChangeOutputList()); case SIGNED_WITNESS_STORE: return SignedWitnessStore.fromProto(proto.getSignedWitnessStore()); + case TRADE_STATISTICS3_STORE: + return TradeStatistics3Store.fromProto(proto.getTradeStatistics3Store()); default: throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " + diff --git a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java index fd951d98592..c5c70e8877d 100644 --- a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java +++ b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java @@ -40,7 +40,8 @@ static void setSupportedCapabilities(Config config) { Capability.SIGNED_ACCOUNT_AGE_WITNESS, Capability.REFUND_AGENT, Capability.TRADE_STATISTICS_HASH_UPDATE, - Capability.NO_ADDRESS_PRE_FIX + Capability.NO_ADDRESS_PRE_FIX, + Capability.TRADE_STATISTICS_3 ); if (config.daoActivated) { diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java new file mode 100644 index 00000000000..94d267a7a77 --- /dev/null +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java @@ -0,0 +1,320 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.trade.statistics; + +import bisq.core.monetary.Altcoin; +import bisq.core.monetary.AltcoinExchangeRate; +import bisq.core.monetary.Price; +import bisq.core.monetary.Volume; +import bisq.core.offer.OfferUtil; + +import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; +import bisq.network.p2p.storage.payload.PersistableNetworkPayload; +import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload; + +import bisq.common.app.Capabilities; +import bisq.common.app.Capability; +import bisq.common.crypto.Hash; +import bisq.common.proto.ProtoUtil; +import bisq.common.util.CollectionUtils; +import bisq.common.util.ExtraDataMapValidator; +import bisq.common.util.JsonExclude; +import bisq.common.util.Utilities; + +import com.google.protobuf.ByteString; + +import org.bitcoinj.core.Coin; +import org.bitcoinj.utils.ExchangeRate; +import org.bitcoinj.utils.Fiat; + +import com.google.common.base.Charsets; + +import java.util.Date; +import java.util.Map; +import java.util.Optional; + +import lombok.Value; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * This new trade statistics class uses only the bare minimum of data. + * Data size is about 50 bytes in average + */ +@Slf4j +@Value +public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, + CapabilityRequiringPayload { + + // This enum must not change the order as we use the ordinal for storage to reduce data size. + // The payment method string can be quite long and would consume 15% more space. + // When we get a new payment method we can add it to the enum at the end. Old users would add it as string if not + // recognized. + private enum PaymentMethodMapper { + OK_PAY, + CASH_APP, + VENMO, + AUSTRALIA_PAYID, // seems there is a dev trade + UPHOLD, + MONEY_BEAM, + POPMONEY, + REVOLUT, + PERFECT_MONEY, + SEPA, + SEPA_INSTANT, + FASTER_PAYMENTS, + NATIONAL_BANK, + JAPAN_BANK, + SAME_BANK, + SPECIFIC_BANKS, + SWISH, + ALI_PAY, + WECHAT_PAY, + CLEAR_X_CHANGE, + CHASE_QUICK_PAY, + INTERAC_E_TRANSFER, + US_POSTAL_MONEY_ORDER, + CASH_DEPOSIT, + MONEY_GRAM, + WESTERN_UNION, + HAL_CASH, + F2F, + BLOCK_CHAINS, + PROMPT_PAY, + ADVANCED_CASH, + BLOCK_CHAINS_INSTANT + } + + private final String currency; + private final long price; + private final long amount; + private final String paymentMethod; + // As only seller is publishing it is the sellers trade date + private final long date; + + // Old converted trade stat objects might not have it set + @Nullable + @JsonExclude + private final String mediator; // todo entries from old data could be pruned + @Nullable + @JsonExclude + private final String refundAgent; + + // todo should we add referrerId as well? get added to extra map atm but not used so far + + // Hash get set in constructor from json of all the other data fields (with hash = null). + @JsonExclude + private final byte[] hash; + // Should be only used in emergency case if we need to add data but do not want to break backward compatibility + // at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new + // field in a class would break that hash and therefore break the storage mechanism. + @Nullable + @JsonExclude + private Map extraDataMap; + + public TradeStatistics3(String currency, + long price, + long amount, + String paymentMethod, + long date, + String mediator, + String refundAgent, + @Nullable Map extraDataMap) { + this(currency, + price, + amount, + paymentMethod, + date, + mediator, + refundAgent, + extraDataMap, + null); + } + + // Used from conversion method where we use the hash of the TradeStatistics2 objects to avoid duplicate entries + public TradeStatistics3(String currency, + long price, + long amount, + String paymentMethod, + long date, + String mediator, + String refundAgent, + @Nullable byte[] hash) { + this(currency, + price, + amount, + paymentMethod, + date, + mediator, + refundAgent, + null, + hash); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private TradeStatistics3(String currency, + long price, + long amount, + String paymentMethod, + long date, + @Nullable String mediator, + @Nullable String refundAgent, + @Nullable Map extraDataMap, + @Nullable byte[] hash) { + this.currency = currency; + this.price = price; + this.amount = amount; + String tempPaymentMethod; + try { + tempPaymentMethod = String.valueOf(PaymentMethodMapper.valueOf(paymentMethod).ordinal()); + } catch (Throwable t) { + tempPaymentMethod = paymentMethod; + } + this.paymentMethod = tempPaymentMethod; + this.date = date; + this.mediator = mediator; + this.refundAgent = refundAgent; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); + + this.hash = hash == null ? createHash() : hash; + } + + public byte[] createHash() { + // We create hash from all fields excluding hash itself. We use json as simple data serialisation. + // TradeDate is different for both peers so we ignore it for hash. ExtraDataMap is ignored as well as at + // software updates we might have different entries which would cause a different hash. + return Hash.getSha256Ripemd160hash(Utilities.objectToJson(this).getBytes(Charsets.UTF_8)); + } + + private protobuf.TradeStatistics3.Builder getBuilder() { + protobuf.TradeStatistics3.Builder builder = protobuf.TradeStatistics3.newBuilder() + .setCurrency(currency) + .setPrice(price) + .setAmount(amount) + .setPaymentMethod(paymentMethod) + .setDate(date) + .setHash(ByteString.copyFrom(hash)); + Optional.ofNullable(mediator).ifPresent(builder::setMediator); + Optional.ofNullable(refundAgent).ifPresent(builder::setRefundAgent); + Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); + return builder; + } + + public protobuf.TradeStatistics3 toProtoTradeStatistics3() { + return getBuilder().build(); + } + + @Override + public protobuf.PersistableNetworkPayload toProtoMessage() { + return protobuf.PersistableNetworkPayload.newBuilder().setTradeStatistics3(getBuilder()).build(); + } + + public static TradeStatistics3 fromProto(protobuf.TradeStatistics3 proto) { + return new TradeStatistics3( + proto.getCurrency(), + proto.getPrice(), + proto.getAmount(), + proto.getPaymentMethod(), + proto.getDate(), + ProtoUtil.stringOrNullFromProto(proto.getMediator()), + ProtoUtil.stringOrNullFromProto(proto.getRefundAgent()), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap(), + proto.getHash().toByteArray()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public byte[] getHash() { + return hash; + } + + @Override + public boolean verifyHashSize() { + checkNotNull(hash, "hash must not be null"); + return hash.length == 20; + } + + @Override + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.TRADE_STATISTICS_3); + } + + public String getPaymentMethod() { + try { + return PaymentMethodMapper.values()[Integer.parseInt(paymentMethod)].name(); + } catch (Throwable ignore) { + return paymentMethod; + } + } + + public Date getTradeDate() { + return new Date(date); + } + + public Price getTradePrice() { + return Price.valueOf(currency, price); + } + + public Coin getTradeAmount() { + return Coin.valueOf(amount); + } + + public Volume getTradeVolume() { + if (getTradePrice().getMonetary() instanceof Altcoin) { + return new Volume(new AltcoinExchangeRate((Altcoin) getTradePrice().getMonetary()).coinToAltcoin(getTradeAmount())); + } else { + Volume volume = new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount())); + return OfferUtil.getRoundedFiatVolume(volume); + } + } + + public boolean isValid() { + return amount > 0 && + price > 0 && + date > 0 && + paymentMethod != null && + !paymentMethod.isEmpty() && + currency != null && + !currency.isEmpty(); + } + + @Override + public String toString() { + return "TradeStatistics3{" + + "\n currency='" + currency + '\'' + + ",\n price=" + price + + ",\n amount=" + amount + + ",\n paymentMethod='" + paymentMethod + '\'' + + ",\n date=" + date + + ",\n mediator='" + mediator + '\'' + + ",\n refundAgent='" + refundAgent + '\'' + + ",\n hash=" + Utilities.bytesAsHexString(hash) + + ",\n extraDataMap=" + extraDataMap + + "\n}"; + } +} diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3StorageService.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3StorageService.java new file mode 100644 index 00000000000..16398412319 --- /dev/null +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3StorageService.java @@ -0,0 +1,84 @@ +/* + * This file is part of Bisq. + * + * bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with bisq. If not, see . + */ + +package bisq.core.trade.statistics; + +import bisq.network.p2p.storage.payload.PersistableNetworkPayload; +import bisq.network.p2p.storage.persistence.HistoricalDataStoreService; + +import bisq.common.config.Config; +import bisq.common.persistence.PersistenceManager; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.File; + +import lombok.extern.slf4j.Slf4j; + +@Singleton +@Slf4j +public class TradeStatistics3StorageService extends HistoricalDataStoreService { + private static final String FILE_NAME = "TradeStatistics3Store"; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + @Inject + public TradeStatistics3StorageService(@Named(Config.STORAGE_DIR) File storageDir, + PersistenceManager persistenceManager) { + super(storageDir, persistenceManager); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public String getFileName() { + return FILE_NAME; + } + + @Override + protected void initializePersistenceManager() { + persistenceManager.initialize(store, PersistenceManager.Source.NETWORK); + } + + @Override + public boolean canHandle(PersistableNetworkPayload payload) { + return payload instanceof TradeStatistics3; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Protected + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + protected TradeStatistics3Store createStore() { + return new TradeStatistics3Store(); + } + + public void persistNow() { + persistenceManager.persistNow(() -> { + }); + } +} diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3Store.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3Store.java new file mode 100644 index 00000000000..7c79778d4e1 --- /dev/null +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3Store.java @@ -0,0 +1,73 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.trade.statistics; + +import bisq.network.p2p.storage.P2PDataStorage; +import bisq.network.p2p.storage.persistence.PersistableNetworkPayloadStore; + +import com.google.protobuf.Message; + +import java.util.List; +import java.util.stream.Collectors; + +import lombok.extern.slf4j.Slf4j; + +/** + * We store only the payload in the PB file to save disc space. The hash of the payload can be created anyway and + * is only used as key in the map. So we have a hybrid data structure which is represented as list in the protobuffer + * definition and provide a hashMap for the domain access. + */ +@Slf4j +public class TradeStatistics3Store extends PersistableNetworkPayloadStore { + + TradeStatistics3Store() { + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private TradeStatistics3Store(List list) { + list.forEach(item -> map.put(new P2PDataStorage.ByteArray(item.getHash()), item)); + } + + public Message toProtoMessage() { + return protobuf.PersistableEnvelope.newBuilder() + .setTradeStatistics3Store(getBuilder()) + .build(); + } + + private protobuf.TradeStatistics3Store.Builder getBuilder() { + List protoList = map.values().stream() + .map(payload -> (TradeStatistics3) payload) + .map(TradeStatistics3::toProtoTradeStatistics3) + .collect(Collectors.toList()); + return protobuf.TradeStatistics3Store.newBuilder().addAllItems(protoList); + } + + public static TradeStatistics3Store fromProto(protobuf.TradeStatistics3Store proto) { + List list = proto.getItemsList().stream() + .map(TradeStatistics3::fromProto).collect(Collectors.toList()); + return new TradeStatistics3Store(list); + } + + public boolean containsKey(P2PDataStorage.ByteArray hash) { + return map.containsKey(hash); + } +} diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java new file mode 100644 index 00000000000..fc9e0bc4c5f --- /dev/null +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java @@ -0,0 +1,147 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.trade.statistics; + +import bisq.network.p2p.BootstrapListener; +import bisq.network.p2p.P2PService; +import bisq.network.p2p.storage.P2PDataStorage; +import bisq.network.p2p.storage.payload.PersistableNetworkPayload; +import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; + +import bisq.common.config.Config; +import bisq.common.file.FileUtil; + +import com.google.inject.Inject; + +import javax.inject.Named; + +import java.io.File; +import java.io.IOException; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class TradeStatisticsConverter { + + @Inject + public TradeStatisticsConverter(P2PService p2PService, + P2PDataStorage p2PDataStorage, + TradeStatistics2StorageService tradeStatistics2StorageService, + TradeStatistics3StorageService tradeStatistics3StorageService, + AppendOnlyDataStoreService appendOnlyDataStoreService, + @Named(Config.STORAGE_DIR) File storageDir) { + File tradeStatistics2Store = new File(storageDir, "TradeStatistics2Store"); + appendOnlyDataStoreService.addService(tradeStatistics2StorageService); + + p2PService.addP2PServiceListener(new BootstrapListener() { + @Override + public void onTorNodeReady() { + if (!tradeStatistics2Store.exists()) { + return; + } + + // We convert early once tor is initialized but still not ready to receive data + var mapOfLiveData = tradeStatistics3StorageService.getMapOfLiveData(); + convertToTradeStatistics3(tradeStatistics2StorageService.getMapOfAllData().values()) + .forEach(e -> mapOfLiveData.put(new P2PDataStorage.ByteArray(e.getHash()), e)); + tradeStatistics3StorageService.persistNow(); + try { + log.info("We delete now the old trade statistics file as it was converted to the new format."); + FileUtil.deleteFileIfExists(tradeStatistics2Store); + } catch (IOException e) { + e.printStackTrace(); + log.error(e.toString()); + } + } + + @Override + public void onUpdatedDataReceived() { + } + }); + + // We listen to old TradeStatistics2 objects, convert and store them and rebroadcast. + p2PDataStorage.addAppendOnlyDataStoreListener(payload -> { + if (payload instanceof TradeStatistics2) { + TradeStatistics3 tradeStatistics3 = convertToTradeStatistics3((TradeStatistics2) payload, true); + // We add it to the p2PDataStorage, which handles to get the data stored in the maps and maybe + // re-broadcast as tradeStatistics3 object if not already received. + p2PDataStorage.addPersistableNetworkPayload(tradeStatistics3, null, true); + } + }); + } + + private static Set convertToTradeStatistics3(Collection persistableNetworkPayloads) { + Set result = new HashSet<>(); + long ts = System.currentTimeMillis(); + + // We might have duplicate entries from both traders as the trade date was different from old clients. + // This should not be the case with converting old persisted data as we did filter those out but it is the case + // when we receive old trade stat objects from the network of 2 not updated traders. + // The hash was ignoring the trade date so we use that to get a unique list + Map mapWithoutDuplicates = new HashMap<>(); + persistableNetworkPayloads.stream() + .filter(e -> e instanceof TradeStatistics2) + .map(e -> (TradeStatistics2) e) + .filter(TradeStatistics2::isValid) + .forEach(e -> mapWithoutDuplicates.putIfAbsent(new P2PDataStorage.ByteArray(e.getHash()), e)); + + log.info("We convert the existing {} trade statistics objects to the new format. " + + "This might take a bit but is only done once.", mapWithoutDuplicates.size()); + + mapWithoutDuplicates.values().stream() + .map(e -> convertToTradeStatistics3(e, false)) + .filter(TradeStatistics3::isValid) + .forEach(result::add); + + log.info("Conversion to {} new trade statistic objects has been completed after {} ms", + result.size(), System.currentTimeMillis() - ts); + + return result; + } + + private static TradeStatistics3 convertToTradeStatistics3(TradeStatistics2 tradeStatistics2, boolean fromNetwork) { + Map extraDataMap = tradeStatistics2.getExtraDataMap(); + String mediator = extraDataMap != null ? extraDataMap.get(TradeStatistics2.MEDIATOR_ADDRESS) : null; + String refundAgent = extraDataMap != null ? extraDataMap.get(TradeStatistics2.REFUND_AGENT_ADDRESS) : null; + long time = tradeStatistics2.getTradeDate().getTime(); + byte[] hash = null; + if (fromNetwork) { + // We need to avoid that we duplicate tradeStatistics2 objects in case both traders have not udpated yet. + // Before v1.4.0 both traders published the trade statistics. If one trader has updated he will check + // the capabilities of the peer and if the peer has not updated he will leave publishing to the peer, so we + // do not have the problem of duplicated objects. + // To ensure we add only one object we will use the hash of the tradeStatistics2 object which is the same + // for both traders as it excluded the trade date which is different for both. + hash = tradeStatistics2.getHash(); + } + return new TradeStatistics3(tradeStatistics2.getCurrencyCode(), + tradeStatistics2.getTradePrice().getValue(), + tradeStatistics2.getTradeAmount().getValue(), + tradeStatistics2.getOfferPaymentMethod(), + time, + mediator, + refundAgent, + hash); + } +} diff --git a/monitor/src/main/java/bisq/monitor/Monitor.java b/monitor/src/main/java/bisq/monitor/Monitor.java index 19691c3d66f..c382b82cf24 100644 --- a/monitor/src/main/java/bisq/monitor/Monitor.java +++ b/monitor/src/main/java/bisq/monitor/Monitor.java @@ -90,7 +90,8 @@ private void start() throws Throwable { Capability.DAO_STATE, Capability.BUNDLE_OF_ENVELOPES, Capability.REFUND_AGENT, - Capability.MEDIATION); + Capability.MEDIATION, + Capability.TRADE_STATISTICS_3); // assemble Metrics // - create reporters diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 11c32109811..9e701ae5f25 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -524,10 +524,11 @@ message StoragePayload { message PersistableNetworkPayload { oneof message { AccountAgeWitness account_age_witness = 1; - TradeStatistics2 trade_statistics2 = 2; + TradeStatistics2 trade_statistics2 = 2 [deprecated = true]; ProposalPayload proposal_payload = 3; BlindVotePayload blind_vote_payload = 4; SignedWitness signed_witness = 5; + TradeStatistics3 trade_statistics3 = 6; } } @@ -672,22 +673,34 @@ message TradeStatistics { } message TradeStatistics2 { - string base_currency = 1; - string counter_currency = 2; - OfferPayload.Direction direction = 3; - int64 trade_price = 4; - int64 trade_amount = 5; - int64 trade_date = 6; - string payment_method_id = 7; - int64 offer_date = 8; - bool offer_use_market_based_price = 9; - double offer_market_price_margin = 10; - int64 offer_amount = 11; - int64 offer_min_amount = 12; - string offer_id = 13; - string deposit_tx_id = 14; - bytes hash = 15; - map extra_data = 16; + string base_currency = 1 [deprecated = true]; + string counter_currency = 2 [deprecated = true]; + OfferPayload.Direction direction = 3 [deprecated = true]; + int64 trade_price = 4 [deprecated = true]; + int64 trade_amount = 5 [deprecated = true]; + int64 trade_date = 6 [deprecated = true]; + string payment_method_id = 7 [deprecated = true]; + int64 offer_date = 8 [deprecated = true]; + bool offer_use_market_based_price = 9 [deprecated = true]; + double offer_market_price_margin = 10 [deprecated = true]; + int64 offer_amount = 11 [deprecated = true]; + int64 offer_min_amount = 12 [deprecated = true]; + string offer_id = 13 [deprecated = true]; + string deposit_tx_id = 14 [deprecated = true]; + bytes hash = 15 [deprecated = true]; + map extra_data = 16 [deprecated = true]; +} + +message TradeStatistics3 { + string currency = 1; + int64 price = 2; + int64 amount = 3; + string payment_method = 4; + int64 date = 5; + string mediator = 6; + string refund_agent = 7; + bytes hash = 8; + map extra_data = 9; } message MailboxStoragePayload { @@ -1152,7 +1165,7 @@ message PersistableEnvelope { // BsqState bsq_state = 12; // not used but as other non-dao data have a higher index number we leave it to make clear that we cannot change following indexes AccountAgeWitnessStore account_age_witness_store = 13; - TradeStatistics2Store trade_statistics2_store = 14; + TradeStatistics2Store trade_statistics2_store = 14 [deprecated = true]; // PersistableNetworkPayloadList persistable_network_payload_list = 15; // long deprecated & migration away from it is already done @@ -1171,6 +1184,7 @@ message PersistableEnvelope { SignedWitnessStore signed_witness_store = 28; MediationDisputeList mediation_dispute_list = 29; RefundDisputeList refund_dispute_list = 30; + TradeStatistics3Store trade_statistics3_store = 31; } } @@ -1211,8 +1225,13 @@ message SignedWitnessStore { } // We use a list not a hash map to save disc space. The hash can be calculated from the payload anyway +// Deprecated message TradeStatistics2Store { - repeated TradeStatistics2 items = 1; + repeated TradeStatistics2 items = 1 [deprecated = true]; +} + +message TradeStatistics3Store { + repeated TradeStatistics3 items = 1; } message PeerList { From 406bcfb06474831638c975da836a4ca066262df9 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 15:26:16 -0500 Subject: [PATCH 069/123] Remove PublishTradeStatistics from buyer protocol We let seller publish trade stats to avoid those issues with duplicated entries as trade date is different. We could fix that to use the same trade date, but it seems to be not needed that both traders are publishing and the risk if a trade stat does not get successfully published does not cause real problems. There is guarantee anyway that the data is broadcast even if both do it. In case we still want to do it from both sides we need to use the sellers trade date which is exchanged early in the trade protocol but yet not further used beside for account age check. --- .../src/main/java/bisq/core/trade/protocol/BuyerProtocol.java | 4 +--- desktop/src/main/java/bisq/desktop/main/debug/DebugView.java | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java index 64c718ce412..ffff35a79bb 100644 --- a/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java @@ -24,7 +24,6 @@ import bisq.core.trade.messages.PayoutTxPublishedMessage; import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.protocol.tasks.ApplyFilter; -import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDepositTxAndDelayedPayoutTxMessage; import bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage; @@ -114,8 +113,7 @@ protected void handle(DepositTxAndDelayedPayoutTxMessage message, NodeAddress pe removeMailboxMessageAfterProcessing(message); })) .setup(tasks(BuyerProcessDepositTxAndDelayedPayoutTxMessage.class, - BuyerVerifiesFinalDelayedPayoutTx.class, - PublishTradeStatistics.class) + BuyerVerifiesFinalDelayedPayoutTx.class) .using(new TradeTaskRunner(trade, () -> { stopTimeout(); diff --git a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java index 86b0c4d0d05..cbf2ca618d9 100644 --- a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java +++ b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java @@ -179,7 +179,6 @@ public void initialize() { BuyerProcessDepositTxAndDelayedPayoutTxMessage.class, BuyerVerifiesFinalDelayedPayoutTx.class, - PublishTradeStatistics.class, ApplyFilter.class, MakerVerifyTakerFeePayment.class, @@ -216,7 +215,6 @@ public void initialize() { BuyerProcessDepositTxAndDelayedPayoutTxMessage.class, BuyerVerifiesFinalDelayedPayoutTx.class, - PublishTradeStatistics.class, ApplyFilter.class, TakerVerifyMakerFeePayment.class, From b75aa6771f286d09a227acbcb125bc84d905ae94 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 15:26:41 -0500 Subject: [PATCH 070/123] Refactor: Move class --- .../src/main/java/bisq/core/trade/protocol/SellerProtocol.java | 2 +- .../protocol/tasks/{ => seller}/PublishTradeStatistics.java | 3 ++- desktop/src/main/java/bisq/desktop/main/debug/DebugView.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) rename core/src/main/java/bisq/core/trade/protocol/tasks/{ => seller}/PublishTradeStatistics.java (96%) diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java index 35eea700b6c..e5c559b1517 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java @@ -23,8 +23,8 @@ import bisq.core.trade.messages.DelayedPayoutTxSignatureResponse; import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.protocol.tasks.ApplyFilter; -import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.TradeTask; +import bisq.core.trade.protocol.tasks.seller.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/PublishTradeStatistics.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/PublishTradeStatistics.java similarity index 96% rename from core/src/main/java/bisq/core/trade/protocol/tasks/PublishTradeStatistics.java rename to core/src/main/java/bisq/core/trade/protocol/tasks/seller/PublishTradeStatistics.java index 8eb71ffdd87..c894c5413b3 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/PublishTradeStatistics.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/PublishTradeStatistics.java @@ -15,11 +15,12 @@ * along with Bisq. If not, see . */ -package bisq.core.trade.protocol.tasks; +package bisq.core.trade.protocol.tasks.seller; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; import bisq.core.trade.Trade; +import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.statistics.TradeStatistics2; import bisq.network.p2p.NodeAddress; diff --git a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java index cbf2ca618d9..16c767db612 100644 --- a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java +++ b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java @@ -27,7 +27,6 @@ import bisq.core.offer.placeoffer.tasks.CreateMakerFeeTx; import bisq.core.offer.placeoffer.tasks.ValidateOffer; import bisq.core.trade.protocol.tasks.ApplyFilter; -import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest; import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDepositTxAndDelayedPayoutTxMessage; @@ -50,6 +49,7 @@ import bisq.core.trade.protocol.tasks.maker.MakerRemovesOpenOffer; import bisq.core.trade.protocol.tasks.maker.MakerSetsLockTime; import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment; +import bisq.core.trade.protocol.tasks.seller.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx; From a522d0ade9870af3c4a51dc4d6ce59a123e1e34a Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 15:26:56 -0500 Subject: [PATCH 071/123] Refactor: Rename class --- .../main/java/bisq/core/trade/protocol/SellerProtocol.java | 4 ++-- ...eStatistics.java => SellerPublishesTradeStatistics.java} | 4 ++-- .../src/main/java/bisq/desktop/main/debug/DebugView.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename core/src/main/java/bisq/core/trade/protocol/tasks/seller/{PublishTradeStatistics.java => SellerPublishesTradeStatistics.java} (95%) diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java index e5c559b1517..1b4815f4678 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java @@ -24,12 +24,12 @@ import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.TradeTask; -import bisq.core.trade.protocol.tasks.seller.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage; import bisq.core.trade.protocol.tasks.seller.SellerProcessDelayedPayoutTxSignatureResponse; import bisq.core.trade.protocol.tasks.seller.SellerPublishesDepositTx; +import bisq.core.trade.protocol.tasks.seller.SellerPublishesTradeStatistics; import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage; import bisq.core.trade.protocol.tasks.seller.SellerSendsDepositTxAndDelayedPayoutTxMessage; import bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx; @@ -79,7 +79,7 @@ protected void handle(DelayedPayoutTxSignatureResponse message, NodeAddress peer SellerFinalizesDelayedPayoutTx.class, SellerSendsDepositTxAndDelayedPayoutTxMessage.class, SellerPublishesDepositTx.class, - PublishTradeStatistics.class)) + SellerPublishesTradeStatistics.class)) .run(() -> { // We stop timeout here and don't start a new one as the // SellerSendsDepositTxAndDelayedPayoutTxMessage repeats the send the message and has it's own diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/PublishTradeStatistics.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java similarity index 95% rename from core/src/main/java/bisq/core/trade/protocol/tasks/seller/PublishTradeStatistics.java rename to core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java index c894c5413b3..2dfb52e44f5 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/PublishTradeStatistics.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java @@ -37,8 +37,8 @@ import static com.google.common.base.Preconditions.checkNotNull; @Slf4j -public class PublishTradeStatistics extends TradeTask { - public PublishTradeStatistics(TaskRunner taskHandler, Trade trade) { +public class SellerPublishesTradeStatistics extends TradeTask { + public SellerPublishesTradeStatistics(TaskRunner taskHandler, Trade trade) { super(taskHandler, trade); } diff --git a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java index 16c767db612..89d0acbf090 100644 --- a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java +++ b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java @@ -49,13 +49,13 @@ import bisq.core.trade.protocol.tasks.maker.MakerRemovesOpenOffer; import bisq.core.trade.protocol.tasks.maker.MakerSetsLockTime; import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment; -import bisq.core.trade.protocol.tasks.seller.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage; import bisq.core.trade.protocol.tasks.seller.SellerProcessDelayedPayoutTxSignatureResponse; import bisq.core.trade.protocol.tasks.seller.SellerPublishesDepositTx; +import bisq.core.trade.protocol.tasks.seller.SellerPublishesTradeStatistics; import bisq.core.trade.protocol.tasks.seller.SellerSendDelayedPayoutTxSignatureRequest; import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage; import bisq.core.trade.protocol.tasks.seller.SellerSendsDepositTxAndDelayedPayoutTxMessage; @@ -145,7 +145,7 @@ public void initialize() { SellerFinalizesDelayedPayoutTx.class, SellerSendsDepositTxAndDelayedPayoutTxMessage.class, SellerPublishesDepositTx.class, - PublishTradeStatistics.class, + SellerPublishesTradeStatistics.class, SellerProcessCounterCurrencyTransferStartedMessage.class, ApplyFilter.class, @@ -246,7 +246,7 @@ public void initialize() { SellerFinalizesDelayedPayoutTx.class, SellerSendsDepositTxAndDelayedPayoutTxMessage.class, SellerPublishesDepositTx.class, - PublishTradeStatistics.class, + SellerPublishesTradeStatistics.class, SellerProcessCounterCurrencyTransferStartedMessage.class, ApplyFilter.class, From b2665bdd2cf73659e41fa763f05abb19654560d8 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 17:43:35 -0500 Subject: [PATCH 072/123] let seller publish trade statistics only if peer is updated user. If not the peer will publish only. --- .../SellerPublishesTradeStatistics.java | 76 ++++++++++++------- .../java/bisq/network/p2p/P2PService.java | 12 +++ 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java index 2dfb52e44f5..0489ac0e325 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java @@ -21,12 +21,13 @@ import bisq.core.offer.OfferPayload; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.NetworkNode; import bisq.network.p2p.network.TorNetworkNode; +import bisq.common.app.Capability; import bisq.common.taskrunner.TaskRunner; import java.util.HashMap; @@ -49,31 +50,54 @@ protected void run() { checkNotNull(trade.getDepositTx()); - Map extraDataMap = new HashMap<>(); - if (processModel.getReferralIdService().getOptionalReferralId().isPresent()) { - extraDataMap.put(OfferPayload.REFERRAL_ID, processModel.getReferralIdService().getOptionalReferralId().get()); - } - - NodeAddress mediatorNodeAddress = checkNotNull(trade.getMediatorNodeAddress()); - // The first 4 chars are sufficient to identify a mediator. - // For testing with regtest/localhost we use the full address as its localhost and would result in - // same values for multiple mediators. - NetworkNode networkNode = model.getProcessModel().getP2PService().getNetworkNode(); - String address = networkNode instanceof TorNetworkNode ? - mediatorNodeAddress.getFullAddress().substring(0, 4) : - mediatorNodeAddress.getFullAddress(); - extraDataMap.put(TradeStatistics2.MEDIATOR_ADDRESS, address); - - Offer offer = checkNotNull(trade.getOffer()); - TradeStatistics2 tradeStatistics = new TradeStatistics2(offer.getOfferPayload(), - trade.getTradePrice(), - checkNotNull(trade.getTradeAmount()), - trade.getDate(), - trade.getDepositTxId(), - extraDataMap); - processModel.getP2PService().addPersistableNetworkPayload(tradeStatistics, true); - - complete(); + processModel.getP2PService().findPeersCapabilities(trade.getTradingPeerNodeAddress()) + .filter(capabilities -> capabilities.containsAll(Capability.TRADE_STATISTICS_3)) + .ifPresentOrElse(capabilities -> { + // Our peer has updated, so as we are the seller we will publish the trade statistics. + // The peer as buyer does not publish anymore with v.1.4.0 (where Capability.TRADE_STATISTICS_3 was added) + + Map extraDataMap = new HashMap<>(); + if (processModel.getReferralIdService().getOptionalReferralId().isPresent()) { + extraDataMap.put(OfferPayload.REFERRAL_ID, processModel.getReferralIdService().getOptionalReferralId().get()); + } + + NodeAddress mediatorNodeAddress = checkNotNull(trade.getMediatorNodeAddress()); + // The first 4 chars are sufficient to identify a mediator. + // For testing with regtest/localhost we use the full address as its localhost and would result in + // same values for multiple mediators. + NetworkNode networkNode = model.getProcessModel().getP2PService().getNetworkNode(); + String truncatedMediatorNodeAddress = networkNode instanceof TorNetworkNode ? + mediatorNodeAddress.getFullAddress().substring(0, 4) : + mediatorNodeAddress.getFullAddress(); + + NodeAddress refundAgentNodeAddress = checkNotNull(trade.getRefundAgentNodeAddress()); + String truncatedRefundAgentNodeAddress = networkNode instanceof TorNetworkNode ? + refundAgentNodeAddress.getFullAddress().substring(0, 4) : + refundAgentNodeAddress.getFullAddress(); + + Offer offer = checkNotNull(trade.getOffer()); + TradeStatistics3 tradeStatistics = new TradeStatistics3(offer.getCurrencyCode(), + trade.getTradePrice().getValue(), + trade.getTradeAmountAsLong(), + offer.getPaymentMethod().getId(), + trade.getTakeOfferDate().getTime(), + truncatedMediatorNodeAddress, + truncatedRefundAgentNodeAddress, + extraDataMap); + if (tradeStatistics.isValid()) { + log.info("Publishing trade statistics"); + processModel.getP2PService().addPersistableNetworkPayload(tradeStatistics, true); + } else { + log.warn("Trade statistics are invalid. We do not publish. {}", tradeStatistics); + } + + complete(); + }, + () -> { + log.info("Our peer does not has updated yet, so they will publish the trade statistics. " + + "To avoid duplicates we do not publish from our side."); + complete(); + }); } catch (Throwable t) { failed(t); } diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 95b68f0c611..bf594ecf6fc 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -920,6 +920,18 @@ public KeyRing getKeyRing() { return keyRing; } + public Optional findPeersCapabilities(NodeAddress peer) { + return networkNode.getConfirmedConnections().stream() + .filter(e -> e.getPeersNodeAddressOptional().isPresent()) + .filter(e -> e.getPeersNodeAddressOptional().get().equals(peer)) + .map(Connection::getCapabilities) + .findAny(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// @Value public class MailboxItem { From 6d43c09b33e4a5de6fb0cae17d3430c13d89b9f1 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 17:53:13 -0500 Subject: [PATCH 073/123] Delete TradeStatistics (version 1) --- .../network/CoreNetworkProtoResolver.java | 4 - .../trade/statistics/TradeStatistics.java | 236 ------------------ proto/src/main/proto/pb.proto | 26 +- 3 files changed, 2 insertions(+), 264 deletions(-) delete mode 100644 core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java diff --git a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java index 8d57a75c14b..675011cec3d 100644 --- a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java @@ -59,7 +59,6 @@ import bisq.core.trade.messages.PeerPublishedDelayedPayoutTxMessage; import bisq.core.trade.messages.RefreshTradeStateRequest; import bisq.core.trade.messages.TraderSignedWitnessMessage; -import bisq.core.trade.statistics.TradeStatistics; import bisq.network.p2p.AckMessage; import bisq.network.p2p.BundleOfEnvelopes; @@ -265,9 +264,6 @@ public NetworkPayload fromProto(protobuf.StoragePayload proto) { return RefundAgent.fromProto(proto.getRefundAgent()); case FILTER: return Filter.fromProto(proto.getFilter()); - case TRADE_STATISTICS: - // Still used to convert TradeStatistics data from pre v0.6 versions - return TradeStatistics.fromProto(proto.getTradeStatistics()); case MAILBOX_STORAGE_PAYLOAD: return MailboxStoragePayload.fromProto(proto.getMailboxStoragePayload()); case OFFER_PAYLOAD: diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java deleted file mode 100644 index ad543213191..00000000000 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.trade.statistics; - -import bisq.core.monetary.Altcoin; -import bisq.core.monetary.AltcoinExchangeRate; -import bisq.core.monetary.Price; -import bisq.core.monetary.Volume; -import bisq.core.offer.OfferPayload; - -import bisq.network.p2p.storage.payload.ExpirablePayload; -import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload; -import bisq.network.p2p.storage.payload.ProtectedStoragePayload; - -import bisq.common.crypto.Sig; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.CollectionUtils; -import bisq.common.util.ExtraDataMapValidator; -import bisq.common.util.JsonExclude; - -import com.google.protobuf.ByteString; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.utils.ExchangeRate; -import org.bitcoinj.utils.Fiat; - -import java.security.PublicKey; - -import java.util.Date; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -/** - * @deprecated Was used in pre v0.6.0 version - */ -@Deprecated -@Slf4j -@EqualsAndHashCode(exclude = {"signaturePubKeyBytes"}) -@Value -public final class TradeStatistics implements ProcessOncePersistableNetworkPayload, ProtectedStoragePayload, ExpirablePayload, PersistablePayload { - private final OfferPayload.Direction direction; - private final String baseCurrency; - private final String counterCurrency; - private final String offerPaymentMethod; - private final long offerDate; - private final boolean offerUseMarketBasedPrice; - private final double offerMarketPriceMargin; - private final long offerAmount; - private final long offerMinAmount; - private final String offerId; - private final long tradePrice; - private final long tradeAmount; - private final long tradeDate; - private final String depositTxId; - @JsonExclude - private final byte[] signaturePubKeyBytes; - @JsonExclude - transient private final PublicKey signaturePubKey; - - // Should be only used in emergency case if we need to add data but do not want to break backward compatibility - // at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new - // field in a class would break that hash and therefore break the storage mechanism. - @Nullable - private Map extraDataMap; - - public TradeStatistics(OfferPayload offerPayload, - Price tradePrice, - Coin tradeAmount, - Date tradeDate, - String depositTxId, - byte[] signaturePubKeyBytes) { - this(offerPayload.getDirection(), - offerPayload.getBaseCurrencyCode(), - offerPayload.getCounterCurrencyCode(), - offerPayload.getPaymentMethodId(), - offerPayload.getDate(), - offerPayload.isUseMarketBasedPrice(), - offerPayload.getMarketPriceMargin(), - offerPayload.getAmount(), - offerPayload.getMinAmount(), - offerPayload.getId(), - tradePrice.getValue(), - tradeAmount.value, - tradeDate.getTime(), - depositTxId, - signaturePubKeyBytes, - null); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - TradeStatistics(OfferPayload.Direction direction, - String baseCurrency, - String counterCurrency, - String offerPaymentMethod, - long offerDate, - boolean offerUseMarketBasedPrice, - double offerMarketPriceMargin, - long offerAmount, - long offerMinAmount, - String offerId, - long tradePrice, - long tradeAmount, - long tradeDate, - String depositTxId, - byte[] signaturePubKeyBytes, - @Nullable Map extraDataMap) { - this.direction = direction; - this.baseCurrency = baseCurrency; - this.counterCurrency = counterCurrency; - this.offerPaymentMethod = offerPaymentMethod; - this.offerDate = offerDate; - this.offerUseMarketBasedPrice = offerUseMarketBasedPrice; - this.offerMarketPriceMargin = offerMarketPriceMargin; - this.offerAmount = offerAmount; - this.offerMinAmount = offerMinAmount; - this.offerId = offerId; - this.tradePrice = tradePrice; - this.tradeAmount = tradeAmount; - this.tradeDate = tradeDate; - this.depositTxId = depositTxId; - this.signaturePubKeyBytes = signaturePubKeyBytes; - this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); - - signaturePubKey = Sig.getPublicKeyFromBytes(signaturePubKeyBytes); - } - - @Override - public protobuf.StoragePayload toProtoMessage() { - final protobuf.TradeStatistics.Builder builder = protobuf.TradeStatistics.newBuilder() - .setDirection(OfferPayload.Direction.toProtoMessage(direction)) - .setBaseCurrency(baseCurrency) - .setCounterCurrency(counterCurrency) - .setPaymentMethodId(offerPaymentMethod) - .setOfferDate(offerDate) - .setOfferUseMarketBasedPrice(offerUseMarketBasedPrice) - .setOfferMarketPriceMargin(offerMarketPriceMargin) - .setOfferAmount(offerAmount) - .setOfferMinAmount(offerMinAmount) - .setOfferId(offerId) - .setTradePrice(tradePrice) - .setTradeAmount(tradeAmount) - .setTradeDate(tradeDate) - .setDepositTxId(depositTxId) - .setSignaturePubKeyBytes(ByteString.copyFrom(signaturePubKeyBytes)); - Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); - return protobuf.StoragePayload.newBuilder().setTradeStatistics(builder).build(); - } - - public protobuf.TradeStatistics toProtoTradeStatistics() { - return toProtoMessage().getTradeStatistics(); - } - - public static TradeStatistics fromProto(protobuf.TradeStatistics proto) { - return new TradeStatistics( - OfferPayload.Direction.fromProto(proto.getDirection()), - proto.getBaseCurrency(), - proto.getCounterCurrency(), - proto.getPaymentMethodId(), - proto.getOfferDate(), - proto.getOfferUseMarketBasedPrice(), - proto.getOfferMarketPriceMargin(), - proto.getOfferAmount(), - proto.getOfferMinAmount(), - proto.getOfferId(), - proto.getTradePrice(), - proto.getTradeAmount(), - proto.getTradeDate(), - proto.getDepositTxId(), - proto.getSignaturePubKeyBytes().toByteArray(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public long getTTL() { - return TimeUnit.DAYS.toMillis(30); - } - - @Override - public PublicKey getOwnerPubKey() { - return signaturePubKey; - } - - public Date getTradeDate() { - return new Date(tradeDate); - } - - public Price getTradePrice() { - return Price.valueOf(getCurrencyCode(), tradePrice); - } - - public String getCurrencyCode() { - return baseCurrency.equals("BTC") ? counterCurrency : baseCurrency; - } - - public Coin getTradeAmount() { - return Coin.valueOf(tradeAmount); - } - - public Volume getTradeVolume() { - if (getTradePrice().getMonetary() instanceof Altcoin) - return new Volume(new AltcoinExchangeRate((Altcoin) getTradePrice().getMonetary()).coinToAltcoin(getTradeAmount())); - else - return new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount())); - } -} diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 9e701ae5f25..8d7b152bd27 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -510,9 +510,7 @@ message StoragePayload { Mediator mediator = 3; Filter filter = 4; - // not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older - // versions and convert it to TradeStatistics2 objects. - TradeStatistics trade_statistics = 5 [deprecated = true]; + // TradeStatistics trade_statistics = 5 [deprecated = true]; Removed in v.1.4.0 MailboxStoragePayload mailbox_storage_payload = 6; OfferPayload offer_payload = 7; @@ -651,27 +649,7 @@ message Filter { bool disable_auto_conf = 24; } -// not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older -// versions and convert it to TradeStatistics2 objects. -message TradeStatistics { - string base_currency = 1; - string counter_currency = 2; - OfferPayload.Direction direction = 3; - int64 trade_price = 4; - int64 trade_amount = 5; - int64 trade_date = 6; - string payment_method_id = 7; - int64 offer_date = 8; - bool offer_use_market_based_price = 9; - double offer_market_price_margin = 10; - int64 offer_amount = 11; - int64 offer_min_amount = 12; - string offer_id = 13; - string deposit_tx_id = 14; - bytes signature_pub_key_bytes = 15; - map extra_data = 16; -} - +// Deprecated message TradeStatistics2 { string base_currency = 1 [deprecated = true]; string counter_currency = 2 [deprecated = true]; From 98207518d61bdabfc3c26a285c096aa6b8df41cc Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 17:59:24 -0500 Subject: [PATCH 074/123] We "hack" TradeStatistics2StorageService to fulfill our needs: 1. We do not want that initial data request/response use old trades statistics for excluded key hashes. Thats why we return an empty map in getMap. 2. We do not read resource file as we have removed that. 3. We do not persist as we convert the existing data and re-publish as new data, or at startup we convert the old data to the new one and then delete the file. --- .../TradeStatistics2StorageService.java | 23 +++++++++++++++++-- .../storage/persistence/MapStoreService.java | 2 +- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2StorageService.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2StorageService.java index 52822e2f2e0..27024943903 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2StorageService.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2StorageService.java @@ -29,6 +29,7 @@ import java.io.File; +import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; @@ -65,11 +66,29 @@ public String getFileName() { @Override public Map getMap() { - return store.getMap(); + // As it is used for data request and response and we do not want to send any old trade stat data anymore. + return new HashMap<>(); + } + + // We overwrite that method to receive old trade stats from the network. As we deactivated getMap to not deliver + // hashes we needed to use the getMapOfAllData method to actually store the data. + // That's a bit of a hack but it's just for transition and can be removed after a few months anyway. + // Alternatively we could create a new interface to handle it differently on the other client classes but that + // seems to be not justified as it is needed only temporarily. + @Override + protected PersistableNetworkPayload putIfAbsent(P2PDataStorage.ByteArray hash, PersistableNetworkPayload payload) { + PersistableNetworkPayload previous = getMapOfAllData().putIfAbsent(hash, payload); + return previous; + } + + @Override + protected void readFromResources(String postFix) { + // We do not attempt to read from resources as that file is not provided anymore + readStore(); } public Map getMapOfAllData() { - return getMap(); + return store.getMap(); } @Override diff --git a/p2p/src/main/java/bisq/network/p2p/storage/persistence/MapStoreService.java b/p2p/src/main/java/bisq/network/p2p/storage/persistence/MapStoreService.java index 52f37bf44e5..7be3e4d1906 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/persistence/MapStoreService.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/persistence/MapStoreService.java @@ -61,7 +61,7 @@ void put(P2PDataStorage.ByteArray hash, R payload) { requestPersistence(); } - R putIfAbsent(P2PDataStorage.ByteArray hash, R payload) { + protected R putIfAbsent(P2PDataStorage.ByteArray hash, R payload) { R previous = getMap().putIfAbsent(hash, payload); requestPersistence(); return previous; From c4a4c878b86830adaf8fb20899123034a508a4a2 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 18:12:49 -0500 Subject: [PATCH 075/123] Apply TradeStatistics3 to TradeStatisticsManager and some related classes --- .../core/provider/price/PriceFeedService.java | 17 +-- .../java/bisq/core/trade/TradeModule.java | 4 - .../statistics/TradeStatisticsForJson.java | 52 +++---- .../statistics/TradeStatisticsManager.java | 129 ++++++------------ 4 files changed, 67 insertions(+), 135 deletions(-) diff --git a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java index 3db0e855e09..168812b4f43 100644 --- a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java @@ -23,7 +23,7 @@ import bisq.core.monetary.Price; import bisq.core.provider.PriceNodeHttpClient; import bisq.core.provider.ProvidersRepository; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.user.Preferences; import bisq.network.http.HttpClient; @@ -50,6 +50,7 @@ import java.time.Instant; import java.util.ArrayList; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -288,12 +289,12 @@ public Date getLastRequestTimeStamp() { return new Date(epochInMillisAtLastRequest); } - public void applyLatestBisqMarketPrice(Set tradeStatisticsSet) { + public void applyLatestBisqMarketPrice(Set tradeStatisticsSet) { // takes about 10 ms for 5000 items - Map> mapByCurrencyCode = new HashMap<>(); + Map> mapByCurrencyCode = new HashMap<>(); tradeStatisticsSet.forEach(e -> { - final List list; - final String currencyCode = e.getCurrencyCode(); + List list; + String currencyCode = e.getCurrency(); if (mapByCurrencyCode.containsKey(currencyCode)) { list = mapByCurrencyCode.get(currencyCode); } else { @@ -306,9 +307,9 @@ public void applyLatestBisqMarketPrice(Set tradeStatisticsSet) mapByCurrencyCode.values().stream() .filter(list -> !list.isEmpty()) .forEach(list -> { - list.sort((o1, o2) -> o1.getTradeDate().compareTo(o2.getTradeDate())); - TradeStatistics2 tradeStatistics = list.get(list.size() - 1); - setBisqMarketPrice(tradeStatistics.getCurrencyCode(), tradeStatistics.getTradePrice()); + list.sort(Comparator.comparing(o -> o.getTradeDate())); + TradeStatistics3 tradeStatistics = list.get(list.size() - 1); + setBisqMarketPrice(tradeStatistics.getCurrency(), tradeStatistics.getTradePrice()); }); } diff --git a/core/src/main/java/bisq/core/trade/TradeModule.java b/core/src/main/java/bisq/core/trade/TradeModule.java index 779cf6e7600..751bd5a0eb1 100644 --- a/core/src/main/java/bisq/core/trade/TradeModule.java +++ b/core/src/main/java/bisq/core/trade/TradeModule.java @@ -24,8 +24,6 @@ import bisq.core.trade.closed.ClosedTradableManager; import bisq.core.trade.failed.FailedTradesManager; import bisq.core.trade.statistics.ReferralIdService; -import bisq.core.trade.statistics.TradeStatistics2StorageService; -import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.common.app.AppModule; import bisq.common.config.Config; @@ -46,8 +44,6 @@ public TradeModule(Config config) { @Override protected void configure() { bind(TradeManager.class).in(Singleton.class); - bind(TradeStatisticsManager.class).in(Singleton.class); - bind(TradeStatistics2StorageService.class).in(Singleton.class); bind(ClosedTradableManager.class).in(Singleton.class); bind(FailedTradesManager.class).in(Singleton.class); bind(AccountAgeWitnessService.class).in(Singleton.class); diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java index fbf675dd8e5..ebff70616be 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java @@ -21,7 +21,6 @@ import bisq.core.locale.Res; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; -import bisq.core.offer.OfferPayload; import bisq.common.util.MathUtils; @@ -38,65 +37,44 @@ @ToString @Slf4j public final class TradeStatisticsForJson { - public final String currency; - public final OfferPayload.Direction direction; public final long tradePrice; public final long tradeAmount; public final long tradeDate; public final String paymentMethod; - public final long offerDate; - public final boolean useMarketBasedPrice; - public final double marketPriceMargin; - public final long offerAmount; - public final long offerMinAmount; - public final String offerId; - public final String depositTxId; // primaryMarket fields are based on industry standard where primaryMarket is always in the focus (in the app BTC is always in the focus - will be changed in a larger refactoring once) public String currencyPair; - public OfferPayload.Direction primaryMarketDirection; public long primaryMarketTradePrice; public long primaryMarketTradeAmount; public long primaryMarketTradeVolume; - public TradeStatisticsForJson(TradeStatistics2 tradeStatistics) { - this.direction = OfferPayload.Direction.valueOf(tradeStatistics.getDirection().name()); - this.currency = tradeStatistics.getCurrencyCode(); - this.paymentMethod = tradeStatistics.getOfferPaymentMethod(); - this.offerDate = tradeStatistics.getOfferDate(); - this.useMarketBasedPrice = tradeStatistics.isOfferUseMarketBasedPrice(); - this.marketPriceMargin = tradeStatistics.getOfferMarketPriceMargin(); - this.offerAmount = tradeStatistics.getOfferAmount(); - this.offerMinAmount = tradeStatistics.getOfferMinAmount(); - this.offerId = tradeStatistics.getOfferId(); - this.tradePrice = tradeStatistics.getTradePrice().getValue(); - this.tradeAmount = tradeStatistics.getTradeAmount().getValue(); - this.tradeDate = tradeStatistics.getTradeDate().getTime(); - this.depositTxId = tradeStatistics.getDepositTxId(); + public TradeStatisticsForJson(TradeStatistics3 tradeStatistics) { + this.currency = tradeStatistics.getCurrency(); + this.paymentMethod = tradeStatistics.getPaymentMethod(); + this.tradePrice = tradeStatistics.getPrice(); + this.tradeAmount = tradeStatistics.getAmount(); + this.tradeDate = tradeStatistics.getDate(); try { - final Price tradePrice = getTradePrice(); + Price tradePrice = getTradePrice(); if (CurrencyUtil.isCryptoCurrency(currency)) { - primaryMarketDirection = direction == OfferPayload.Direction.BUY ? OfferPayload.Direction.SELL : OfferPayload.Direction.BUY; currencyPair = currency + "/" + Res.getBaseCurrencyCode(); - primaryMarketTradePrice = tradePrice.getValue(); - - primaryMarketTradeAmount = getTradeVolume() != null ? getTradeVolume().getValue() : 0; + primaryMarketTradeAmount = getTradeVolume() != null ? + getTradeVolume().getValue() : + 0; primaryMarketTradeVolume = getTradeAmount().getValue(); } else { - primaryMarketDirection = direction; currencyPair = Res.getBaseCurrencyCode() + "/" + currency; - // we use precision 4 for fiat based price but on the markets api we use precision 8 so we scale up by 10000 primaryMarketTradePrice = (long) MathUtils.scaleUpByPowerOf10(tradePrice.getValue(), 4); - primaryMarketTradeAmount = getTradeAmount().getValue(); // we use precision 4 for fiat but on the markets api we use precision 8 so we scale up by 10000 primaryMarketTradeVolume = getTradeVolume() != null ? - (long) MathUtils.scaleUpByPowerOf10(getTradeVolume().getValue(), 4) : 0; + (long) MathUtils.scaleUpByPowerOf10(getTradeVolume().getValue(), 4) : + 0; } } catch (Throwable t) { log.error(t.getMessage()); @@ -113,6 +91,10 @@ public Coin getTradeAmount() { } public Volume getTradeVolume() { - return getTradePrice().getVolumeByAmount(getTradeAmount()); + try { + return getTradePrice().getVolumeByAmount(getTradeAmount()); + } catch (Throwable t) { + return Volume.parse("0", currency); + } } } diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java index 4dbe86c45bb..7e571391c84 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -32,6 +32,7 @@ import com.google.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; import javafx.collections.FXCollections; import javafx.collections.ObservableSet; @@ -40,94 +41,75 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +@Singleton @Slf4j public class TradeStatisticsManager { - - private final JsonFileManager jsonFileManager; private final P2PService p2PService; private final PriceFeedService priceFeedService; - private final TradeStatistics2StorageService tradeStatistics2StorageService; + private final TradeStatistics3StorageService tradeStatistics3StorageService; + private final File storageDir; private final boolean dumpStatistics; - private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet(); + private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet(); + private JsonFileManager jsonFileManager; @Inject public TradeStatisticsManager(P2PService p2PService, PriceFeedService priceFeedService, - TradeStatistics2StorageService tradeStatistics2StorageService, + TradeStatistics3StorageService tradeStatistics3StorageService, AppendOnlyDataStoreService appendOnlyDataStoreService, @Named(Config.STORAGE_DIR) File storageDir, @Named(Config.DUMP_STATISTICS) boolean dumpStatistics) { this.p2PService = p2PService; this.priceFeedService = priceFeedService; - this.tradeStatistics2StorageService = tradeStatistics2StorageService; + this.tradeStatistics3StorageService = tradeStatistics3StorageService; + this.storageDir = storageDir; this.dumpStatistics = dumpStatistics; - jsonFileManager = new JsonFileManager(storageDir); - appendOnlyDataStoreService.addService(tradeStatistics2StorageService); + + appendOnlyDataStoreService.addService(tradeStatistics3StorageService); } public void onAllServicesInitialized() { p2PService.getP2PDataStorage().addAppendOnlyDataStoreListener(payload -> { - if (payload instanceof TradeStatistics2) - addToSet((TradeStatistics2) payload); + if (payload instanceof TradeStatistics3) { + TradeStatistics3 tradeStatistics = (TradeStatistics3) payload; + if (!tradeStatistics.isValid()) { + return; + } + observableTradeStatisticsSet.add(tradeStatistics); + priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet); + maybeDump(); + } }); - Set set = tradeStatistics2StorageService.getMapOfAllData().values().stream() - .filter(e -> e instanceof TradeStatistics2) - .map(e -> (TradeStatistics2) e) - .map(WrapperTradeStatistics2::new) - .distinct() - .map(WrapperTradeStatistics2::unwrap) - .filter(TradeStatistics2::isValid) + Set set = tradeStatistics3StorageService.getMapOfAllData().values().stream() + .filter(e -> e instanceof TradeStatistics3) + .map(e -> (TradeStatistics3) e) + .filter(TradeStatistics3::isValid) .collect(Collectors.toSet()); observableTradeStatisticsSet.addAll(set); - priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet); - - dump(); + maybeDump(); } - public ObservableSet getObservableTradeStatisticsSet() { + public ObservableSet getObservableTradeStatisticsSet() { return observableTradeStatisticsSet; } - private void addToSet(TradeStatistics2 tradeStatistics) { - if (!observableTradeStatisticsSet.contains(tradeStatistics)) { - Optional duplicate = observableTradeStatisticsSet.stream().filter( - e -> e.getOfferId().equals(tradeStatistics.getOfferId())).findAny(); - - if (duplicate.isPresent()) { - // TODO: Can be removed as soon as everyone uses v1.2.6+ - // Removes an existing object with a trade id if the new one matches the existing except - // for the deposit tx id - if (tradeStatistics.getDepositTxId() == null && - tradeStatistics.isValid() && - duplicate.get().compareTo(tradeStatistics) == 0) { - observableTradeStatisticsSet.remove(duplicate.get()); - } else { - return; - } - } - - if (!tradeStatistics.isValid()) { - return; - } - - observableTradeStatisticsSet.add(tradeStatistics); - priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet); - dump(); + private void maybeDump() { + if (!dumpStatistics) { + return; } - } - private void dump() { - if (dumpStatistics) { + if (jsonFileManager == null) { + jsonFileManager = new JsonFileManager(storageDir); + + // We only dump once the currencies as they do not change during runtime ArrayList fiatCurrencyList = CurrencyUtil.getAllSortedFiatCurrencies().stream() .map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8)) .collect(Collectors.toCollection(ArrayList::new)); @@ -138,44 +120,15 @@ private void dump() { .collect(Collectors.toCollection(ArrayList::new)); cryptoCurrencyList.add(0, new CurrencyTuple(Res.getBaseCurrencyCode(), Res.getBaseCurrencyName(), 8)); jsonFileManager.writeToDisc(Utilities.objectToJson(cryptoCurrencyList), "crypto_currency_list"); - - // We store the statistics as json so it is easy for further processing (e.g. for web based services) - // TODO This is just a quick solution for storing to one file. - // 1 statistic entry has 500 bytes as json. - // Need a more scalable solution later when we get more volume. - // The flag will only be activated by dedicated nodes, so it should not be too critical for the moment, but needs to - // get improved. Maybe a LevelDB like DB...? Could be impl. in a headless version only. - List list = observableTradeStatisticsSet.stream().map(TradeStatisticsForJson::new) - .sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate))) - .collect(Collectors.toList()); - TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()]; - list.toArray(array); - jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics"); - } - } - - static class WrapperTradeStatistics2 { - private final TradeStatistics2 tradeStatistics; - - public WrapperTradeStatistics2(TradeStatistics2 tradeStatistics) { - this.tradeStatistics = tradeStatistics; } - public TradeStatistics2 unwrap() { - return this.tradeStatistics; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - var wrapper = (WrapperTradeStatistics2) obj; - return Objects.equals(tradeStatistics.getOfferId(), wrapper.tradeStatistics.getOfferId()); - } - - @Override - public int hashCode() { - return Objects.hash(tradeStatistics.getOfferId()); - } + List list = observableTradeStatisticsSet.stream() + .map(TradeStatisticsForJson::new) + .sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate))) + .collect(Collectors.toList()); + TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()]; + list.toArray(array); + jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics"); } + } From 52be1266673675b6dbc5a2fbce96876deb989fc0 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 18:20:56 -0500 Subject: [PATCH 076/123] Apply TradeStatistics3 to client classes --- core/src/main/java/bisq/core/api/CoreApi.java | 4 +- .../dao/governance/asset/AssetService.java | 6 +- .../availability/DisputeAgentSelection.java | 17 ++-- .../grpc/GrpcGetTradeStatisticsService.java | 4 +- .../economy/dashboard/BsqDashboardView.java | 32 +++---- .../bisq/desktop/main/market/MarketView.java | 32 ++++--- .../main/market/trades/TradesChartsView.java | 96 +++++++------------ .../market/trades/TradesChartsViewModel.java | 33 +++---- .../main/offer/MutableOfferDataModel.java | 6 +- .../bisq/monitor/metric/P2PMarketStats.java | 6 +- .../monitor/metric/P2PSeedNodeSnapshot.java | 6 +- 11 files changed, 106 insertions(+), 136 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index e944265a4b7..be694bc7130 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -22,7 +22,7 @@ import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; import bisq.core.payment.PaymentAccount; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.common.app.Version; @@ -196,7 +196,7 @@ public void removeWalletPassword(String password) { walletsService.removeWalletPassword(password); } - public List getTradeStatistics() { + public List getTradeStatistics() { return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); } diff --git a/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java b/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java index f66614ec855..cc902185057 100644 --- a/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java +++ b/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java @@ -154,10 +154,10 @@ public void updateAssetStates() { // TradeAmountDateTuple object holding only the data we need. Map> lookupMap = new HashMap<>(); tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> CurrencyUtil.isCryptoCurrency(e.getBaseCurrency())) + .filter(e -> CurrencyUtil.isCryptoCurrency(e.getCurrency())) .forEach(e -> { - lookupMap.putIfAbsent(e.getBaseCurrency(), new ArrayList<>()); - lookupMap.get(e.getBaseCurrency()).add(new TradeAmountDateTuple(e.getTradeAmount().getValue(), e.getTradeDate().getTime())); + lookupMap.putIfAbsent(e.getCurrency(), new ArrayList<>()); + lookupMap.get(e.getCurrency()).add(new TradeAmountDateTuple(e.getAmount(), e.getDate())); }); getStatefulAssets().stream() diff --git a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java index 478038ad9ab..55996437f24 100644 --- a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java +++ b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java @@ -19,7 +19,7 @@ import bisq.core.support.dispute.agent.DisputeAgent; import bisq.core.support.dispute.agent.DisputeAgentManager; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.common.util.Tuple2; @@ -30,7 +30,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -46,22 +45,22 @@ public static T getLeastUsedMediator(TradeStatisticsMan DisputeAgentManager disputeAgentManager) { return getLeastUsedDisputeAgent(tradeStatisticsManager, disputeAgentManager, - TradeStatistics2.MEDIATOR_ADDRESS); + true); } public static T getLeastUsedRefundAgent(TradeStatisticsManager tradeStatisticsManager, DisputeAgentManager disputeAgentManager) { return getLeastUsedDisputeAgent(tradeStatisticsManager, disputeAgentManager, - TradeStatistics2.REFUND_AGENT_ADDRESS); + false); } private static T getLeastUsedDisputeAgent(TradeStatisticsManager tradeStatisticsManager, DisputeAgentManager disputeAgentManager, - String extraMapKey) { + boolean isMediator) { // We take last 100 entries from trade statistics - List list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); - list.sort(Comparator.comparing(TradeStatistics2::getTradeDate)); + List list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); + list.sort(Comparator.comparing(TradeStatistics3::getDate)); Collections.reverse(list); if (!list.isEmpty()) { int max = Math.min(list.size(), 100); @@ -70,9 +69,7 @@ private static T getLeastUsedDisputeAgent(TradeStatisti // We stored only first 4 chars of disputeAgents onion address List lastAddressesUsedInTrades = list.stream() - .filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap() != null) - .map(tradeStatistics2 -> tradeStatistics2.getExtraDataMap().get(extraMapKey)) - .filter(Objects::nonNull) + .map(tradeStatistics3 -> isMediator ? tradeStatistics3.getMediator() : tradeStatistics3.getRefundAgent()) .collect(Collectors.toList()); Set disputeAgents = disputeAgentManager.getObservableMap().values().stream() diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcGetTradeStatisticsService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcGetTradeStatisticsService.java index 281fc121185..4c98e939af3 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcGetTradeStatisticsService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcGetTradeStatisticsService.java @@ -1,7 +1,7 @@ package bisq.daemon.grpc; import bisq.core.api.CoreApi; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.proto.grpc.GetTradeStatisticsGrpc; import bisq.proto.grpc.GetTradeStatisticsReply; @@ -27,7 +27,7 @@ public void getTradeStatistics(GetTradeStatisticsRequest req, StreamObserver responseObserver) { var tradeStatistics = coreApi.getTradeStatistics().stream() - .map(TradeStatistics2::toProtoTradeStatistics2) + .map(TradeStatistics3::toProtoTradeStatistics3) .collect(Collectors.toList()); var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build(); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java index 24ef6601465..a96dfd58719 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java @@ -30,7 +30,7 @@ import bisq.core.monetary.Altcoin; import bisq.core.monetary.Price; import bisq.core.provider.price.PriceFeedService; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; import bisq.core.util.FormattingUtils; @@ -289,17 +289,17 @@ private void updateChartData() { private void updateBsqPriceData() { seriesBSQPrice.getData().clear(); - Map> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> e.getCurrencyCode().equals("BSQ")) - .sorted(Comparator.comparing(TradeStatistics2::getTradeDate)) - .collect(Collectors.groupingBy(item -> new java.sql.Date(item.getTradeDate().getTime()).toLocalDate() + Map> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() + .filter(e -> e.getCurrency().equals("BSQ")) + .sorted(Comparator.comparing(TradeStatistics3::getDate)) + .collect(Collectors.groupingBy(item -> new java.sql.Date(item.getDate()).toLocalDate() .with(ADJUSTERS.get(DAY)))); List> updatedBSQPrice = bsqPriceByDate.keySet().stream() .map(e -> { ZonedDateTime zonedDateTime = e.atStartOfDay(ZoneId.systemDefault()); return new XYChart.Data(zonedDateTime.toInstant().getEpochSecond(), bsqPriceByDate.get(e).stream() - .map(TradeStatistics2::getTradePrice) + .map(TradeStatistics3::getTradePrice) .mapToDouble(Price::getValue) .average() .orElse(Double.NaN) @@ -370,12 +370,12 @@ private void updateAveragePriceFields(TextField field90, TextFieldWithIcon field private long updateAveragePriceField(TextField textField, int days, boolean isUSDField) { Date pastXDays = getPastDate(days); - List bsqTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> e.getCurrencyCode().equals("BSQ")) + List bsqTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() + .filter(e -> e.getCurrency().equals("BSQ")) .filter(e -> e.getTradeDate().after(pastXDays)) .collect(Collectors.toList()); - List usdTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> e.getCurrencyCode().equals("USD")) + List usdTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() + .filter(e -> e.getCurrency().equals("USD")) .filter(e -> e.getTradeDate().after(pastXDays)) .collect(Collectors.toList()); long average = isUSDField ? getUSDAverage(bsqTradePastXDays, usdTradePastXDays) : @@ -391,11 +391,11 @@ private long updateAveragePriceField(TextField textField, int days, boolean isUS return average; } - private long getBTCAverage(List bsqList) { + private long getBTCAverage(List bsqList) { long accumulatedVolume = 0; long accumulatedAmount = 0; - for (TradeStatistics2 item : bsqList) { + for (TradeStatistics3 item : bsqList) { accumulatedVolume += item.getTradeVolume().getValue(); accumulatedAmount += item.getTradeAmount().getValue(); // Amount of BTC traded } @@ -406,17 +406,17 @@ private long getBTCAverage(List bsqList) { return averagePrice; } - private long getUSDAverage(List bsqList, List usdList) { + private long getUSDAverage(List bsqList, List usdList) { // Use next USD/BTC print as price to calculate BSQ/USD rate // Store each trade as amount of USD and amount of BSQ traded List> usdBsqList = new ArrayList<>(bsqList.size()); - usdList.sort(Comparator.comparing(o -> o.getTradeDate().getTime())); + usdList.sort(Comparator.comparing(TradeStatistics3::getDate)); var usdBTCPrice = 10000d; // Default to 10000 USD per BTC if there is no USD feed at all - for (TradeStatistics2 item : bsqList) { + for (TradeStatistics3 item : bsqList) { // Find usdprice for trade item usdBTCPrice = usdList.stream() - .filter(usd -> usd.getTradeDate().getTime() > item.getTradeDate().getTime()) + .filter(usd -> usd.getDate() > item.getDate()) .map(usd -> MathUtils.scaleDownByPowerOf10((double) usd.getTradePrice().getValue(), Fiat.SMALLEST_UNIT_EXPONENT)) .findFirst() diff --git a/desktop/src/main/java/bisq/desktop/main/market/MarketView.java b/desktop/src/main/java/bisq/desktop/main/market/MarketView.java index 15641ac3dc1..9a3544e06d0 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/MarketView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/MarketView.java @@ -35,7 +35,7 @@ import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.offer.OfferPayload; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.util.FormattingUtils; import bisq.core.util.coin.CoinFormatter; @@ -82,7 +82,10 @@ public class MarketView extends ActivatableView { @Inject - public MarketView(CachingViewLoader viewLoader, P2PService p2PService, OfferBook offerBook, @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter, + public MarketView(CachingViewLoader viewLoader, + P2PService p2PService, + OfferBook offerBook, + @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter, Navigation navigation) { this.viewLoader = viewLoader; this.p2PService = p2PService; @@ -179,20 +182,19 @@ private String getAllTradesWithReferralId() { // If both traders had set it the tradeStatistics is only delivered once. // If both traders used a different referral ID then we would get 2 objects. List list = p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().stream() - .filter(e -> e instanceof TradeStatistics2) - .map(e -> (TradeStatistics2) e) - .filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap() != null) - .filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap().get(OfferPayload.REFERRAL_ID) != null) - .map(trade -> { + .filter(e -> e instanceof TradeStatistics3) + .map(e -> (TradeStatistics3) e) + .filter(tradeStatistics3 -> tradeStatistics3.getExtraDataMap() != null) + .filter(tradeStatistics3 -> tradeStatistics3.getExtraDataMap().get(OfferPayload.REFERRAL_ID) != null) + .map(tradeStatistics3 -> { StringBuilder sb = new StringBuilder(); - sb.append("Trade ID: ").append(trade.getOfferId()).append("\n") - .append("Date: ").append(DisplayUtils.formatDateTime(trade.getTradeDate())).append("\n") - .append("Market: ").append(CurrencyUtil.getCurrencyPair(trade.getCurrencyCode())).append("\n") - .append("Price: ").append(FormattingUtils.formatPrice(trade.getTradePrice())).append("\n") - .append("Amount: ").append(formatter.formatCoin(trade.getTradeAmount())).append("\n") - .append("Volume: ").append(DisplayUtils.formatVolume(trade.getTradeVolume())).append("\n") - .append("Payment method: ").append(Res.get(trade.getOfferPaymentMethod())).append("\n") - .append("ReferralID: ").append(trade.getExtraDataMap().get(OfferPayload.REFERRAL_ID)); + sb.append("Date: ").append(DisplayUtils.formatDateTime(tradeStatistics3.getTradeDate())).append("\n") + .append("Market: ").append(CurrencyUtil.getCurrencyPair(tradeStatistics3.getCurrency())).append("\n") + .append("Price: ").append(FormattingUtils.formatPrice(tradeStatistics3.getTradePrice())).append("\n") + .append("Amount: ").append(formatter.formatCoin(tradeStatistics3.getTradeAmount())).append("\n") + .append("Volume: ").append(DisplayUtils.formatVolume(tradeStatistics3.getTradeVolume())).append("\n") + .append("Payment method: ").append(Res.get(tradeStatistics3.getPaymentMethod())).append("\n") + .append("ReferralID: ").append(tradeStatistics3.getExtraDataMap().get(OfferPayload.REFERRAL_ID)); return sb.toString(); }) .collect(Collectors.toList()); diff --git a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java index f26d3c1ea05..4a70b96ff43 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java @@ -34,8 +34,7 @@ import bisq.core.locale.Res; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; -import bisq.core.offer.OfferPayload; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.util.FormattingUtils; import bisq.core.util.coin.CoinFormatter; @@ -103,7 +102,7 @@ public class TradesChartsView extends ActivatableViewAndModel tableView; + private TableView tableView; private AutocompleteComboBox currencyComboBox; private VolumeChart volumeChart; private CandleStickChart priceChart; @@ -118,12 +117,12 @@ public class TradesChartsView extends ActivatableViewAndModel timeUnitChangeListener; private ToggleGroup toggleGroup; private final ListChangeListener> itemsChangeListener; - private SortedList sortedList; + private SortedList sortedList; private Label nrOfTradeStatisticsLabel; - private ListChangeListener tradeStatisticsByCurrencyListener; + private ListChangeListener tradeStatisticsByCurrencyListener; private ChangeListener selectedTabIndexListener; private SingleSelectionModel tabPaneSelectionModel; - private TableColumn priceColumn, volumeColumn, marketColumn; + private TableColumn priceColumn, volumeColumn, marketColumn; @SuppressWarnings("FieldCanBeLocal") private MonadicBinding currencySelectionBinding; private Subscription currencySelectionSubscriber; @@ -550,7 +549,7 @@ private void createTable() { VBox.setVgrow(tableView, Priority.ALWAYS); // date - TableColumn dateColumn = new AutoTooltipTableColumn<>(Res.get("shared.dateTime")) { + TableColumn dateColumn = new AutoTooltipTableColumn<>(Res.get("shared.dateTime")) { { setMinWidth(240); setMaxWidth(240); @@ -561,11 +560,11 @@ private void createTable() { dateColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn column) { return new TableCell<>() { @Override - public void updateItem(final TradeStatistics2 item, boolean empty) { + public void updateItem(final TradeStatistics3 item, boolean empty) { super.updateItem(item, empty); if (item != null) setText(DisplayUtils.formatDateTime(item.getTradeDate())); @@ -575,7 +574,7 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { }; } }); - dateColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradeDate)); + dateColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeDate)); tableView.getColumns().add(dateColumn); // market @@ -590,21 +589,21 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { marketColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn column) { return new TableCell<>() { @Override - public void updateItem(final TradeStatistics2 item, boolean empty) { + public void updateItem(final TradeStatistics3 item, boolean empty) { super.updateItem(item, empty); if (item != null) - setText(CurrencyUtil.getCurrencyPair(item.getCurrencyCode())); + setText(CurrencyUtil.getCurrencyPair(item.getCurrency())); else setText(""); } }; } }); - marketColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradeDate)); + marketColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeDate)); tableView.getColumns().add(marketColumn); // price @@ -614,11 +613,11 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { priceColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn column) { return new TableCell<>() { @Override - public void updateItem(final TradeStatistics2 item, boolean empty) { + public void updateItem(final TradeStatistics3 item, boolean empty) { super.updateItem(item, empty); if (item != null) setText(FormattingUtils.formatPrice(item.getTradePrice())); @@ -628,21 +627,21 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { }; } }); - priceColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradePrice)); + priceColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradePrice)); tableView.getColumns().add(priceColumn); // amount - TableColumn amountColumn = new AutoTooltipTableColumn<>(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())); + TableColumn amountColumn = new AutoTooltipTableColumn<>(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())); amountColumn.getStyleClass().add("number-column"); amountColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue())); amountColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn column) { return new TableCell<>() { @Override - public void updateItem(final TradeStatistics2 item, boolean empty) { + public void updateItem(final TradeStatistics3 item, boolean empty) { super.updateItem(item, empty); if (item != null) setGraphic(new ColoredDecimalPlacesWithZerosText(formatter.formatCoin(item.getTradeAmount(), @@ -653,7 +652,7 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { }; } }); - amountColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradeAmount)); + amountColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeAmount)); tableView.getColumns().add(amountColumn); // volume @@ -663,11 +662,11 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { volumeColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn column) { return new TableCell<>() { @Override - public void updateItem(final TradeStatistics2 item, boolean empty) { + public void updateItem(final TradeStatistics3 item, boolean empty) { super.updateItem(item, empty); if (item != null) setText(model.showAllTradeCurrenciesProperty.get() ? @@ -687,17 +686,17 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { tableView.getColumns().add(volumeColumn); // paymentMethod - TableColumn paymentMethodColumn = new AutoTooltipTableColumn<>(Res.get("shared.paymentMethod")); + TableColumn paymentMethodColumn = new AutoTooltipTableColumn<>(Res.get("shared.paymentMethod")); paymentMethodColumn.getStyleClass().add("number-column"); paymentMethodColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue())); paymentMethodColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn column) { return new TableCell<>() { @Override - public void updateItem(final TradeStatistics2 item, boolean empty) { + public void updateItem(final TradeStatistics3 item, boolean empty) { super.updateItem(item, empty); if (item != null) setText(getPaymentMethodLabel(item)); @@ -710,30 +709,6 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { paymentMethodColumn.setComparator(Comparator.comparing(this::getPaymentMethodLabel)); tableView.getColumns().add(paymentMethodColumn); - // direction - TableColumn directionColumn = new AutoTooltipTableColumn<>(Res.get("shared.offerType")); - directionColumn.getStyleClass().addAll("number-column", "last-column"); - directionColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue())); - directionColumn.setCellFactory( - new Callback<>() { - @Override - public TableCell call( - TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final TradeStatistics2 item, boolean empty) { - super.updateItem(item, empty); - if (item != null) - setText(getDirectionLabel(item)); - else - setText(""); - } - }; - } - }); - directionColumn.setComparator(Comparator.comparing(this::getDirectionLabel)); - tableView.getColumns().add(directionColumn); - tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); Label placeholder = new AutoTooltipLabel(Res.get("table.placeholder.noData")); placeholder.setWrapText(true); @@ -743,13 +718,8 @@ public void updateItem(final TradeStatistics2 item, boolean empty) { } @NotNull - private String getDirectionLabel(TradeStatistics2 item) { - return DisplayUtils.getDirectionWithCode(OfferPayload.Direction.valueOf(item.getDirection().name()), item.getCurrencyCode()); - } - - @NotNull - private String getPaymentMethodLabel(TradeStatistics2 item) { - return Res.get(item.getOfferPaymentMethod()); + private String getPaymentMethodLabel(TradeStatistics3 item) { + return Res.get(item.getPaymentMethod()); } private void layout() { diff --git a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java index f070b5946b3..3bf125ad897 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java @@ -34,7 +34,7 @@ import bisq.core.locale.TradeCurrency; import bisq.core.monetary.Altcoin; import bisq.core.provider.price.PriceFeedService; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; @@ -97,18 +97,18 @@ public enum TickUnit { private final TradeStatisticsManager tradeStatisticsManager; final Preferences preferences; - private PriceFeedService priceFeedService; - private Navigation navigation; + private final PriceFeedService priceFeedService; + private final Navigation navigation; - private final SetChangeListener setChangeListener; + private final SetChangeListener setChangeListener; final ObjectProperty selectedTradeCurrencyProperty = new SimpleObjectProperty<>(); final BooleanProperty showAllTradeCurrenciesProperty = new SimpleBooleanProperty(false); private final CurrencyList currencyListItems; private final CurrencyListItem showAllCurrencyListItem = new CurrencyListItem(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, ""), -1); - final ObservableList tradeStatisticsByCurrency = FXCollections.observableArrayList(); + final ObservableList tradeStatisticsByCurrency = FXCollections.observableArrayList(); final ObservableList> priceItems = FXCollections.observableArrayList(); final ObservableList> volumeItems = FXCollections.observableArrayList(); - private Map>> itemsPerInterval; + private Map>> itemsPerInterval; TickUnit tickUnit; final int maxTicks = 90; @@ -119,7 +119,8 @@ public enum TickUnit { /////////////////////////////////////////////////////////////////////////////////////////// @Inject - TradesChartsViewModel(TradeStatisticsManager tradeStatisticsManager, Preferences preferences, PriceFeedService priceFeedService, Navigation navigation) { + TradesChartsViewModel(TradeStatisticsManager tradeStatisticsManager, Preferences preferences, + PriceFeedService priceFeedService, Navigation navigation) { this.tradeStatisticsManager = tradeStatisticsManager; this.preferences = preferences; this.priceFeedService = priceFeedService; @@ -145,7 +146,7 @@ public enum TickUnit { private void fillTradeCurrencies() { // Don't use a set as we need all entries List tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrencyCode()).stream()) + .flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrency()).stream()) .collect(Collectors.toList()); currencyListItems.updateWithCurrencies(tradeCurrencyList, showAllCurrencyListItem); @@ -241,15 +242,15 @@ private void syncPriceFeedCurrency() { private void updateChartData() { tradeStatisticsByCurrency.setAll(tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> showAllTradeCurrenciesProperty.get() || e.getCurrencyCode().equals(getCurrencyCode())) + .filter(e -> showAllTradeCurrenciesProperty.get() || e.getCurrency().equals(getCurrencyCode())) .collect(Collectors.toList())); // Generate date range and create sets for all ticks itemsPerInterval = new HashMap<>(); Date time = new Date(); for (long i = maxTicks + 1; i >= 0; --i) { - Set set = new HashSet<>(); - Pair> pair = new Pair<>((Date) time.clone(), set); + Set set = new HashSet<>(); + Pair> pair = new Pair<>((Date) time.clone(), set); itemsPerInterval.put(i, pair); time.setTime(time.getTime() - 1); time = roundToTick(time, tickUnit); @@ -258,7 +259,7 @@ private void updateChartData() { // Get all entries for the defined time interval tradeStatisticsByCurrency.forEach(e -> { for (long i = maxTicks; i > 0; --i) { - Pair> p = itemsPerInterval.get(i); + Pair> p = itemsPerInterval.get(i); if (e.getTradeDate().after(p.getKey())) { p.getValue().add(e); break; @@ -283,7 +284,7 @@ private void updateChartData() { } @VisibleForTesting - CandleData getCandleData(long tick, Set set) { + CandleData getCandleData(long tick, Set set) { long open = 0; long close = 0; long high = 0; @@ -293,7 +294,7 @@ CandleData getCandleData(long tick, Set set) { long numTrades = set.size(); List tradePrices = new ArrayList<>(set.size()); - for (TradeStatistics2 item : set) { + for (TradeStatistics3 item : set) { long tradePriceAsLong = item.getTradePrice().getValue(); // Previously a check was done which inverted the low and high for cryptocurrencies. low = (low != 0) ? Math.min(low, tradePriceAsLong) : tradePriceAsLong; @@ -305,8 +306,8 @@ CandleData getCandleData(long tick, Set set) { } Collections.sort(tradePrices); - List list = new ArrayList<>(set); - list.sort(Comparator.comparingLong(o -> o.getTradeDate().getTime())); + List list = new ArrayList<>(set); + list.sort(Comparator.comparingLong(TradeStatistics3::getDate)); if (list.size() > 0) { open = list.get(0).getTradePrice().getValue(); close = list.get(list.size() - 1).getTradePrice().getValue(); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index 44371697167..62869cd54e7 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -43,7 +43,7 @@ import bisq.core.provider.fee.FeeService; import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.handlers.TransactionResultHandler; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -344,9 +344,9 @@ private void setSuggestedSecurityDeposit(PaymentAccount paymentAccount) { var blocksRange = Restrictions.getLockTime(paymentAccount.getPaymentMethod().isAsset()); var startDate = new Date(System.currentTimeMillis() - blocksRange * 10 * 60000); var sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> e.getCurrencyCode().equals(getTradeCurrency().getCode())) + .filter(e -> e.getCurrency().equals(getTradeCurrency().getCode())) .filter(e -> e.getTradeDate().compareTo(startDate) >= 0) - .sorted(Comparator.comparing(TradeStatistics2::getTradeDate)) + .sorted(Comparator.comparing(TradeStatistics3::getTradeDate)) .collect(Collectors.toList()); var movingAverage = new MathUtils.MovingAverage(10, 0.2); double[] extremes = {Double.MAX_VALUE, Double.MIN_VALUE}; diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PMarketStats.java b/monitor/src/main/java/bisq/monitor/metric/P2PMarketStats.java index 189c739b3b0..0f7e8afb83c 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PMarketStats.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PMarketStats.java @@ -23,7 +23,7 @@ import bisq.core.account.witness.AccountAgeWitnessStore; import bisq.core.offer.OfferPayload; import bisq.core.proto.persistable.CorePersistenceProtoResolver; -import bisq.core.trade.statistics.TradeStatistics2Store; +import bisq.core.trade.statistics.TradeStatistics3Store; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.Connection; @@ -163,8 +163,8 @@ public void configure(Properties properties) { String networkPostfix = "_" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].toString(); try { PersistenceManager persistenceManager = new PersistenceManager<>(dir, new CorePersistenceProtoResolver(null, null), null); - TradeStatistics2Store tradeStatistics2Store = (TradeStatistics2Store) persistenceManager.getPersisted(TradeStatistics2Store.class.getSimpleName() + networkPostfix); - hashes.addAll(tradeStatistics2Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList())); + TradeStatistics3Store tradeStatistics3Store = (TradeStatistics3Store) persistenceManager.getPersisted(TradeStatistics3Store.class.getSimpleName() + networkPostfix); + hashes.addAll(tradeStatistics3Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList())); AccountAgeWitnessStore accountAgeWitnessStore = (AccountAgeWitnessStore) persistenceManager.getPersisted(AccountAgeWitnessStore.class.getSimpleName() + networkPostfix); hashes.addAll(accountAgeWitnessStore.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList())); diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java index e87d7468253..6786cb65368 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java @@ -27,7 +27,7 @@ import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest; import bisq.core.dao.monitoring.network.messages.GetStateHashesResponse; import bisq.core.proto.persistable.CorePersistenceProtoResolver; -import bisq.core.trade.statistics.TradeStatistics2Store; +import bisq.core.trade.statistics.TradeStatistics3Store; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.Connection; @@ -137,8 +137,8 @@ public void configure(Properties properties) { String networkPostfix = "_" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].toString(); try { PersistenceManager persistenceManager = new PersistenceManager<>(dir, new CorePersistenceProtoResolver(null, null), null); - TradeStatistics2Store tradeStatistics2Store = (TradeStatistics2Store) persistenceManager.getPersisted(TradeStatistics2Store.class.getSimpleName() + networkPostfix); - hashes.addAll(tradeStatistics2Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList())); + TradeStatistics3Store tradeStatistics3Store = (TradeStatistics3Store) persistenceManager.getPersisted(TradeStatistics3Store.class.getSimpleName() + networkPostfix); + hashes.addAll(tradeStatistics3Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList())); AccountAgeWitnessStore accountAgeWitnessStore = (AccountAgeWitnessStore) persistenceManager.getPersisted(AccountAgeWitnessStore.class.getSimpleName() + networkPostfix); hashes.addAll(accountAgeWitnessStore.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList())); From 0e70a99c42a002f6e38a77621e8e18168176196c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 18:27:54 -0500 Subject: [PATCH 077/123] Adjust tests, remove tests which do not make sense anymore --- .../trade/statistics/TradeStatistics3.java | 20 +-- .../statistics/TradeStatistics2Maker.java | 91 -------------- .../statistics/TradeStatistics2Test.java | 45 ------- .../TradeStatisticsManagerTest.java | 114 ------------------ .../trades/TradesChartsViewModelTest.java | 62 ++++++++-- 5 files changed, 63 insertions(+), 269 deletions(-) delete mode 100644 core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Maker.java delete mode 100644 core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Test.java delete mode 100644 core/src/test/java/bisq/core/trade/statistics/TradeStatisticsManagerTest.java diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java index 94d267a7a77..12a43003ad1 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java @@ -42,6 +42,7 @@ import org.bitcoinj.utils.ExchangeRate; import org.bitcoinj.utils.Fiat; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import java.util.Date; @@ -173,15 +174,16 @@ public TradeStatistics3(String currency, // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// - private TradeStatistics3(String currency, - long price, - long amount, - String paymentMethod, - long date, - @Nullable String mediator, - @Nullable String refundAgent, - @Nullable Map extraDataMap, - @Nullable byte[] hash) { + @VisibleForTesting + public TradeStatistics3(String currency, + long price, + long amount, + String paymentMethod, + long date, + @Nullable String mediator, + @Nullable String refundAgent, + @Nullable Map extraDataMap, + @Nullable byte[] hash) { this.currency = currency; this.price = price; this.amount = amount; diff --git a/core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Maker.java b/core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Maker.java deleted file mode 100644 index 3c49ce2d5a6..00000000000 --- a/core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Maker.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.trade.statistics; - -import bisq.core.monetary.Price; -import bisq.core.offer.OfferPayload; - -import org.bitcoinj.core.Coin; - -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; - -import com.natpryce.makeiteasy.Instantiator; -import com.natpryce.makeiteasy.Maker; -import com.natpryce.makeiteasy.Property; - -import static com.natpryce.makeiteasy.MakeItEasy.a; - -public class TradeStatistics2Maker { - - public static final Property date = new Property<>(); - public static final Property depositTxId = new Property<>(); - public static final Property tradeAmount = new Property<>(); - - public static final Instantiator TradeStatistic2 = lookup -> { - Calendar calendar = Calendar.getInstance(); - calendar.set(2016, 3, 19); - - return new TradeStatistics2( - new OfferPayload("1234", - 0L, - null, - null, - OfferPayload.Direction.BUY, - 100000L, - 0.0, - false, - 100000L, - 100000L, - "BTC", - "USD", - null, - null, - "SEPA", - "", - null, - null, - null, - null, - null, - "", - 0L, - 0L, - 0L, - false, - 0L, - 0L, - 0L, - 0L, - false, - false, - 0L, - 0L, - false, - null, - null, - 0), - Price.valueOf("BTC", 100000L), - lookup.valueOf(tradeAmount, Coin.SATOSHI), - lookup.valueOf(date, new Date(calendar.getTimeInMillis())), - lookup.valueOf(depositTxId, "123456"), - Collections.emptyMap()); - }; - public static final Maker dayZeroTrade = a(TradeStatistic2); -} diff --git a/core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Test.java b/core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Test.java deleted file mode 100644 index 725d18d5c7c..00000000000 --- a/core/src/test/java/bisq/core/trade/statistics/TradeStatistics2Test.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.trade.statistics; - -import org.junit.Test; - -import static bisq.core.trade.statistics.TradeStatistics2Maker.dayZeroTrade; -import static bisq.core.trade.statistics.TradeStatistics2Maker.depositTxId; -import static com.natpryce.makeiteasy.MakeItEasy.make; -import static com.natpryce.makeiteasy.MakeItEasy.withNull; -import static org.junit.Assert.assertTrue; - - -public class TradeStatistics2Test { - - @Test - public void isValid_WithDepositTxId() { - - TradeStatistics2 tradeStatistic = make(dayZeroTrade); - - assertTrue(tradeStatistic.isValid()); - } - - @Test - public void isValid_WithEmptyDepositTxId() { - TradeStatistics2 tradeStatistic = make(dayZeroTrade.but(withNull(depositTxId))); - - assertTrue(tradeStatistic.isValid()); - } -} diff --git a/core/src/test/java/bisq/core/trade/statistics/TradeStatisticsManagerTest.java b/core/src/test/java/bisq/core/trade/statistics/TradeStatisticsManagerTest.java deleted file mode 100644 index 44b84d13848..00000000000 --- a/core/src/test/java/bisq/core/trade/statistics/TradeStatisticsManagerTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.trade.statistics; - -import bisq.core.provider.price.PriceFeedService; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.storage.P2PDataStorage; -import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreListener; -import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; - -import org.bitcoinj.core.Coin; - -import java.io.File; - -import org.mockito.ArgumentCaptor; - -import org.junit.Before; -import org.junit.Test; - -import static bisq.core.trade.statistics.TradeStatistics2Maker.dayZeroTrade; -import static bisq.core.trade.statistics.TradeStatistics2Maker.depositTxId; -import static bisq.core.trade.statistics.TradeStatistics2Maker.tradeAmount; -import static com.natpryce.makeiteasy.MakeItEasy.make; -import static com.natpryce.makeiteasy.MakeItEasy.with; -import static com.natpryce.makeiteasy.MakeItEasy.withNull; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class TradeStatisticsManagerTest { - - private TradeStatisticsManager manager; - private TradeStatistics2 tradeWithNullDepositTxId; - private ArgumentCaptor listenerArgumentCaptor; - - @Before - public void prepareMocksAndObjects() { - P2PService p2PService = mock(P2PService.class); - P2PDataStorage p2PDataStorage = mock(P2PDataStorage.class); - File storageDir = mock(File.class); - TradeStatistics2StorageService tradeStatistics2StorageService = mock(TradeStatistics2StorageService.class); - PriceFeedService priceFeedService = mock(PriceFeedService.class); - - AppendOnlyDataStoreService appendOnlyDataStoreService = mock(AppendOnlyDataStoreService.class); - when(p2PService.getP2PDataStorage()).thenReturn(p2PDataStorage); - - manager = new TradeStatisticsManager(p2PService, priceFeedService, - tradeStatistics2StorageService, appendOnlyDataStoreService, storageDir, false); - - tradeWithNullDepositTxId = make(dayZeroTrade.but(withNull(depositTxId))); - - manager.onAllServicesInitialized(); - listenerArgumentCaptor = ArgumentCaptor.forClass(AppendOnlyDataStoreListener.class); - verify(p2PDataStorage).addAppendOnlyDataStoreListener(listenerArgumentCaptor.capture()); - - } - - @Test - public void addToSet_ObjectWithNullDepositTxId() { - listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId); - assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId)); - } - - @Test - public void addToSet_RemoveExistingObjectIfObjectWithNullDepositTxIdIsAdded() { - TradeStatistics2 tradeWithDepositTxId = make(dayZeroTrade); - - listenerArgumentCaptor.getValue().onAdded(tradeWithDepositTxId); - listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId); - - assertFalse(manager.getObservableTradeStatisticsSet().contains(tradeWithDepositTxId)); - assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId)); - } - - @Test - public void addToSet_NotRemoveExistingObjectIfObjectsNotEqual() { - TradeStatistics2 tradeWithDepositTxId = make(dayZeroTrade.but(with(tradeAmount, Coin.FIFTY_COINS))); - - listenerArgumentCaptor.getValue().onAdded(tradeWithDepositTxId); - listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId); - - assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithDepositTxId)); - assertFalse(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId)); - } - - @Test - public void addToSet_IgnoreObjectIfObjectWithNullDepositTxIdAlreadyExists() { - TradeStatistics2 tradeWithDepositTxId = make(dayZeroTrade); - - listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId); - listenerArgumentCaptor.getValue().onAdded(tradeWithDepositTxId); - - assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId)); - assertFalse(manager.getObservableTradeStatisticsSet().contains(tradeWithDepositTxId)); - } -} diff --git a/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java index f3223f5572f..3e7dd9db5bf 100644 --- a/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java @@ -23,8 +23,9 @@ import bisq.core.locale.FiatCurrency; import bisq.core.monetary.Price; import bisq.core.offer.OfferPayload; +import bisq.core.payment.payload.PaymentMethod; import bisq.core.provider.price.PriceFeedService; -import bisq.core.trade.statistics.TradeStatistics2; +import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; @@ -106,6 +107,7 @@ public class TradesChartsViewModelTest { null, 1 ); + @Before public void setup() throws IOException { tradeStatisticsManager = mock(TradeStatisticsManager.class); @@ -134,13 +136,45 @@ public void testGetCandleData() { long volume = Fiat.parseFiat("EUR", "2200").value; boolean isBullish = true; - Set set = new HashSet<>(); + Set set = new HashSet<>(); final Date now = new Date(); - set.add(new TradeStatistics2(offer, Price.parse("EUR", "520"), Coin.parseCoin("1"), new Date(now.getTime()), null, null)); - set.add(new TradeStatistics2(offer, Price.parse("EUR", "500"), Coin.parseCoin("1"), new Date(now.getTime() + 100), null, null)); - set.add(new TradeStatistics2(offer, Price.parse("EUR", "600"), Coin.parseCoin("1"), new Date(now.getTime() + 200), null, null)); - set.add(new TradeStatistics2(offer, Price.parse("EUR", "580"), Coin.parseCoin("1"), new Date(now.getTime() + 300), null, null)); + set.add(new TradeStatistics3(offer.getCurrencyCode(), + Price.parse("EUR", "520").getValue(), + Coin.parseCoin("1").getValue(), + PaymentMethod.BLOCK_CHAINS_ID, + now.getTime(), + null, + null, + null, + null)); + set.add(new TradeStatistics3(offer.getCurrencyCode(), + Price.parse("EUR", "500").getValue(), + Coin.parseCoin("1").getValue(), + PaymentMethod.BLOCK_CHAINS_ID, + now.getTime() + 100, + null, + null, + null, + null)); + set.add(new TradeStatistics3(offer.getCurrencyCode(), + Price.parse("EUR", "600").getValue(), + Coin.parseCoin("1").getValue(), + PaymentMethod.BLOCK_CHAINS_ID, + now.getTime() + 200, + null, + null, + null, + null)); + set.add(new TradeStatistics3(offer.getCurrencyCode(), + Price.parse("EUR", "580").getValue(), + Coin.parseCoin("1").getValue(), + PaymentMethod.BLOCK_CHAINS_ID, + now.getTime() + 300, + null, + null, + null, + null)); CandleData candleData = model.getCandleData(model.roundToTick(now, TradesChartsViewModel.TickUnit.DAY).getTime(), set); assertEquals(open, candleData.open); @@ -194,11 +228,19 @@ long currentTimeMillis() { // Two trades 10 seconds apart, different YEAR, MONTH, WEEK, DAY, HOUR, MINUTE_10 trades.add(new Trade("2017-12-31T23:59:52", "1", "100", "EUR")); trades.add(new Trade("2018-01-01T00:00:02", "1", "110", "EUR")); - Set set = new HashSet<>(); + Set set = new HashSet<>(); trades.forEach(t -> - set.add(new TradeStatistics2(offer, Price.parse(t.cc, t.price), Coin.parseCoin(t.size), t.date, null, null)) + set.add(new TradeStatistics3(offer.getCurrencyCode(), + Price.parse(t.cc, t.price).getValue(), + Coin.parseCoin(t.size).getValue(), + PaymentMethod.BLOCK_CHAINS_ID, + t.date.getTime(), + null, + null, + null, + null)) ); - ObservableSet tradeStats = FXCollections.observableSet(set); + ObservableSet tradeStats = FXCollections.observableSet(set); // Run test for each tick type for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) { @@ -209,7 +251,7 @@ long currentTimeMillis() { // Trigger chart update model.setTickUnit(tick); - assertEquals(model.selectedTradeCurrencyProperty.get().getCode(), tradeStats.iterator().next().getCurrencyCode()); + assertEquals(model.selectedTradeCurrencyProperty.get().getCode(), tradeStats.iterator().next().getCurrency()); assertEquals(2, model.priceItems.size()); assertEquals(2, model.volumeItems.size()); } From 6766835af61b5cff514c53cee703ee2904ebfd4f Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 18:28:37 -0500 Subject: [PATCH 078/123] Use TradeStatistics3 in protobuf file --- proto/src/main/proto/grpc.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index 49f47e156ec..2d23a43b2d3 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -154,7 +154,7 @@ message GetTradeStatisticsRequest { } message GetTradeStatisticsReply { - repeated TradeStatistics2 TradeStatistics = 1; + repeated TradeStatistics3 TradeStatistics = 1; } /////////////////////////////////////////////////////////////////////////////////////////// From b14266d815ab79b3b15fa925e715d203a0daad7c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 18:29:05 -0500 Subject: [PATCH 079/123] Remove resource file --- p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET diff --git a/p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET b/p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET deleted file mode 100644 index b43cfb50f36..00000000000 --- a/p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c6b7362593537425e1228ee9a7a234811dc23ec1f66db31be4cc896124ff20fa -size 15989339 From 9016cb6c32e355abc100b30a36fc6cd217aea02c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 19:55:15 -0500 Subject: [PATCH 080/123] Prune mediator and refund agent entries for all entries beside the last 100 we use for the selection algorithm. --- .../availability/DisputeAgentSelection.java | 3 +- .../trade/statistics/TradeStatistics3.java | 43 ++++++++++++++++--- .../statistics/TradeStatisticsConverter.java | 27 +++++++++--- 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java index 55996437f24..111260a30ea 100644 --- a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java +++ b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java @@ -41,6 +41,7 @@ @Slf4j public class DisputeAgentSelection { + public static final int LOOK_BACK_RANGE = 100; public static T getLeastUsedMediator(TradeStatisticsManager tradeStatisticsManager, DisputeAgentManager disputeAgentManager) { return getLeastUsedDisputeAgent(tradeStatisticsManager, @@ -63,7 +64,7 @@ private static T getLeastUsedDisputeAgent(TradeStatisti list.sort(Comparator.comparing(TradeStatistics3::getDate)); Collections.reverse(list); if (!list.isEmpty()) { - int max = Math.min(list.size(), 100); + int max = Math.min(list.size(), LOOK_BACK_RANGE); list = list.subList(0, max); } diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java index 12a43003ad1..e585eff1fa1 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java @@ -45,11 +45,12 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; +import java.util.Arrays; import java.util.Date; import java.util.Map; import java.util.Optional; -import lombok.Value; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; @@ -61,7 +62,7 @@ * Data size is about 50 bytes in average */ @Slf4j -@Value +@Getter public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, CapabilityRequiringPayload { @@ -114,10 +115,10 @@ private enum PaymentMethodMapper { // Old converted trade stat objects might not have it set @Nullable @JsonExclude - private final String mediator; // todo entries from old data could be pruned + private String mediator; @Nullable @JsonExclude - private final String refundAgent; + private String refundAgent; // todo should we add referrerId as well? get added to extra map atm but not used so far @@ -129,7 +130,7 @@ private enum PaymentMethodMapper { // field in a class would break that hash and therefore break the storage mechanism. @Nullable @JsonExclude - private Map extraDataMap; + private final Map extraDataMap; public TradeStatistics3(String currency, long price, @@ -266,6 +267,11 @@ public Capabilities getRequiredCapabilities() { return new Capabilities(Capability.TRADE_STATISTICS_3); } + public void pruneOptionalData() { + mediator = null; + refundAgent = null; + } + public String getPaymentMethod() { try { return PaymentMethodMapper.values()[Integer.parseInt(paymentMethod)].name(); @@ -305,6 +311,33 @@ public boolean isValid() { !currency.isEmpty(); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TradeStatistics3)) return false; + + TradeStatistics3 that = (TradeStatistics3) o; + + if (price != that.price) return false; + if (amount != that.amount) return false; + if (date != that.date) return false; + if (currency != null ? !currency.equals(that.currency) : that.currency != null) return false; + if (paymentMethod != null ? !paymentMethod.equals(that.paymentMethod) : that.paymentMethod != null) + return false; + return Arrays.equals(hash, that.hash); + } + + @Override + public int hashCode() { + int result = currency != null ? currency.hashCode() : 0; + result = 31 * result + (int) (price ^ (price >>> 32)); + result = 31 * result + (int) (amount ^ (amount >>> 32)); + result = 31 * result + (paymentMethod != null ? paymentMethod.hashCode() : 0); + result = 31 * result + (int) (date ^ (date >>> 32)); + result = 31 * result + Arrays.hashCode(hash); + return result; + } + @Override public String toString() { return "TradeStatistics3{" + diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java index fc9e0bc4c5f..8cfad9aba54 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java @@ -17,6 +17,8 @@ package bisq.core.trade.statistics; +import bisq.core.offer.availability.DisputeAgentSelection; + import bisq.network.p2p.BootstrapListener; import bisq.network.p2p.P2PService; import bisq.network.p2p.storage.P2PDataStorage; @@ -29,18 +31,21 @@ import com.google.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Set; import lombok.extern.slf4j.Slf4j; +@Singleton @Slf4j public class TradeStatisticsConverter { @@ -91,8 +96,8 @@ public void onUpdatedDataReceived() { }); } - private static Set convertToTradeStatistics3(Collection persistableNetworkPayloads) { - Set result = new HashSet<>(); + private static List convertToTradeStatistics3(Collection persistableNetworkPayloads) { + List list = new ArrayList<>(); long ts = System.currentTimeMillis(); // We might have duplicate entries from both traders as the trade date was different from old clients. @@ -112,12 +117,20 @@ private static Set convertToTradeStatistics3(Collecti mapWithoutDuplicates.values().stream() .map(e -> convertToTradeStatistics3(e, false)) .filter(TradeStatistics3::isValid) - .forEach(result::add); + .forEach(list::add); log.info("Conversion to {} new trade statistic objects has been completed after {} ms", - result.size(), System.currentTimeMillis() - ts); + list.size(), System.currentTimeMillis() - ts); + + // We prune mediator and refundAgent data from all objects but the last 100 as we only use the + // last 100 entries (DisputeAgentSelection.LOOK_BACK_RANGE). + list.sort(Comparator.comparing(TradeStatistics3::getDate)); + for (int i = list.size() - DisputeAgentSelection.LOOK_BACK_RANGE; i < list.size(); i++) { + TradeStatistics3 tradeStatistics3 = list.get(i); + tradeStatistics3.pruneOptionalData(); + } - return result; + return list; } private static TradeStatistics3 convertToTradeStatistics3(TradeStatistics2 tradeStatistics2, boolean fromNetwork) { From f56fe42ae13993b472d87f8ac6145b6d2bab9d3d Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 19:56:11 -0500 Subject: [PATCH 081/123] Add injector.getInstance(TradeStatisticsConverter.class) to BisqExecutable to enforce inclusion. Cleanups, renamings --- core/src/main/java/bisq/core/app/BisqExecutable.java | 5 +++++ .../java/bisq/core/provider/price/PriceFeedService.java | 2 +- .../java/bisq/core/trade/statistics/TradeStatistics2.java | 2 +- .../bisq/core/trade/statistics/TradeStatisticsManager.java | 7 +++---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 8e403b97dc1..9ce6393c1a7 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -25,6 +25,7 @@ import bisq.core.setup.CorePersistedDataHost; import bisq.core.setup.CoreSetup; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; +import bisq.core.trade.statistics.TradeStatisticsConverter; import bisq.core.trade.txproof.xmr.XmrTxProofService; import bisq.network.p2p.P2PService; @@ -154,6 +155,10 @@ protected Injector getInjector() { protected void applyInjector() { // Subclasses might configure classes with the injector here + + // As TradeStatisticsConverter is not used by any other class we need to enforce that guice is creating it by + // requesting an instance here. + injector.getInstance(TradeStatisticsConverter.class); } protected void readAllPersisted(Runnable completeHandler) { diff --git a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java index 168812b4f43..b90b9128507 100644 --- a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java @@ -307,7 +307,7 @@ public void applyLatestBisqMarketPrice(Set tradeStatisticsSet) mapByCurrencyCode.values().stream() .filter(list -> !list.isEmpty()) .forEach(list -> { - list.sort(Comparator.comparing(o -> o.getTradeDate())); + list.sort(Comparator.comparing(TradeStatistics3::getTradeDate)); TradeStatistics3 tradeStatistics = list.get(list.size() - 1); setBisqMarketPrice(tradeStatistics.getCurrency(), tradeStatistics.getTradePrice()); }); diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java index 17a878c270c..7beee9864dd 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java @@ -61,7 +61,7 @@ /** * Serialized size is about 180-210 byte. Nov 2017 we have 5500 objects */ - +@Deprecated @Slf4j @Value public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java index 7e571391c84..9f5c967b13e 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -83,7 +83,7 @@ public void onAllServicesInitialized() { } observableTradeStatisticsSet.add(tradeStatistics); priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet); - maybeDump(); + maybeDumpStatistics(); } }); @@ -94,14 +94,14 @@ public void onAllServicesInitialized() { .collect(Collectors.toSet()); observableTradeStatisticsSet.addAll(set); priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet); - maybeDump(); + maybeDumpStatistics(); } public ObservableSet getObservableTradeStatisticsSet() { return observableTradeStatisticsSet; } - private void maybeDump() { + private void maybeDumpStatistics() { if (!dumpStatistics) { return; } @@ -130,5 +130,4 @@ private void maybeDump() { list.toArray(array); jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics"); } - } From fa374b99fb09d5d238c5adec330c9f08861fe693 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 20:17:07 -0500 Subject: [PATCH 082/123] Do conversion in a thread to not block UI thread. takes about 4 seconds on my machine. Add shutdown method to TradeStatisticsConverter and call it via TradeStatisticsManager. Now as we have a reference to TradeStatisticsConverter in we don't need the hack anymore in applyInjector. Set log level for com.neemre.btcdcli4j.core.client.ClientConfigurator to error as there is an expected warn log because of the outdated version. --- .../bisq/common/file/JsonFileManager.java | 4 +- .../java/bisq/core/app/BisqExecutable.java | 7 +-- .../statistics/TradeStatisticsConverter.java | 45 +++++++++++++------ .../statistics/TradeStatisticsManager.java | 10 +++++ desktop/src/main/resources/logback.xml | 3 +- 5 files changed, 48 insertions(+), 21 deletions(-) diff --git a/common/src/main/java/bisq/common/file/JsonFileManager.java b/common/src/main/java/bisq/common/file/JsonFileManager.java index 9cbc91f7b29..1d1db8a9d88 100644 --- a/common/src/main/java/bisq/common/file/JsonFileManager.java +++ b/common/src/main/java/bisq/common/file/JsonFileManager.java @@ -30,7 +30,7 @@ @Slf4j public class JsonFileManager { - private final ThreadPoolExecutor executor = Utilities.getThreadPoolExecutor("JsonFileManagerExecutor", 5, 50, 60); + private final ThreadPoolExecutor executor; private final File dir; @@ -41,6 +41,8 @@ public class JsonFileManager { public JsonFileManager(File dir) { this.dir = dir; + this.executor = Utilities.getThreadPoolExecutor("JsonFileManagerExecutor", 5, 50, 60); + if (!dir.exists()) if (!dir.mkdir()) log.warn("make dir failed"); diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 9ce6393c1a7..66c03dede43 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -25,7 +25,7 @@ import bisq.core.setup.CorePersistedDataHost; import bisq.core.setup.CoreSetup; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; -import bisq.core.trade.statistics.TradeStatisticsConverter; +import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.trade.txproof.xmr.XmrTxProofService; import bisq.network.p2p.P2PService; @@ -155,10 +155,6 @@ protected Injector getInjector() { protected void applyInjector() { // Subclasses might configure classes with the injector here - - // As TradeStatisticsConverter is not used by any other class we need to enforce that guice is creating it by - // requesting an instance here. - injector.getInstance(TradeStatisticsConverter.class); } protected void readAllPersisted(Runnable completeHandler) { @@ -225,6 +221,7 @@ public void gracefulShutDown(ResultHandler resultHandler) { try { injector.getInstance(ArbitratorManager.class).shutDown(); + injector.getInstance(TradeStatisticsManager.class).shutDown(); injector.getInstance(XmrTxProofService.class).shutDown(); injector.getInstance(DaoSetup.class).shutDown(); injector.getInstance(AvoidStandbyModeService.class).shutDown(); diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java index 8cfad9aba54..5e85784913b 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java @@ -25,8 +25,10 @@ import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; +import bisq.common.UserThread; import bisq.common.config.Config; import bisq.common.file.FileUtil; +import bisq.common.util.Utilities; import com.google.inject.Inject; @@ -42,6 +44,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutorService; import lombok.extern.slf4j.Slf4j; @@ -49,6 +52,8 @@ @Slf4j public class TradeStatisticsConverter { + private ExecutorService executor; + @Inject public TradeStatisticsConverter(P2PService p2PService, P2PDataStorage p2PDataStorage, @@ -60,24 +65,33 @@ public TradeStatisticsConverter(P2PService p2PService, appendOnlyDataStoreService.addService(tradeStatistics2StorageService); p2PService.addP2PServiceListener(new BootstrapListener() { + @Override public void onTorNodeReady() { if (!tradeStatistics2Store.exists()) { return; } - - // We convert early once tor is initialized but still not ready to receive data - var mapOfLiveData = tradeStatistics3StorageService.getMapOfLiveData(); - convertToTradeStatistics3(tradeStatistics2StorageService.getMapOfAllData().values()) - .forEach(e -> mapOfLiveData.put(new P2PDataStorage.ByteArray(e.getHash()), e)); - tradeStatistics3StorageService.persistNow(); - try { - log.info("We delete now the old trade statistics file as it was converted to the new format."); - FileUtil.deleteFileIfExists(tradeStatistics2Store); - } catch (IOException e) { - e.printStackTrace(); - log.error(e.toString()); - } + executor = Utilities.getSingleThreadExecutor("TradeStatisticsConverter"); + executor.submit(() -> { + // We convert early once tor is initialized but still not ready to receive data + Map tempMap = new HashMap<>(); + convertToTradeStatistics3(tradeStatistics2StorageService.getMapOfAllData().values()) + .forEach(e -> tempMap.put(new P2PDataStorage.ByteArray(e.getHash()), e)); + + // We map to user thread to avoid potential threading issues + UserThread.execute(() -> { + tradeStatistics3StorageService.getMapOfLiveData().putAll(tempMap); + tradeStatistics3StorageService.persistNow(); + }); + + try { + log.info("We delete now the old trade statistics file as it was converted to the new format."); + FileUtil.deleteFileIfExists(tradeStatistics2Store); + } catch (IOException e) { + e.printStackTrace(); + log.error(e.toString()); + } + }); } @Override @@ -96,6 +110,11 @@ public void onUpdatedDataReceived() { }); } + public void shutDown() { + if (executor != null) + executor.shutdown(); + } + private static List convertToTradeStatistics3(Collection persistableNetworkPayloads) { List list = new ArrayList<>(); long ts = System.currentTimeMillis(); diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java index 9f5c967b13e..dedc250f85a 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -52,6 +52,7 @@ public class TradeStatisticsManager { private final P2PService p2PService; private final PriceFeedService priceFeedService; private final TradeStatistics3StorageService tradeStatistics3StorageService; + private final TradeStatisticsConverter tradeStatisticsConverter; private final File storageDir; private final boolean dumpStatistics; private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet(); @@ -62,11 +63,13 @@ public TradeStatisticsManager(P2PService p2PService, PriceFeedService priceFeedService, TradeStatistics3StorageService tradeStatistics3StorageService, AppendOnlyDataStoreService appendOnlyDataStoreService, + TradeStatisticsConverter tradeStatisticsConverter, @Named(Config.STORAGE_DIR) File storageDir, @Named(Config.DUMP_STATISTICS) boolean dumpStatistics) { this.p2PService = p2PService; this.priceFeedService = priceFeedService; this.tradeStatistics3StorageService = tradeStatistics3StorageService; + this.tradeStatisticsConverter = tradeStatisticsConverter; this.storageDir = storageDir; this.dumpStatistics = dumpStatistics; @@ -74,6 +77,13 @@ public TradeStatisticsManager(P2PService p2PService, appendOnlyDataStoreService.addService(tradeStatistics3StorageService); } + public void shutDown() { + tradeStatisticsConverter.shutDown(); + if (jsonFileManager != null) { + jsonFileManager.shutDown(); + } + } + public void onAllServicesInitialized() { p2PService.getP2PDataStorage().addAppendOnlyDataStoreListener(payload -> { if (payload instanceof TradeStatistics3) { diff --git a/desktop/src/main/resources/logback.xml b/desktop/src/main/resources/logback.xml index 8812d310de9..6b05588a4fe 100644 --- a/desktop/src/main/resources/logback.xml +++ b/desktop/src/main/resources/logback.xml @@ -11,7 +11,6 @@ - + From 213050c3d14a658b3035fafce647c328dc658037 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 23:09:16 -0500 Subject: [PATCH 083/123] Add filter for excluding null objects --- .../bisq/core/offer/availability/DisputeAgentSelection.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java index 111260a30ea..8745bd3c565 100644 --- a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java +++ b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java @@ -30,6 +30,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -42,6 +43,7 @@ @Slf4j public class DisputeAgentSelection { public static final int LOOK_BACK_RANGE = 100; + public static T getLeastUsedMediator(TradeStatisticsManager tradeStatisticsManager, DisputeAgentManager disputeAgentManager) { return getLeastUsedDisputeAgent(tradeStatisticsManager, @@ -71,6 +73,7 @@ private static T getLeastUsedDisputeAgent(TradeStatisti // We stored only first 4 chars of disputeAgents onion address List lastAddressesUsedInTrades = list.stream() .map(tradeStatistics3 -> isMediator ? tradeStatistics3.getMediator() : tradeStatistics3.getRefundAgent()) + .filter(Objects::nonNull) .collect(Collectors.toList()); Set disputeAgents = disputeAgentManager.getObservableMap().values().stream() From 17f4ae2b541e4700562f3a13faf0d7ff5309911d Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 5 Oct 2020 23:53:04 -0500 Subject: [PATCH 084/123] Add check that size is > LOOK_BACK_RANGE --- .../statistics/TradeStatisticsConverter.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java index 5e85784913b..137c7d9ee85 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java @@ -130,23 +130,26 @@ private static List convertToTradeStatistics3(Collection mapWithoutDuplicates.putIfAbsent(new P2PDataStorage.ByteArray(e.getHash()), e)); - log.info("We convert the existing {} trade statistics objects to the new format. " + - "This might take a bit but is only done once.", mapWithoutDuplicates.size()); + log.info("We convert the existing {} trade statistics objects to the new format.", mapWithoutDuplicates.size()); mapWithoutDuplicates.values().stream() .map(e -> convertToTradeStatistics3(e, false)) .filter(TradeStatistics3::isValid) .forEach(list::add); + int size = list.size(); log.info("Conversion to {} new trade statistic objects has been completed after {} ms", - list.size(), System.currentTimeMillis() - ts); + size, System.currentTimeMillis() - ts); // We prune mediator and refundAgent data from all objects but the last 100 as we only use the // last 100 entries (DisputeAgentSelection.LOOK_BACK_RANGE). list.sort(Comparator.comparing(TradeStatistics3::getDate)); - for (int i = list.size() - DisputeAgentSelection.LOOK_BACK_RANGE; i < list.size(); i++) { - TradeStatistics3 tradeStatistics3 = list.get(i); - tradeStatistics3.pruneOptionalData(); + if (size > DisputeAgentSelection.LOOK_BACK_RANGE) { + int start = size - DisputeAgentSelection.LOOK_BACK_RANGE; + for (int i = start; i < size; i++) { + TradeStatistics3 tradeStatistics3 = list.get(i); + tradeStatistics3.pruneOptionalData(); + } } return list; From e95ab2a0b41a6a6b9d986b7118d99fb384f506a1 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 6 Oct 2020 00:12:12 -0500 Subject: [PATCH 085/123] Add resource file for 1.4.0 (should be updated at release time) --- p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET diff --git a/p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET b/p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET new file mode 100644 index 00000000000..97de382b59c --- /dev/null +++ b/p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d58ed2b6b59006a13c31d32838959c3bf910430c6b88e357c967087e2f33a5c +size 3900357 From 58d2f1bda92521408757dc55e11662369f14105b Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 10:56:23 -0500 Subject: [PATCH 086/123] Apply codacy suggestions @ripcurl: The complaint about private constructors (using guice this is legit) should be removed IMO. --- .../main/dao/economy/dashboard/BsqDashboardView.java | 4 ++-- .../main/market/trades/TradesChartsViewModelTest.java | 3 --- .../main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java | 6 +++--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java index a96dfd58719..ed0257e0449 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java @@ -121,7 +121,7 @@ public class BsqDashboardView extends ActivatableView implements /////////////////////////////////////////////////////////////////////////////////////////// @Inject - private BsqDashboardView(DaoFacade daoFacade, + public BsqDashboardView(DaoFacade daoFacade, TradeStatisticsManager tradeStatisticsManager, PriceFeedService priceFeedService, Preferences preferences, @@ -173,7 +173,7 @@ private void createKPIs() { marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.factsAndFigures.dashboard.marketCap")).second; - availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, 1, + availableAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, Res.get("dao.factsAndFigures.dashboard.availableAmount")).second; } diff --git a/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java index 3e7dd9db5bf..8544363176c 100644 --- a/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java @@ -64,9 +64,7 @@ public class TradesChartsViewModelTest { TradesChartsViewModel model; TradeStatisticsManager tradeStatisticsManager; - private static final Logger log = LoggerFactory.getLogger(TradesChartsViewModelTest.class); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - private KeyRing keyRing; private File dir; OfferPayload offer = new OfferPayload(null, 0, @@ -217,7 +215,6 @@ class Trade { ArrayList trades = new ArrayList<>(); // Set predetermined time to use as "now" during test - Date test_time = dateFormat.parse("2018-01-01T00:00:05"); // Monday /* new MockUp() { @Mock long currentTimeMillis() { diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java index 6786cb65368..060c97498fe 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java @@ -233,9 +233,9 @@ void report() { int oldest = (int) nodeAddressTupleMap.values().stream().min(Comparator.comparingLong(Tuple::getHeight)).get().height; // - update queried height - if(type.contains("DaoState")) + if (type.contains("DaoState")) daostateheight = oldest - 20; - else if(type.contains("Proposal")) + else if (type.contains("Proposal")) proposalheight = oldest - 20; else blindvoteheight = oldest - 20; @@ -255,7 +255,7 @@ else if(type.contains("Proposal")) List states = hitcount.entrySet().stream().sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue())).map(byteBufferIntegerEntry -> byteBufferIntegerEntry.getKey()).collect(Collectors.toList()); hitcount.clear(); - + TradesChartsViewModelTest.java nodeAddressTupleMap.forEach((nodeAddress, tuple) -> daoreport.put(type + "." + OnionParser.prettyPrint(nodeAddress) + ".hash", Integer.toString(Arrays.asList(states.toArray()).indexOf(ByteBuffer.wrap(tuple.hash))))); // - report reference head From 197d8c1e0d78acc638571ea801516b445c4e28bd Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 12:19:17 -0500 Subject: [PATCH 087/123] Remove copy&past mistake --- .../src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java | 1 - 1 file changed, 1 deletion(-) diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java index 060c97498fe..2ca12716f04 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java @@ -255,7 +255,6 @@ else if (type.contains("Proposal")) List states = hitcount.entrySet().stream().sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue())).map(byteBufferIntegerEntry -> byteBufferIntegerEntry.getKey()).collect(Collectors.toList()); hitcount.clear(); - TradesChartsViewModelTest.java nodeAddressTupleMap.forEach((nodeAddress, tuple) -> daoreport.put(type + "." + OnionParser.prettyPrint(nodeAddress) + ".hash", Integer.toString(Arrays.asList(states.toArray()).indexOf(ByteBuffer.wrap(tuple.hash))))); // - report reference head From 18a27e90676c91e3b107355e1d85378a56981069 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 13:00:31 -0500 Subject: [PATCH 088/123] Republish trade statistics from seller side if peer capability is know. This is not the case without getting PR #4609 merges as well. We only do it for 2 weeks after planned release time as then it can be assumed that enough nodes have updated that the normal publishing will distribute the object sufficiently. --- .../core/trade/protocol/SellerProtocol.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java index 1b4815f4678..9f1acab3689 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java @@ -39,12 +39,17 @@ import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ResultHandler; +import bisq.common.util.Utilities; + +import java.util.Date; +import java.util.GregorianCalendar; import lombok.extern.slf4j.Slf4j; @Slf4j public abstract class SellerProtocol extends DisputeProtocol { enum SellerEvent implements FluentProtocol.Event { + STARTUP, PAYMENT_RECEIVED } @@ -52,6 +57,27 @@ public SellerProtocol(SellerTrade trade) { super(trade); } + @Override + protected void onInitialized() { + super.onInitialized(); + + // We get called the constructor with any possible state and phase. As we don't want to log an error for such + // cases we use the alternative 'given' method instead of 'expect'. + + // We only re-publish for about 2 weeks after 1.4.0 release until most nodes have updated to + // achieve sufficient resilience. + boolean currentDateBeforeCutOffDate = new Date().before(Utilities.getUTCDate(2020, GregorianCalendar.NOVEMBER, 1)); + given(anyPhase(Trade.Phase.DEPOSIT_PUBLISHED, + Trade.Phase.DEPOSIT_CONFIRMED, + Trade.Phase.FIAT_SENT, + Trade.Phase.FIAT_RECEIVED, + Trade.Phase.PAYOUT_PUBLISHED) + .with(SellerEvent.STARTUP) + .preCondition(currentDateBeforeCutOffDate)) + .setup(tasks(SellerPublishesTradeStatistics.class)) + .executeTasks(); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Mailbox From 68a10cf5a17f914a2e1f9f1d0e559c707d89262e Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 18:51:24 -0500 Subject: [PATCH 089/123] Remove comment line --- p2p/src/main/java/bisq/network/p2p/P2PService.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index bf594ecf6fc..8716c81d9d1 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -928,11 +928,6 @@ public Optional findPeersCapabilities(NodeAddress peer) { .findAny(); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - @Value public class MailboxItem { private final ProtectedMailboxStorageEntry protectedMailboxStorageEntry; From 66740b7dc1113e72f17c2df15c92e4135568d414 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 19:02:51 -0500 Subject: [PATCH 090/123] Remove unused variable --- .../main/market/trades/TradesChartsViewModelTest.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java index 8544363176c..8d3a7d25660 100644 --- a/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/market/trades/TradesChartsViewModelTest.java @@ -29,9 +29,6 @@ import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; -import bisq.common.crypto.KeyRing; -import bisq.common.crypto.KeyStorage; - import org.bitcoinj.core.Coin; import org.bitcoinj.utils.Fiat; @@ -50,9 +47,6 @@ import java.util.HashSet; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -116,7 +110,6 @@ public void setup() throws IOException { dir.delete(); //noinspection ResultOfMethodCallIgnored dir.mkdir(); - keyRing = new KeyRing(new KeyStorage(dir)); } @SuppressWarnings("ConstantConditions") From 39c8ade5ef30ac28fe3b465674ea74c3f7efcc98 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 8 Oct 2020 22:00:57 -0500 Subject: [PATCH 091/123] Cleanups: Remove outdated TODOs, fix typos --- .../src/main/java/bisq/common/app/Version.java | 1 - .../consensus/UsedForTradeContractJson.java | 3 --- .../main/java/bisq/common/crypto/Encryption.java | 1 - .../main/java/bisq/common/crypto/KeyStorage.java | 1 - .../common/proto/network/NetworkEnvelope.java | 1 - .../java/bisq/core/provider/fee/FeeService.java | 1 - .../dispute/arbitration/ArbitrationManager.java | 2 +- .../desktop/components/AddressTextField.java | 3 --- .../desktop/components/BsqAddressTextField.java | 3 --- .../components/TextFieldWithCopyIcon.java | 3 --- .../account/register/AgentRegistrationView.java | 2 -- .../desktop/main/market/spread/SpreadView.java | 2 +- .../main/market/spread/SpreadViewModel.java | 2 +- .../main/java/bisq/desktop/util/FormBuilder.java | 2 +- .../java/bisq/monitor/metric/MarketStats.java | 16 ---------------- .../java/bisq/monitor/metric/PriceNodeStats.java | 3 +-- .../bisq/monitor/metric/TorRoundTripTime.java | 3 ++- .../java/bisq/monitor/metric/TorStartupTime.java | 2 -- 18 files changed, 7 insertions(+), 44 deletions(-) diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index fd8cfb64634..45fc057242b 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -128,7 +128,6 @@ public static void printVersion() { '}'); } - //TODO move to consensus area public static final byte COMPENSATION_REQUEST = (byte) 0x01; public static final byte REIMBURSEMENT_REQUEST = (byte) 0x01; public static final byte PROPOSAL = (byte) 0x01; diff --git a/common/src/main/java/bisq/common/consensus/UsedForTradeContractJson.java b/common/src/main/java/bisq/common/consensus/UsedForTradeContractJson.java index 6aa722a4d7b..94105d34f39 100644 --- a/common/src/main/java/bisq/common/consensus/UsedForTradeContractJson.java +++ b/common/src/main/java/bisq/common/consensus/UsedForTradeContractJson.java @@ -24,8 +24,5 @@ * Better to use the excludeFromJsonDataMap (annotated with @JsonExclude; used in PaymentAccountPayload) to * add a key/value pair. */ -// TODO PubKeyRing and NodeAddress (network) are using UsedForTradeContractJson that is why it is in common module, -// which is a bit weird... Maybe we need either rename common or split it to util and common where common is common code -// used in network and core? public interface UsedForTradeContractJson { } diff --git a/common/src/main/java/bisq/common/crypto/Encryption.java b/common/src/main/java/bisq/common/crypto/Encryption.java index f4e1f8e0221..8be207fe4c2 100644 --- a/common/src/main/java/bisq/common/crypto/Encryption.java +++ b/common/src/main/java/bisq/common/crypto/Encryption.java @@ -48,7 +48,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// TODO is Hmac needed/make sense? public class Encryption { private static final Logger log = LoggerFactory.getLogger(Encryption.class); diff --git a/common/src/main/java/bisq/common/crypto/KeyStorage.java b/common/src/main/java/bisq/common/crypto/KeyStorage.java index c132ab0042f..7b4ecece087 100644 --- a/common/src/main/java/bisq/common/crypto/KeyStorage.java +++ b/common/src/main/java/bisq/common/crypto/KeyStorage.java @@ -55,7 +55,6 @@ import static bisq.common.util.Preconditions.checkDir; -// TODO: use a password protection for key storage @Singleton public class KeyStorage { private static final Logger log = LoggerFactory.getLogger(KeyStorage.class); diff --git a/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java b/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java index 535d65d0486..330fbd72161 100644 --- a/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java +++ b/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java @@ -48,7 +48,6 @@ public Message toProtoMessage() { return getNetworkEnvelopeBuilder().build(); } - // todo remove public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { return getNetworkEnvelopeBuilder().build(); } diff --git a/core/src/main/java/bisq/core/provider/fee/FeeService.java b/core/src/main/java/bisq/core/provider/fee/FeeService.java index 76427f5b3aa..b43c4491b62 100644 --- a/core/src/main/java/bisq/core/provider/fee/FeeService.java +++ b/core/src/main/java/bisq/core/provider/fee/FeeService.java @@ -52,7 +52,6 @@ import static com.google.common.base.Preconditions.checkNotNull; -// TODO use dao parameters for fee @Slf4j public class FeeService { diff --git a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java index 8552003199d..29f6b6939aa 100644 --- a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java @@ -157,7 +157,7 @@ public void cleanupDisputes() { @Override protected String getDisputeInfo(Dispute dispute) { String role = Res.get("shared.arbitrator").toLowerCase(); - String link = "https://docs.bisq.network/trading-rules.html#legacy-arbitration"; //TODO needs to be created + String link = "https://docs.bisq.network/trading-rules.html#legacy-arbitration"; return Res.get("support.initialInfo", role, role, link); } diff --git a/desktop/src/main/java/bisq/desktop/components/AddressTextField.java b/desktop/src/main/java/bisq/desktop/components/AddressTextField.java index 9bc06ce5147..f9ca98a1130 100644 --- a/desktop/src/main/java/bisq/desktop/components/AddressTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/AddressTextField.java @@ -78,9 +78,6 @@ public AddressTextField(String label) { }); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - //TODO app wide focus - //focusedProperty().addListener((ov, oldValue, newValue) -> textField.requestFocus()); - Label extWalletIcon = new Label(); extWalletIcon.setLayoutY(3); extWalletIcon.getStyleClass().addAll("icon", "highlight"); diff --git a/desktop/src/main/java/bisq/desktop/components/BsqAddressTextField.java b/desktop/src/main/java/bisq/desktop/components/BsqAddressTextField.java index 97d61fd07bf..a74a4ae2e3b 100644 --- a/desktop/src/main/java/bisq/desktop/components/BsqAddressTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/BsqAddressTextField.java @@ -74,9 +74,6 @@ public BsqAddressTextField() { }); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - //TODO app wide focus - //focusedProperty().addListener((ov, oldValue, newValue) -> textField.requestFocus()); - Label copyIcon = new Label(); copyIcon.setLayoutY(3); diff --git a/desktop/src/main/java/bisq/desktop/components/TextFieldWithCopyIcon.java b/desktop/src/main/java/bisq/desktop/components/TextFieldWithCopyIcon.java index fe26aebeab7..7f2892379ca 100644 --- a/desktop/src/main/java/bisq/desktop/components/TextFieldWithCopyIcon.java +++ b/desktop/src/main/java/bisq/desktop/components/TextFieldWithCopyIcon.java @@ -79,9 +79,6 @@ public TextFieldWithCopyIcon(String customStyleClass) { AnchorPane.setRightAnchor(textField, 30.0); AnchorPane.setLeftAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - //TODO app wide focus - //focusedProperty().addListener((ov, oldValue, newValue) -> textField.requestFocus()); - getChildren().addAll(textField, copyIcon); } diff --git a/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationView.java b/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationView.java index 4af167a5940..656d126f86d 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationView.java @@ -64,8 +64,6 @@ import static bisq.desktop.util.FormBuilder.addTitledGroupBg; import static bisq.desktop.util.FormBuilder.addTopLabelTextField; -// TODO translation string keys should renamed to be more generic. -// Lets do it for 1.1.7 the translator have time to add new string. public abstract class AgentRegistrationView> extends ActivatableViewAndModel { diff --git a/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadView.java b/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadView.java index 02a2a402e51..539ab5ffad1 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadView.java @@ -298,7 +298,7 @@ public TableCell call( public void updateItem(final SpreadItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { - // TODO maybe show exra colums with item.priceSpread and use real amount diff + // TODO maybe show extra columns with item.priceSpread and use real amount diff // not % based if (item.priceSpread != null) setText(item.percentage); diff --git a/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadViewModel.java b/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadViewModel.java index 97aecedb3cb..e5ee933ca0d 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/market/spread/SpreadViewModel.java @@ -172,7 +172,7 @@ private void update(ObservableList offerBookListItems) { else spread = bestBuyOfferPrice.subtract(bestSellOfferPrice); - // TODO maybe show extra colums with spread and use real amount diff + // TODO maybe show extra columns with spread and use real amount diff // not % based. e.g. diff between best buy and sell offer (of small amounts its a smaller gain) if (spread != null && marketPrice != null && marketPrice.isPriceAvailable()) { diff --git a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java index 46db98c0153..f8061e04f99 100644 --- a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java +++ b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java @@ -332,7 +332,7 @@ public static Tuple3 addTopLabelTextField(GridPane gridP final Tuple2 topLabelWithVBox = addTopLabelWithVBox(gridPane, rowIndex, title, textField, top); - // TOD not 100% sure if that is a good idea.... + // TODO not 100% sure if that is a good idea.... //topLabelWithVBox.first.getStyleClass().add("jfx-text-field-top-label"); return new Tuple3<>(topLabelWithVBox.first, textField, topLabelWithVBox.second); diff --git a/monitor/src/main/java/bisq/monitor/metric/MarketStats.java b/monitor/src/main/java/bisq/monitor/metric/MarketStats.java index 389722329a3..f831cafd135 100644 --- a/monitor/src/main/java/bisq/monitor/metric/MarketStats.java +++ b/monitor/src/main/java/bisq/monitor/metric/MarketStats.java @@ -20,30 +20,15 @@ import bisq.monitor.Metric; import bisq.monitor.Reporter; -import bisq.asset.Asset; -import bisq.asset.AssetRegistry; - -import bisq.network.p2p.storage.payload.ProtectedStoragePayload; - -import org.berndpruenster.netlayer.tor.TorCtlException; - -import com.runjva.sourceforge.jsocks.protocol.SocksSocket; - -import java.net.HttpURLConnection; -import java.net.Socket; import java.net.URL; import java.net.URLConnection; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; @@ -110,7 +95,6 @@ protected void execute() { } catch (IllegalStateException ignore) { // no match found } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } diff --git a/monitor/src/main/java/bisq/monitor/metric/PriceNodeStats.java b/monitor/src/main/java/bisq/monitor/metric/PriceNodeStats.java index 9258eb5e8d5..fd10691f727 100644 --- a/monitor/src/main/java/bisq/monitor/metric/PriceNodeStats.java +++ b/monitor/src/main/java/bisq/monitor/metric/PriceNodeStats.java @@ -54,7 +54,7 @@ /** * Fetches fee and price data from the configured price nodes. * Based on the work of HarryMcFinned. - * + * * @author Florian Reimair * @author HarryMcFinned * @@ -159,7 +159,6 @@ protected void execute() { } } } catch (TorCtlException | IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } diff --git a/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java b/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java index 16499689b08..a40c0261662 100644 --- a/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java +++ b/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java @@ -21,6 +21,7 @@ import bisq.monitor.OnionParser; import bisq.monitor.Reporter; import bisq.monitor.StatisticsHelper; + import bisq.network.p2p.NodeAddress; import org.berndpruenster.netlayer.tor.Tor; @@ -33,6 +34,7 @@ import java.util.ArrayList; import java.util.List; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -84,7 +86,6 @@ protected void execute() { reporter.report(StatisticsHelper.process(samples), getName()); } } catch (TorCtlException | IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } diff --git a/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java b/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java index 6e53e02462b..9ea3198e197 100644 --- a/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java +++ b/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java @@ -58,7 +58,6 @@ public void configure(Properties properties) { try { torOverrides = new Torrc(overrides); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } @@ -79,7 +78,6 @@ protected void execute() { // stop the timer and set its timestamp reporter.report(System.currentTimeMillis() - start, getName()); } catch (TorCtlException e) { - // TODO Auto-generated catch block e.printStackTrace(); } finally { // cleanup From 4d2f1b5d431167b5c5c9a90732616e72975f0f60 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Sat, 10 Oct 2020 19:01:51 -0500 Subject: [PATCH 092/123] Set memo to tx after tx creation and not on broadcast success, as broadcast success might not get called (even it is broadcast -> pending btcj bug) --- .../core/btc/wallet/BtcWalletService.java | 28 +++++++++-------- .../main/funds/withdrawal/WithdrawalView.java | 30 +++++++++++-------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index e21ce45b99a..bda7a25203e 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -614,10 +614,10 @@ public AddressEntry getFreshAddressEntry(boolean segwit) { .filter(e -> context == e.getContext()) .filter(e -> isAddressUnused(e.getAddress())) .filter(e -> { - boolean isSegwitOutputScriptType = Script.ScriptType.P2WPKH.equals(e.getAddress().getOutputScriptType()); - // We need to ensure that we take only addressEntries which matches our segWit flag - boolean isMatchingOutputScriptType = isSegwitOutputScriptType == segwit; - return isMatchingOutputScriptType; + boolean isSegwitOutputScriptType = Script.ScriptType.P2WPKH.equals(e.getAddress().getOutputScriptType()); + // We need to ensure that we take only addressEntries which matches our segWit flag + boolean isMatchingOutputScriptType = isSegwitOutputScriptType == segwit; + return isMatchingOutputScriptType; }) .findAny(); return getOrCreateAddressEntry(context, addressEntry, segwit); @@ -628,7 +628,9 @@ public void recoverAddressEntry(String offerId, String address, AddressEntry.Con addressEntryList.swapAvailableToAddressEntryWithOfferId(addressEntry, context, offerId)); } - private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optional addressEntry, boolean segwit) { + private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, + Optional addressEntry, + boolean segwit) { if (addressEntry.isPresent()) { return addressEntry.get(); } else { @@ -1056,13 +1058,13 @@ public String sendFunds(String fromAddress, return sendResult.tx.getTxId().toString(); } - public String sendFundsForMultipleAddresses(Set fromAddresses, - String toAddress, - Coin receiverAmount, - Coin fee, - @Nullable String changeAddress, - @Nullable KeyParameter aesKey, - FutureCallback callback) throws AddressFormatException, + public Transaction sendFundsForMultipleAddresses(Set fromAddresses, + String toAddress, + Coin receiverAmount, + Coin fee, + @Nullable String changeAddress, + @Nullable KeyParameter aesKey, + FutureCallback callback) throws AddressFormatException, AddressEntryException, InsufficientMoneyException { SendRequest request = getSendRequestForMultipleAddresses(fromAddresses, toAddress, receiverAmount, fee, changeAddress, aesKey); @@ -1070,7 +1072,7 @@ public String sendFundsForMultipleAddresses(Set fromAddresses, Futures.addCallback(sendResult.broadcastComplete, callback, MoreExecutors.directExecutor()); printTx("sendFunds", sendResult.tx); - return sendResult.tx.getTxId().toString(); + return sendResult.tx; } private SendRequest getSendRequest(String fromAddress, diff --git a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java index 530e203d23f..898fab36c48 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java @@ -366,17 +366,17 @@ private void onWithdraw() { double kb = txSize / 1000d; String messageText = Res.get("shared.sendFundsDetailsWithFee", - formatter.formatCoinWithCode(sendersAmount), - withdrawFromTextField.getText(), - withdrawToTextField.getText(), - formatter.formatCoinWithCode(fee), - feePerByte, - kb, - formatter.formatCoinWithCode(receiverAmount)); + formatter.formatCoinWithCode(sendersAmount), + withdrawFromTextField.getText(), + withdrawToTextField.getText(), + formatter.formatCoinWithCode(fee), + feePerByte, + kb, + formatter.formatCoinWithCode(receiverAmount)); if (dust.isPositive()) { messageText = Res.get("shared.sendFundsDetailsDust", - dust.value, dust.value > 1 ? "s" : "") - + messageText; + dust.value, dust.value > 1 ? "s" : "") + + messageText; } new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest")) @@ -386,7 +386,6 @@ private void onWithdraw() { @Override public void onSuccess(@javax.annotation.Nullable Transaction transaction) { if (transaction != null) { - transaction.setMemo(withdrawMemoTextField.getText()); log.debug("onWithdraw onSuccess tx ID:{}", transaction.getTxId().toString()); } else { log.error("onWithdraw transaction is null"); @@ -498,7 +497,14 @@ private void doWithdraw(Coin amount, Coin fee, FutureCallback callb private void sendFunds(Coin amount, Coin fee, KeyParameter aesKey, FutureCallback callback) { try { - btcWalletService.sendFundsForMultipleAddresses(fromAddresses, withdrawToTextField.getText(), amount, fee, null, aesKey, callback); + Transaction transaction = btcWalletService.sendFundsForMultipleAddresses(fromAddresses, + withdrawToTextField.getText(), + amount, + fee, + null, + aesKey, + callback); + transaction.setMemo(withdrawMemoTextField.getText()); reset(); updateList(); } catch (AddressFormatException e) { @@ -672,7 +678,7 @@ public void updateItem(final WithdrawalListItem item, boolean empty) { // returns the 'dust amount' to indicate if any dust was detected. private Coin getDust(Transaction transaction) { Coin dust = Coin.ZERO; - for (TransactionOutput transactionOutput: transaction.getOutputs()) { + for (TransactionOutput transactionOutput : transaction.getOutputs()) { if (transactionOutput.getValue().isLessThan(Restrictions.getMinNonDustOutput())) { dust = dust.add(transactionOutput.getValue()); log.info("dust TXO = {}", transactionOutput.toString()); From db10aa31aa4c720d47b09931e478bf8075f7b895 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Sun, 11 Oct 2020 16:40:07 -0500 Subject: [PATCH 093/123] Only show xmr auto conf label for sell offers --- .../desktop/main/overlays/windows/OfferDetailsWindow.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java index 1d88267abe7..872a149cf15 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java @@ -165,7 +165,8 @@ private void addContent() { if (isF2F) rows += 2; - if (offer.isXmr()) { + boolean showXmrAutoConf = offer.isXmr() && offer.getDirection() == OfferPayload.Direction.SELL; + if (showXmrAutoConf) { rows++; } @@ -262,7 +263,7 @@ else if (BankUtil.isBankNameRequired(countryCode)) } } - if (offer.isXmr()) { + if (showXmrAutoConf) { String isAutoConf = offer.isXmrAutoConf() ? Res.get("shared.yes") : Res.get("shared.no"); From 1f0590dc95f7ff6fbd6fc8168516597acdd2bde0 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Sun, 11 Oct 2020 17:16:17 -0500 Subject: [PATCH 094/123] Use hash at conversion also for local data to avoid duplicates. --- .../statistics/TradeStatisticsConverter.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java index 137c7d9ee85..43693badce8 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java @@ -102,7 +102,7 @@ public void onUpdatedDataReceived() { // We listen to old TradeStatistics2 objects, convert and store them and rebroadcast. p2PDataStorage.addAppendOnlyDataStoreListener(payload -> { if (payload instanceof TradeStatistics2) { - TradeStatistics3 tradeStatistics3 = convertToTradeStatistics3((TradeStatistics2) payload, true); + TradeStatistics3 tradeStatistics3 = convertToTradeStatistics3((TradeStatistics2) payload); // We add it to the p2PDataStorage, which handles to get the data stored in the maps and maybe // re-broadcast as tradeStatistics3 object if not already received. p2PDataStorage.addPersistableNetworkPayload(tradeStatistics3, null, true); @@ -133,7 +133,7 @@ private static List convertToTradeStatistics3(Collection convertToTradeStatistics3(e, false)) + .map(TradeStatisticsConverter::convertToTradeStatistics3) .filter(TradeStatistics3::isValid) .forEach(list::add); @@ -151,25 +151,22 @@ private static List convertToTradeStatistics3(Collection extraDataMap = tradeStatistics2.getExtraDataMap(); String mediator = extraDataMap != null ? extraDataMap.get(TradeStatistics2.MEDIATOR_ADDRESS) : null; String refundAgent = extraDataMap != null ? extraDataMap.get(TradeStatistics2.REFUND_AGENT_ADDRESS) : null; long time = tradeStatistics2.getTradeDate().getTime(); - byte[] hash = null; - if (fromNetwork) { - // We need to avoid that we duplicate tradeStatistics2 objects in case both traders have not udpated yet. - // Before v1.4.0 both traders published the trade statistics. If one trader has updated he will check - // the capabilities of the peer and if the peer has not updated he will leave publishing to the peer, so we - // do not have the problem of duplicated objects. - // To ensure we add only one object we will use the hash of the tradeStatistics2 object which is the same - // for both traders as it excluded the trade date which is different for both. - hash = tradeStatistics2.getHash(); - } + // We need to avoid that we duplicate tradeStatistics2 objects in case both traders have not updated yet. + // Before v1.4.0 both traders published the trade statistics. If one trader has updated he will check + // the capabilities of the peer and if the peer has not updated he will leave publishing to the peer, so we + // do not have the problem of duplicated objects. + // Also at conversion of locally stored old trade statistics we need to avoid duplicated entries. + // To ensure we add only one object we will use the hash of the tradeStatistics2 object which is the same + // for both traders as it excluded the trade date which is different for both. + byte[] hash = tradeStatistics2.getHash(); return new TradeStatistics3(tradeStatistics2.getCurrencyCode(), tradeStatistics2.getTradePrice().getValue(), tradeStatistics2.getTradeAmount().getValue(), From 8e93e4be0b9f4a31c22a4a1ccec15cdf2c4fad75 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Sun, 11 Oct 2020 20:42:53 -0300 Subject: [PATCH 095/123] Use aesKey in Wallet.toString() --- core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java | 2 +- core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java index 39eda3be0aa..7fd1229cfaa 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java @@ -208,7 +208,7 @@ public void onParseBlockCompleteAfterBatchProcessing(Block block) { @Override String getWalletAsString(boolean includePrivKeys) { - return wallet.toString(true, includePrivKeys, null, true, true, walletsSetup.getChain()) + "\n\n" + + return wallet.toString(true, includePrivKeys, this.aesKey, true, true, walletsSetup.getChain()) + "\n\n" + "All pubKeys as hex:\n" + wallet.printAllPubKeysAsHex(); } diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index e21ce45b99a..50f0f31364d 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -138,10 +138,11 @@ void encryptWallet(KeyCrypterScrypt keyCrypterScrypt, KeyParameter key) { String getWalletAsString(boolean includePrivKeys) { StringBuilder sb = new StringBuilder(); getAddressEntryListAsImmutableList().forEach(e -> sb.append(e.toString()).append("\n")); + //boolean reallyIncludePrivKeys = includePrivKeys && !wallet.isEncrypted(); return "Address entry list:\n" + sb.toString() + "\n\n" + - wallet.toString(true, includePrivKeys, null, true, true, walletsSetup.getChain()) + "\n\n" + + wallet.toString(true, includePrivKeys, this.aesKey, true, true, walletsSetup.getChain()) + "\n\n" + "All pubKeys as hex:\n" + wallet.printAllPubKeysAsHex(); } From 1c0655e2c7c28428a0068cfe953ad3744a62dc10 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 12 Oct 2020 17:10:39 -0300 Subject: [PATCH 096/123] Stop PeerGroup only if running --- core/src/main/java/bisq/core/btc/setup/WalletConfig.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index fc8c6a09506..9f574036429 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -468,9 +468,13 @@ protected void shutDown() throws Exception { // vPeerGroup.stop has no timeout and can take very long (10 sec. in my test). So we call it at the end. // We might get likely interrupted by the parent call timeout. - vPeerGroup.stop(); + if (vPeerGroup.isRunning()) { + vPeerGroup.stop(); + log.info("PeerGroup stopped"); + } else { + log.info("PeerGroup not stopped because it was not running"); + } vPeerGroup = null; - log.info("PeerGroup stopped"); } catch (BlockStoreException e) { throw new IOException(e); } From 0d469066ce5afa427fe47f153289478e039d613e Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 12 Oct 2020 23:51:15 -0500 Subject: [PATCH 097/123] Change write json files to disk strategy We have written all files each time at each new block which accumulated to about 1 GB of data, this led to stability issues and very high disk IO for the explorer nodes. Now we write only the data of the new block. We also remove the deletion of all files at startup. The dao state is still written in a monolithic file so that cannot be optimized but we added a new directly where we add each block named by block height. Looking up the latest written file tells us our last persisted block. So all that data is equivalent to the monolithic daoState data which should be removed once the webapp devs have implemented the change to use the blocks directory. --- .../bisq/common/file/JsonFileManager.java | 103 +++++---- .../app/misc/ExecutableForAppWithP2p.java | 4 + .../main/java/bisq/core/dao/node/BsqNode.java | 6 +- .../node/explorer/ExportJsonFilesService.java | 200 ++++++++++-------- .../bisq/core/dao/node/full/FullNode.java | 2 +- .../bisq/core/dao/node/lite/LiteNode.java | 13 +- .../bisq/core/offer/OfferBookService.java | 2 +- .../bisq/core/trade/DumpDelayedPayoutTx.java | 2 +- .../statistics/TradeStatisticsManager.java | 6 +- 9 files changed, 199 insertions(+), 139 deletions(-) diff --git a/common/src/main/java/bisq/common/file/JsonFileManager.java b/common/src/main/java/bisq/common/file/JsonFileManager.java index 1d1db8a9d88..4a10f508e3b 100644 --- a/common/src/main/java/bisq/common/file/JsonFileManager.java +++ b/common/src/main/java/bisq/common/file/JsonFileManager.java @@ -24,13 +24,26 @@ import java.io.File; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ThreadPoolExecutor; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + @Slf4j public class JsonFileManager { - private final ThreadPoolExecutor executor; + private final static List INSTANCES = new ArrayList<>(); + + public static void shutDownAllInstances() { + INSTANCES.forEach(JsonFileManager::shutDown); + } + + + @Nullable + private ThreadPoolExecutor executor; private final File dir; @@ -41,54 +54,62 @@ public class JsonFileManager { public JsonFileManager(File dir) { this.dir = dir; - this.executor = Utilities.getThreadPoolExecutor("JsonFileManagerExecutor", 5, 50, 60); + if (!dir.exists() && !dir.mkdir()) { + log.warn("make dir failed"); + } - if (!dir.exists()) - if (!dir.mkdir()) - log.warn("make dir failed"); + INSTANCES.add(this); + } - Runtime.getRuntime().addShutdownHook(new Thread(JsonFileManager.this::shutDown, - "JsonFileManager.ShutDownHook")); + @NotNull + protected ThreadPoolExecutor getExecutor() { + if (executor == null) { + executor = Utilities.getThreadPoolExecutor("JsonFileManagerExecutor", 5, 50, 60); + } + return executor; } public void shutDown() { - executor.shutdown(); + if (executor != null) { + executor.shutdown(); + } } - public void writeToDisc(String json, String fileName) { - executor.execute(() -> { - File jsonFile = new File(Paths.get(dir.getAbsolutePath(), fileName + ".json").toString()); - File tempFile = null; - PrintWriter printWriter = null; - try { - tempFile = File.createTempFile("temp", null, dir); - if (!executor.isShutdown() && !executor.isTerminated() && !executor.isTerminating()) - tempFile.deleteOnExit(); - - printWriter = new PrintWriter(tempFile); - printWriter.println(json); - - // This close call and comment is borrowed from FileManager. Not 100% sure it that is really needed but - // seems that had fixed in the past and we got reported issues on Windows so that fix might be still - // required. - // Close resources before replacing file with temp file because otherwise it causes problems on windows - // when rename temp file - printWriter.close(); + public void writeToDiscThreaded(String json, String fileName) { + getExecutor().execute(() -> writeToDisc(json, fileName)); + } - FileUtil.renameFile(tempFile, jsonFile); - } catch (Throwable t) { - log.error("storageFile " + jsonFile.toString()); - t.printStackTrace(); - } finally { - if (tempFile != null && tempFile.exists()) { - log.warn("Temp file still exists after failed save. We will delete it now. storageFile=" + fileName); - if (!tempFile.delete()) - log.error("Cannot delete temp file."); - } - - if (printWriter != null) - printWriter.close(); + public void writeToDisc(String json, String fileName) { + File jsonFile = new File(Paths.get(dir.getAbsolutePath(), fileName + ".json").toString()); + File tempFile = null; + PrintWriter printWriter = null; + try { + tempFile = File.createTempFile("temp", null, dir); + tempFile.deleteOnExit(); + + printWriter = new PrintWriter(tempFile); + printWriter.println(json); + + // This close call and comment is borrowed from FileManager. Not 100% sure it that is really needed but + // seems that had fixed in the past and we got reported issues on Windows so that fix might be still + // required. + // Close resources before replacing file with temp file because otherwise it causes problems on windows + // when rename temp file + printWriter.close(); + + FileUtil.renameFile(tempFile, jsonFile); + } catch (Throwable t) { + log.error("storageFile " + jsonFile.toString()); + t.printStackTrace(); + } finally { + if (tempFile != null && tempFile.exists()) { + log.warn("Temp file still exists after failed save. We will delete it now. storageFile=" + fileName); + if (!tempFile.delete()) + log.error("Cannot delete temp file."); } - }); + + if (printWriter != null) + printWriter.close(); + } } } diff --git a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java index 8a22a56bcae..ef2c7dffaf2 100644 --- a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java @@ -21,6 +21,7 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.dao.DaoSetup; import bisq.core.offer.OpenOfferManager; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; @@ -31,6 +32,7 @@ import bisq.common.UserThread; import bisq.common.app.DevEnv; import bisq.common.config.Config; +import bisq.common.file.JsonFileManager; import bisq.common.handlers.ResultHandler; import bisq.common.persistence.PersistenceManager; import bisq.common.setup.GracefulShutDownHandler; @@ -83,6 +85,8 @@ public void gracefulShutDown(ResultHandler resultHandler) { log.info("gracefulShutDown"); try { if (injector != null) { + JsonFileManager.shutDownAllInstances(); + injector.getInstance(DaoSetup.class).shutDown(); injector.getInstance(ArbitratorManager.class).shutDown(); injector.getInstance(OpenOfferManager.class).shutDown(() -> injector.getInstance(P2PService.class).shutDown(() -> { injector.getInstance(WalletsSetup.class).shutDownComplete.addListener((ov, o, n) -> { diff --git a/core/src/main/java/bisq/core/dao/node/BsqNode.java b/core/src/main/java/bisq/core/dao/node/BsqNode.java index e06be387575..fd44b875338 100644 --- a/core/src/main/java/bisq/core/dao/node/BsqNode.java +++ b/core/src/main/java/bisq/core/dao/node/BsqNode.java @@ -209,7 +209,7 @@ protected void onParseBlockChainComplete() { parseBlockchainComplete = true; daoStateService.onParseBlockChainComplete(); - maybeExportToJson(); + exportJsonFilesService.onParseBlockChainComplete(); } @SuppressWarnings("WeakerAccess") @@ -291,7 +291,7 @@ protected Optional doParseBlock(RawBlock rawBlock) throws RequiredReorgFr return Optional.empty(); } - protected void maybeExportToJson() { - exportJsonFilesService.maybeExportToJson(); + protected void maybeExportNewBlockToJson(Block block) { + exportJsonFilesService.onNewBlock(block); } } diff --git a/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java b/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java index 1c9c605f04d..193218f63fb 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java @@ -19,7 +19,6 @@ import bisq.core.dao.DaoSetupService; import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.DaoState; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.PubKeyScript; import bisq.core.dao.state.model.blockchain.Tx; @@ -27,7 +26,6 @@ import bisq.core.dao.state.model.blockchain.TxType; import bisq.common.config.Config; -import bisq.common.file.FileUtil; import bisq.common.file.JsonFileManager; import bisq.common.util.Utilities; @@ -37,18 +35,11 @@ import javax.inject.Named; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - import java.nio.file.Paths; import java.io.File; -import java.io.IOException; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -56,17 +47,13 @@ import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; - @Slf4j public class ExportJsonFilesService implements DaoSetupService { private final DaoStateService daoStateService; private final File storageDir; - private final boolean dumpBlockchainData; - - private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", - 1, 1, 1200); - private JsonFileManager txFileManager, txOutputFileManager, bsqStateFileManager; + private boolean dumpBlockchainData; + private JsonFileManager blockFileManager, txFileManager, txOutputFileManager, bsqStateFileManager; + private File blockDir; @Inject public ExportJsonFilesService(DaoStateService daoStateService, @@ -88,88 +75,135 @@ public void addListeners() { @Override public void start() { - if (dumpBlockchainData) { - File jsonDir = new File(Paths.get(storageDir.getAbsolutePath(), "json").toString()); - File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "tx").toString()); - File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "txo").toString()); - File bsqStateDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "all").toString()); - try { - if (txDir.exists()) - FileUtil.deleteDirectory(txDir); - if (txOutputDir.exists()) - FileUtil.deleteDirectory(txOutputDir); - if (bsqStateDir.exists()) - FileUtil.deleteDirectory(bsqStateDir); - if (jsonDir.exists()) - FileUtil.deleteDirectory(jsonDir); - } catch (IOException e) { - log.error(e.toString()); - e.printStackTrace(); - } + if (!dumpBlockchainData) { + return; + } - if (!jsonDir.mkdir()) - log.warn("make jsonDir failed.\njsonDir=" + jsonDir.getAbsolutePath()); + File jsonDir = new File(Paths.get(storageDir.getAbsolutePath(), "json").toString()); + blockDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "block").toString()); + File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "tx").toString()); + File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "txo").toString()); + File bsqStateDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "all").toString()); - if (!txDir.mkdir()) - log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath()); + if (!jsonDir.mkdir()) + log.warn("make jsonDir failed.\njsonDir=" + jsonDir.getAbsolutePath()); - if (!txOutputDir.mkdir()) - log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath()); + if (!blockDir.mkdir()) + log.warn("make blockDir failed.\njsonDir=" + blockDir.getAbsolutePath()); - if (!bsqStateDir.mkdir()) - log.warn("make bsqStateDir failed.\nbsqStateDir=" + bsqStateDir.getAbsolutePath()); + if (!txDir.mkdir()) + log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath()); - txFileManager = new JsonFileManager(txDir); - txOutputFileManager = new JsonFileManager(txOutputDir); - bsqStateFileManager = new JsonFileManager(bsqStateDir); - } + if (!txOutputDir.mkdir()) + log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath()); + + if (!bsqStateDir.mkdir()) + log.warn("make bsqStateDir failed.\nbsqStateDir=" + bsqStateDir.getAbsolutePath()); + + blockFileManager = new JsonFileManager(blockDir); + txFileManager = new JsonFileManager(txDir); + txOutputFileManager = new JsonFileManager(txOutputDir); + bsqStateFileManager = new JsonFileManager(bsqStateDir); } public void shutDown() { - if (dumpBlockchainData && txFileManager != null) { - txFileManager.shutDown(); - txOutputFileManager.shutDown(); - bsqStateFileManager.shutDown(); + if (!dumpBlockchainData) { + return; } + + blockFileManager.shutDown(); + txFileManager.shutDown(); + txOutputFileManager.shutDown(); + bsqStateFileManager.shutDown(); + dumpBlockchainData = false; } - public void maybeExportToJson() { - if (dumpBlockchainData && - daoStateService.isParseBlockChainComplete()) { - // We store the data we need once we write the data to disk (in the thread) locally. - // Access to daoStateService is single threaded, we must not access daoStateService from the thread. - List allJsonTxOutputs = new ArrayList<>(); - - List jsonTxs = daoStateService.getUnorderedTxStream() - .map(tx -> { - JsonTx jsonTx = getJsonTx(tx); - allJsonTxOutputs.addAll(jsonTx.getOutputs()); - return jsonTx; - }).collect(Collectors.toList()); - - DaoState daoState = daoStateService.getClone(); - List jsonBlockList = daoState.getBlocks().stream() - .map(this::getJsonBlock) - .collect(Collectors.toList()); - JsonBlocks jsonBlocks = new JsonBlocks(daoState.getChainHeight(), jsonBlockList); + public void onNewBlock(Block block) { + if (!dumpBlockchainData) { + return; + } - ListenableFuture future = executor.submit(() -> { - bsqStateFileManager.writeToDisc(Utilities.objectToJson(jsonBlocks), "blocks"); - allJsonTxOutputs.forEach(jsonTxOutput -> txOutputFileManager.writeToDisc(Utilities.objectToJson(jsonTxOutput), jsonTxOutput.getId())); - jsonTxs.forEach(jsonTx -> txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), jsonTx.getId())); - return null; - }); + // We do write the block on the main thread as the overhead to create a thread and risk for inconsistency is not + // worth the potential performance gain. + processBlock(block, true); + } - Futures.addCallback(future, new FutureCallback<>() { - public void onSuccess(Void ignore) { - } + private void processBlock(Block block, boolean doDumpDaoState) { + int lastPersistedBlock = getLastPersistedBlock(); + if (block.getHeight() <= lastPersistedBlock) { + return; + } + + long ts = System.currentTimeMillis(); + JsonBlock jsonBlock = getJsonBlock(block); + blockFileManager.writeToDisc(Utilities.objectToJson(jsonBlock), String.valueOf(jsonBlock.getHeight())); + + jsonBlock.getTxs().forEach(jsonTx -> { + txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), jsonTx.getId()); + + jsonTx.getOutputs().forEach(jsonTxOutput -> + txOutputFileManager.writeToDisc(Utilities.objectToJson(jsonTxOutput), jsonTxOutput.getId())); + }); - public void onFailure(@NotNull Throwable throwable) { - log.error(throwable.toString()); - throwable.printStackTrace(); + log.info("Write json data for block {} took {} ms", block.getHeight(), System.currentTimeMillis() - ts); + + if (doDumpDaoState) { + dumpDaoState(); + } + } + + public void onParseBlockChainComplete() { + if (!dumpBlockchainData) { + return; + } + + int lastPersistedBlock = getLastPersistedBlock(); + List blocks = daoStateService.getBlocksFromBlockHeight(lastPersistedBlock + 1, Integer.MAX_VALUE); + + // We use a thread here to write all past blocks to avoid that the main thread gets blocked for too long. + new Thread(() -> { + Thread.currentThread().setName("Write all blocks to json"); + blocks.forEach(e -> processBlock(e, false)); + }).start(); + + dumpDaoState(); + } + + private void dumpDaoState() { + // TODO we should get rid of that data structure and use the individual jsonBlocks instead as we cannot cache data + // here and re-write each time the full blockchain which is already > 200 MB + // Once the webapp has impl the changes we can delete that here. + long ts = System.currentTimeMillis(); + List jsonBlockList = daoStateService.getBlocks().stream() + .map(this::getJsonBlock) + .collect(Collectors.toList()); + JsonBlocks jsonBlocks = new JsonBlocks(daoStateService.getChainHeight(), jsonBlockList); + + // We use here the thread write method as the data is quite large and write can take a bit + bsqStateFileManager.writeToDiscThreaded(Utilities.objectToJson(jsonBlocks), "blocks"); + log.info("Dumping full bsqState with {} blocks took {} ms", + jsonBlocks.getBlocks().size(), System.currentTimeMillis() - ts); + } + + private int getLastPersistedBlock() { + // At start we use one block before genesis + int result = daoStateService.getGenesisBlockHeight() - 1; + String[] list = blockDir.list(); + if (list != null && list.length > 0) { + List blocks = Arrays.stream(list) + .filter(e -> !e.endsWith(".tmp")) + .map(e -> e.replace(".json", "")) + .map(Integer::valueOf) + .sorted() + .collect(Collectors.toList()); + if (!blocks.isEmpty()) { + Integer lastBlockHeight = blocks.get(blocks.size() - 1); + if (lastBlockHeight > result) { + result = lastBlockHeight; } - }, MoreExecutors.directExecutor()); + } } + return result; } private JsonBlock getJsonBlock(Block block) { diff --git a/core/src/main/java/bisq/core/dao/node/full/FullNode.java b/core/src/main/java/bisq/core/dao/node/full/FullNode.java index 91e9d41362d..f4e42dd7932 100644 --- a/core/src/main/java/bisq/core/dao/node/full/FullNode.java +++ b/core/src/main/java/bisq/core/dao/node/full/FullNode.java @@ -168,7 +168,7 @@ private void addBlockHandler() { } private void onNewBlock(Block block) { - maybeExportToJson(); + maybeExportNewBlockToJson(block); if (p2pNetworkReady && parseBlockchainComplete) fullNodeNetworkService.publishNewBlock(block); diff --git a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java index a51c5a11235..99cb779682b 100644 --- a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java +++ b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java @@ -28,6 +28,7 @@ import bisq.core.dao.node.parser.exceptions.RequiredReorgFromSnapshotException; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.DaoStateSnapshotService; +import bisq.core.dao.state.model.blockchain.Block; import bisq.network.p2p.P2PService; import bisq.network.p2p.network.Connection; @@ -39,6 +40,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import lombok.extern.slf4j.Slf4j; @@ -227,19 +229,18 @@ private void runDelayedBatchProcessing(List blocks, Runnable resultHan } // We received a new block - private void onNewBlockReceived(RawBlock block) { - int blockHeight = block.getHeight(); - log.debug("onNewBlockReceived: block at height {}, hash={}", blockHeight, block.getHash()); + private void onNewBlockReceived(RawBlock rawBlock) { + int blockHeight = rawBlock.getHeight(); + log.debug("onNewBlockReceived: block at height {}, hash={}", blockHeight, rawBlock.getHash()); // We only update chainTipHeight if we get a newer block if (blockHeight > chainTipHeight) chainTipHeight = blockHeight; try { - doParseBlock(block); + Optional optionalBlock = doParseBlock(rawBlock); + optionalBlock.ifPresent(this::maybeExportNewBlockToJson); } catch (RequiredReorgFromSnapshotException ignore) { } - - maybeExportToJson(); } } diff --git a/core/src/main/java/bisq/core/offer/OfferBookService.java b/core/src/main/java/bisq/core/offer/OfferBookService.java index 4176132baa9..66ed7ca2b35 100644 --- a/core/src/main/java/bisq/core/offer/OfferBookService.java +++ b/core/src/main/java/bisq/core/offer/OfferBookService.java @@ -245,6 +245,6 @@ private void doDumpStatistics() { }) .filter(Objects::nonNull) .collect(Collectors.toList()); - jsonFileManager.writeToDisc(Utilities.objectToJson(offerForJsonList), "offers_statistics"); + jsonFileManager.writeToDiscThreaded(Utilities.objectToJson(offerForJsonList), "offers_statistics"); } } diff --git a/core/src/main/java/bisq/core/trade/DumpDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/DumpDelayedPayoutTx.java index c407126a9db..3d2785c01e2 100644 --- a/core/src/main/java/bisq/core/trade/DumpDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/DumpDelayedPayoutTx.java @@ -58,7 +58,7 @@ public void maybeDumpDelayedPayoutTxs(TradableList trada .map(trade -> new DelayedPayoutHash(trade.getId(), Utilities.bytesAsHexString(((Trade) trade).getDelayedPayoutTxBytes()))) .collect(Collectors.toList()); - jsonFileManager.writeToDisc(Utilities.objectToJson(delayedPayoutHashes), fileName); + jsonFileManager.writeToDiscThreaded(Utilities.objectToJson(delayedPayoutHashes), fileName); } } diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java index dedc250f85a..01a978c1a9e 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -123,13 +123,13 @@ private void maybeDumpStatistics() { ArrayList fiatCurrencyList = CurrencyUtil.getAllSortedFiatCurrencies().stream() .map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8)) .collect(Collectors.toCollection(ArrayList::new)); - jsonFileManager.writeToDisc(Utilities.objectToJson(fiatCurrencyList), "fiat_currency_list"); + jsonFileManager.writeToDiscThreaded(Utilities.objectToJson(fiatCurrencyList), "fiat_currency_list"); ArrayList cryptoCurrencyList = CurrencyUtil.getAllSortedCryptoCurrencies().stream() .map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8)) .collect(Collectors.toCollection(ArrayList::new)); cryptoCurrencyList.add(0, new CurrencyTuple(Res.getBaseCurrencyCode(), Res.getBaseCurrencyName(), 8)); - jsonFileManager.writeToDisc(Utilities.objectToJson(cryptoCurrencyList), "crypto_currency_list"); + jsonFileManager.writeToDiscThreaded(Utilities.objectToJson(cryptoCurrencyList), "crypto_currency_list"); } List list = observableTradeStatisticsSet.stream() @@ -138,6 +138,6 @@ private void maybeDumpStatistics() { .collect(Collectors.toList()); TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()]; list.toArray(array); - jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics"); + jsonFileManager.writeToDiscThreaded(Utilities.objectToJson(array), "trade_statistics"); } } From 3b4d109652c361e51388d5f6d13bde75a715b47e Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 13 Oct 2020 10:26:20 -0500 Subject: [PATCH 098/123] Fix bug at mediation with old client If dispute opener is old client the delayed payout tx is not sent in mediation case (only in refund agent cases). At 1.4.0 we send it as well in mediation case and the mediator get a warning shown in case its missing. To avoid that warning we check if dispute is of refund agent type and only check in that case. This can be removed once we have enforced update to 1.4.0 (segwit will require that). Also added checks to not add null entries in the duplicates checks. --- .../bisq/core/support/dispute/Dispute.java | 2 +- .../bisq/core/trade/TradeDataValidation.java | 43 +++++++++++++------ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/bisq/core/support/dispute/Dispute.java b/core/src/main/java/bisq/core/support/dispute/Dispute.java index a622d6bc738..a9ac1ae1293 100644 --- a/core/src/main/java/bisq/core/support/dispute/Dispute.java +++ b/core/src/main/java/bisq/core/support/dispute/Dispute.java @@ -103,7 +103,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload { @Nullable private String delayedPayoutTxId; - // Added at v1.3.9 + // Added at v1.4.0 @Setter @Nullable private String donationAddressOfDelayedPayoutTx; diff --git a/core/src/main/java/bisq/core/trade/TradeDataValidation.java b/core/src/main/java/bisq/core/trade/TradeDataValidation.java index faf9529c968..1e10b75ad75 100644 --- a/core/src/main/java/bisq/core/trade/TradeDataValidation.java +++ b/core/src/main/java/bisq/core/trade/TradeDataValidation.java @@ -20,6 +20,7 @@ import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.DaoFacade; import bisq.core.offer.Offer; +import bisq.core.support.SupportType; import bisq.core.support.dispute.Dispute; import bisq.core.util.validation.RegexValidatorFactory; @@ -136,14 +137,18 @@ private static Tuple3>, Map>, Map()); - set = disputesPerDelayedPayoutTxId.get(delayedPayoutTxId); - set.add(uid); + if (delayedPayoutTxId != null) { + disputesPerDelayedPayoutTxId.putIfAbsent(delayedPayoutTxId, new HashSet<>()); + set = disputesPerDelayedPayoutTxId.get(delayedPayoutTxId); + set.add(uid); + } String depositTxId = dispute.getDepositTxId(); - disputesPerDepositTxId.putIfAbsent(depositTxId, new HashSet<>()); - set = disputesPerDepositTxId.get(depositTxId); - set.add(uid); + if (depositTxId != null) { + disputesPerDepositTxId.putIfAbsent(depositTxId, new HashSet<>()); + set = disputesPerDepositTxId.get(depositTxId); + set.add(uid); + } }); return new Tuple3<>(disputesPerTradeId, disputesPerDelayedPayoutTxId, disputesPerDepositTxId); @@ -161,8 +166,14 @@ private static void testIfDisputeTriesReplay(Dispute disputeToTest, String disputeToTestDepositTxId = disputeToTest.getDepositTxId(); String disputeToTestUid = disputeToTest.getUid(); - checkNotNull(disputeToTestDelayedPayoutTxId, - "delayedPayoutTxId must not be null. Trade ID: " + disputeToTestTradeId); + // For pre v1.4.0 we do not get the delayed payout tx sent in mediation cases but in refund agent case we do. + // So until all users have updated to 1.4.0 we only check in refund agent case. With 1.4.0 we send the + // delayed payout tx also in mediation cases and that if check can be removed. + if (disputeToTest.getSupportType() == SupportType.REFUND) { + checkNotNull(disputeToTestDelayedPayoutTxId, + "Delayed payout transaction ID is null. " + + "Trade ID: " + disputeToTestTradeId); + } checkNotNull(disputeToTestDepositTxId, "depositTxId must not be null. Trade ID: " + disputeToTestTradeId); checkNotNull(disputeToTestUid, @@ -171,12 +182,16 @@ private static void testIfDisputeTriesReplay(Dispute disputeToTest, checkArgument(disputesPerTradeId.get(disputeToTestTradeId).size() <= 2, "We found more then 2 disputes with the same trade ID. " + "Trade ID: " + disputeToTestTradeId); - checkArgument(disputesPerDelayedPayoutTxId.get(disputeToTestDelayedPayoutTxId).size() <= 2, - "We found more then 2 disputes with the same delayedPayoutTxId. " + - "Trade ID: " + disputeToTestTradeId); - checkArgument(disputesPerDepositTxId.get(disputeToTestDepositTxId).size() <= 2, - "We found more then 2 disputes with the same depositTxId. " + - "Trade ID: " + disputeToTestTradeId); + if (!disputesPerDelayedPayoutTxId.isEmpty()) { + checkArgument(disputesPerDelayedPayoutTxId.get(disputeToTestDelayedPayoutTxId).size() <= 2, + "We found more then 2 disputes with the same delayedPayoutTxId. " + + "Trade ID: " + disputeToTestTradeId); + } + if (!disputesPerDepositTxId.isEmpty()) { + checkArgument(disputesPerDepositTxId.get(disputeToTestDepositTxId).size() <= 2, + "We found more then 2 disputes with the same depositTxId. " + + "Trade ID: " + disputeToTestTradeId); + } } catch (IllegalArgumentException | NullPointerException e) { throw new DisputeReplayException(disputeToTest, e.getMessage()); From b71930252f9a60a978d08e661d78dd965d751fb8 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 13 Oct 2020 11:29:54 -0500 Subject: [PATCH 099/123] Exclude depositTxId as it seems that leads to duplicates (we got about 1000 objects more without excluding it, so seems some traders had set it to null, prob. due bugs) --- .../main/java/bisq/core/trade/statistics/TradeStatistics2.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java index 7beee9864dd..41f753bc3b6 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java @@ -87,6 +87,7 @@ public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayl // tradeDate is different for both peers so we ignore it for hash @JsonExclude private final long tradeDate; + @JsonExclude @Nullable private final String depositTxId; From 30c77aa4197f7d0251e5200dd42ab27777a866e7 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 13 Oct 2020 11:31:54 -0500 Subject: [PATCH 100/123] Update resource file. Number of objects is 24 more then with 1.3.9. Seems there are still either a few duplicate with some diverging data which should not be different or that our old code to filter duplicates had some issues. But a difference of 24 out of 75 000 object can be ignored IMO. --- .../main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET b/p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET index 97de382b59c..ea0feb1744c 100644 --- a/p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET +++ b/p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d58ed2b6b59006a13c31d32838959c3bf910430c6b88e357c967087e2f33a5c -size 3900357 +oid sha256:f2d4caac9bfd9bf4f490476520734a8b5fd16e33cb99b30a98ad59d7b9ecd635 +size 3923456 From 864700775bbd4065baabce58c0ffa403bcd75482 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Mon, 12 Oct 2020 17:23:20 -0300 Subject: [PATCH 101/123] Accept segwit addresses when sending non-BSQ funds --- .../bisq/core/btc/wallet/BsqWalletService.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java index 7fd1229cfaa..d85b8ac40ed 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java @@ -513,7 +513,7 @@ public void commitTx(Transaction tx, TxType txType) { public Transaction getPreparedSendBsqTx(String receiverAddress, Coin receiverAmount) throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException { - return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector); + return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector, false); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -522,17 +522,21 @@ public Transaction getPreparedSendBsqTx(String receiverAddress, Coin receiverAmo public Transaction getPreparedSendBtcTx(String receiverAddress, Coin receiverAmount) throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException { - return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector); + return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector, true); } - private Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount, CoinSelector coinSelector) + private Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount, CoinSelector coinSelector, + boolean allowSegwitOuput) throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException { daoKillSwitch.assertDaoIsNotDisabled(); Transaction tx = new Transaction(params); checkArgument(Restrictions.isAboveDust(receiverAmount), "The amount is too low (dust limit)."); - tx.addOutput(receiverAmount, LegacyAddress.fromBase58(params, receiverAddress)); - + if (allowSegwitOuput) { + tx.addOutput(receiverAmount, Address.fromString(params, receiverAddress)); + } else { + tx.addOutput(receiverAmount, LegacyAddress.fromBase58(params, receiverAddress)); + } SendRequest sendRequest = SendRequest.forTx(tx); sendRequest.fee = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO; From cc5bdfaf9c387c4fa6eac103569b5feee85edbd6 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 13 Oct 2020 20:31:09 -0500 Subject: [PATCH 102/123] Change data response behaviour Add DateSortedTruncatablePayload interface for TradeStatistics2 We check first if we need to truncate dateSortedTruncatablePayloads, if so we have sorted by date and truncate in the way that we receive the most recent data. We define the maxItems in the class implementing the interface (3000 for trade stats). Later we apply the maxEntries check the combined list and if we need to truncate here as well (10 000) we have added the dateSortedTruncatablePayloads at the end so those will get truncated with higher prio. There is also a bit wrong handling in the previous code that we check for max limits before the shouldTransmitPayloadToPeer filter. Should be fixed in another PR for master... --- .../trade/statistics/TradeStatistics3.java | 13 ++++- .../network/p2p/storage/P2PDataStorage.java | 56 ++++++++++++++----- .../payload/DateSortedTruncatablePayload.java | 31 ++++++++++ 3 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 p2p/src/main/java/bisq/network/p2p/storage/payload/DateSortedTruncatablePayload.java diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java index e585eff1fa1..08963dab471 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java @@ -24,6 +24,7 @@ import bisq.core.offer.OfferUtil; import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; +import bisq.network.p2p.storage.payload.DateSortedTruncatablePayload; import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload; @@ -64,7 +65,7 @@ @Slf4j @Getter public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, - CapabilityRequiringPayload { + CapabilityRequiringPayload, DateSortedTruncatablePayload { // This enum must not change the order as we use the ordinal for storage to reduce data size. // The payment method string can be quite long and would consume 15% more space. @@ -267,6 +268,16 @@ public Capabilities getRequiredCapabilities() { return new Capabilities(Capability.TRADE_STATISTICS_3); } + @Override + public Date getDate() { + return getTradeDate(); + } + + @Override + public int maxItems() { + return 3000; + } + public void pruneOptionalData() { mediator = null; refundAgent = null; diff --git a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java index 7b9e3714cea..9d6e0a705ef 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java @@ -37,6 +37,7 @@ import bisq.network.p2p.storage.messages.RemoveDataMessage; import bisq.network.p2p.storage.messages.RemoveMailboxDataMessage; import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; +import bisq.network.p2p.storage.payload.DateSortedTruncatablePayload; import bisq.network.p2p.storage.payload.DateTolerantPayload; import bisq.network.p2p.storage.payload.MailboxStoragePayload; import bisq.network.p2p.storage.payload.PersistableNetworkPayload; @@ -96,7 +97,6 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; @@ -317,30 +317,58 @@ private Map getMapForDataResponse(String r * by a given set of keys and peer capabilities. */ static private Set filterKnownHashes( - Map mapToFilter, - Function objToPayloadFunction, + Map toFilter, + Function objToPayload, Set knownHashes, Capabilities peerCapabilities, int maxEntries, - AtomicBoolean wasTruncated) { + AtomicBoolean outTruncated) { - AtomicInteger limit = new AtomicInteger(maxEntries); + log.info("Num knownHashes {}", knownHashes.size()); - Set filteredResults = mapToFilter.entrySet().stream() - .filter(e -> !knownHashes.contains(e.getKey())) - .filter(e -> limit.decrementAndGet() >= 0) + Set> entries = toFilter.entrySet(); + List dateSortedTruncatablePayloads = entries.stream() + .filter(entry -> entry.getValue() instanceof DateSortedTruncatablePayload) + .filter(entry -> !knownHashes.contains(entry.getKey())) .map(Map.Entry::getValue) - .filter(networkPayload -> shouldTransmitPayloadToPeer(peerCapabilities, - objToPayloadFunction.apply(networkPayload))) - .collect(Collectors.toSet()); + .filter(payload -> shouldTransmitPayloadToPeer(peerCapabilities, objToPayload.apply(payload))) + .sorted(Comparator.comparing(payload -> ((DateSortedTruncatablePayload) payload).getDate())) + .collect(Collectors.toList()); + log.info("Num filtered dateSortedTruncatablePayloads {}", dateSortedTruncatablePayloads.size()); + if (!dateSortedTruncatablePayloads.isEmpty()) { + int maxItems = ((DateSortedTruncatablePayload) dateSortedTruncatablePayloads.get(0)).maxItems(); + if (dateSortedTruncatablePayloads.size() > maxItems) { + int fromIndex = dateSortedTruncatablePayloads.size() - maxItems; + int toIndex = dateSortedTruncatablePayloads.size(); + dateSortedTruncatablePayloads = dateSortedTruncatablePayloads.subList(fromIndex, toIndex); + log.info("Num truncated dateSortedTruncatablePayloads {}", dateSortedTruncatablePayloads.size()); + } + } - if (limit.get() < 0) { - wasTruncated.set(true); + List filteredResults = entries.stream() + .filter(entry -> !(entry.getValue() instanceof DateSortedTruncatablePayload)) + .filter(entry -> !knownHashes.contains(entry.getKey())) + .map(Map.Entry::getValue) + .filter(payload -> shouldTransmitPayloadToPeer(peerCapabilities, objToPayload.apply(payload))) + .collect(Collectors.toList()); + log.info("Num filtered non-dateSortedTruncatablePayloads {}", filteredResults.size()); + + // The non-dateSortedTruncatablePayloads have higher prio, so we added dateSortedTruncatablePayloads + // after those so in case we need to truncate we first truncate the dateSortedTruncatablePayloads. + filteredResults.addAll(dateSortedTruncatablePayloads); + + if (filteredResults.size() > maxEntries) { + filteredResults = filteredResults.subList(0, maxEntries); + outTruncated.set(true); + log.info("Num truncated filteredResults {}", filteredResults.size()); + } else { + log.info("Num filteredResults {}", filteredResults.size()); } - return filteredResults; + return new HashSet<>(filteredResults); } + private Set getKeysAsByteSet(Map map) { return map.keySet().stream() .map(e -> e.bytes) diff --git a/p2p/src/main/java/bisq/network/p2p/storage/payload/DateSortedTruncatablePayload.java b/p2p/src/main/java/bisq/network/p2p/storage/payload/DateSortedTruncatablePayload.java new file mode 100644 index 00000000000..8e7d830a390 --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/storage/payload/DateSortedTruncatablePayload.java @@ -0,0 +1,31 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.network.p2p.storage.payload; + +import java.util.Date; + +/** + * Marker interface for PersistableNetworkPayloads which get truncated at initial data response in case we exceed + * the max items defined for that type of object. The truncation happens on a sorted list where we use the date for + * sorting so in case of truncation we prefer to receive the most recent data. + */ +public interface DateSortedTruncatablePayload extends PersistableNetworkPayload { + Date getDate(); + + int maxItems(); +} From b0bc3d0c8799338d28657a75e1140303bfc3cb2a Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 13 Oct 2020 20:46:05 -0500 Subject: [PATCH 103/123] Remove lombok getter for date and rename getDate to getDateAsLong and getTradeDate to getDate. --- .../core/dao/governance/asset/AssetService.java | 2 +- .../availability/DisputeAgentSelection.java | 2 +- .../core/provider/price/PriceFeedService.java | 2 +- .../core/trade/statistics/TradeStatistics3.java | 17 +++++++++++------ .../statistics/TradeStatisticsConverter.java | 2 +- .../statistics/TradeStatisticsForJson.java | 2 +- .../dao/economy/dashboard/BsqDashboardView.java | 13 ++++++------- .../bisq/desktop/main/market/MarketView.java | 2 +- .../main/market/trades/TradesChartsView.java | 6 +++--- .../market/trades/TradesChartsViewModel.java | 4 ++-- .../main/offer/MutableOfferDataModel.java | 4 ++-- 11 files changed, 30 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java b/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java index cc902185057..0a6843a6447 100644 --- a/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java +++ b/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java @@ -157,7 +157,7 @@ public void updateAssetStates() { .filter(e -> CurrencyUtil.isCryptoCurrency(e.getCurrency())) .forEach(e -> { lookupMap.putIfAbsent(e.getCurrency(), new ArrayList<>()); - lookupMap.get(e.getCurrency()).add(new TradeAmountDateTuple(e.getAmount(), e.getDate())); + lookupMap.get(e.getCurrency()).add(new TradeAmountDateTuple(e.getAmount(), e.getDateAsLong())); }); getStatefulAssets().stream() diff --git a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java index 8745bd3c565..eaec9dcbe96 100644 --- a/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java +++ b/core/src/main/java/bisq/core/offer/availability/DisputeAgentSelection.java @@ -63,7 +63,7 @@ private static T getLeastUsedDisputeAgent(TradeStatisti boolean isMediator) { // We take last 100 entries from trade statistics List list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); - list.sort(Comparator.comparing(TradeStatistics3::getDate)); + list.sort(Comparator.comparing(TradeStatistics3::getDateAsLong)); Collections.reverse(list); if (!list.isEmpty()) { int max = Math.min(list.size(), LOOK_BACK_RANGE); diff --git a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java index b90b9128507..44c9635b866 100644 --- a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java @@ -307,7 +307,7 @@ public void applyLatestBisqMarketPrice(Set tradeStatisticsSet) mapByCurrencyCode.values().stream() .filter(list -> !list.isEmpty()) .forEach(list -> { - list.sort(Comparator.comparing(TradeStatistics3::getTradeDate)); + list.sort(Comparator.comparing(TradeStatistics3::getDate)); TradeStatistics3 tradeStatistics = list.get(list.size() - 1); setBisqMarketPrice(tradeStatistics.getCurrency(), tradeStatistics.getTradePrice()); }); diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java index 08963dab471..0fb4032f0aa 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java @@ -63,7 +63,6 @@ * Data size is about 50 bytes in average */ @Slf4j -@Getter public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, CapabilityRequiringPayload, DateSortedTruncatablePayload { @@ -106,8 +105,11 @@ private enum PaymentMethodMapper { BLOCK_CHAINS_INSTANT } + @Getter private final String currency; + @Getter private final long price; + @Getter private final long amount; private final String paymentMethod; // As only seller is publishing it is the sellers trade date @@ -116,9 +118,11 @@ private enum PaymentMethodMapper { // Old converted trade stat objects might not have it set @Nullable @JsonExclude + @Getter private String mediator; @Nullable @JsonExclude + @Getter private String refundAgent; // todo should we add referrerId as well? get added to extra map atm but not used so far @@ -131,6 +135,7 @@ private enum PaymentMethodMapper { // field in a class would break that hash and therefore break the storage mechanism. @Nullable @JsonExclude + @Getter private final Map extraDataMap; public TradeStatistics3(String currency, @@ -270,7 +275,11 @@ public Capabilities getRequiredCapabilities() { @Override public Date getDate() { - return getTradeDate(); + return new Date(date); + } + + public long getDateAsLong() { + return date; } @Override @@ -291,10 +300,6 @@ public String getPaymentMethod() { } } - public Date getTradeDate() { - return new Date(date); - } - public Price getTradePrice() { return Price.valueOf(currency, price); } diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java index 43693badce8..f360dd5557e 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsConverter.java @@ -143,7 +143,7 @@ private static List convertToTradeStatistics3(Collection DisputeAgentSelection.LOOK_BACK_RANGE) { int start = size - DisputeAgentSelection.LOOK_BACK_RANGE; for (int i = start; i < size; i++) { diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java index ebff70616be..34fde08f839 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsForJson.java @@ -55,7 +55,7 @@ public TradeStatisticsForJson(TradeStatistics3 tradeStatistics) { this.paymentMethod = tradeStatistics.getPaymentMethod(); this.tradePrice = tradeStatistics.getPrice(); this.tradeAmount = tradeStatistics.getAmount(); - this.tradeDate = tradeStatistics.getDate(); + this.tradeDate = tradeStatistics.getDateAsLong(); try { Price tradePrice = getTradePrice(); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java index ed0257e0449..4e5bea571ca 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java @@ -20,7 +20,6 @@ import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.TextFieldWithIcon; -import bisq.desktop.util.FormBuilder; import bisq.core.dao.DaoFacade; import bisq.core.dao.state.DaoStateListener; @@ -291,8 +290,8 @@ private void updateBsqPriceData() { Map> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() .filter(e -> e.getCurrency().equals("BSQ")) - .sorted(Comparator.comparing(TradeStatistics3::getDate)) - .collect(Collectors.groupingBy(item -> new java.sql.Date(item.getDate()).toLocalDate() + .sorted(Comparator.comparing(TradeStatistics3::getDateAsLong)) + .collect(Collectors.groupingBy(item -> new java.sql.Date(item.getDateAsLong()).toLocalDate() .with(ADJUSTERS.get(DAY)))); List> updatedBSQPrice = bsqPriceByDate.keySet().stream() @@ -372,11 +371,11 @@ private long updateAveragePriceField(TextField textField, int days, boolean isUS Date pastXDays = getPastDate(days); List bsqTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() .filter(e -> e.getCurrency().equals("BSQ")) - .filter(e -> e.getTradeDate().after(pastXDays)) + .filter(e -> e.getDate().after(pastXDays)) .collect(Collectors.toList()); List usdTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() .filter(e -> e.getCurrency().equals("USD")) - .filter(e -> e.getTradeDate().after(pastXDays)) + .filter(e -> e.getDate().after(pastXDays)) .collect(Collectors.toList()); long average = isUSDField ? getUSDAverage(bsqTradePastXDays, usdTradePastXDays) : getBTCAverage(bsqTradePastXDays); @@ -410,13 +409,13 @@ private long getUSDAverage(List bsqList, List> usdBsqList = new ArrayList<>(bsqList.size()); - usdList.sort(Comparator.comparing(TradeStatistics3::getDate)); + usdList.sort(Comparator.comparing(TradeStatistics3::getDateAsLong)); var usdBTCPrice = 10000d; // Default to 10000 USD per BTC if there is no USD feed at all for (TradeStatistics3 item : bsqList) { // Find usdprice for trade item usdBTCPrice = usdList.stream() - .filter(usd -> usd.getDate() > item.getDate()) + .filter(usd -> usd.getDateAsLong() > item.getDateAsLong()) .map(usd -> MathUtils.scaleDownByPowerOf10((double) usd.getTradePrice().getValue(), Fiat.SMALLEST_UNIT_EXPONENT)) .findFirst() diff --git a/desktop/src/main/java/bisq/desktop/main/market/MarketView.java b/desktop/src/main/java/bisq/desktop/main/market/MarketView.java index 9a3544e06d0..7220952576b 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/MarketView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/MarketView.java @@ -188,7 +188,7 @@ private String getAllTradesWithReferralId() { .filter(tradeStatistics3 -> tradeStatistics3.getExtraDataMap().get(OfferPayload.REFERRAL_ID) != null) .map(tradeStatistics3 -> { StringBuilder sb = new StringBuilder(); - sb.append("Date: ").append(DisplayUtils.formatDateTime(tradeStatistics3.getTradeDate())).append("\n") + sb.append("Date: ").append(DisplayUtils.formatDateTime(tradeStatistics3.getDate())).append("\n") .append("Market: ").append(CurrencyUtil.getCurrencyPair(tradeStatistics3.getCurrency())).append("\n") .append("Price: ").append(FormattingUtils.formatPrice(tradeStatistics3.getTradePrice())).append("\n") .append("Amount: ").append(formatter.formatCoin(tradeStatistics3.getTradeAmount())).append("\n") diff --git a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java index 4a70b96ff43..604d572f47d 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java @@ -567,14 +567,14 @@ public TableCell call( public void updateItem(final TradeStatistics3 item, boolean empty) { super.updateItem(item, empty); if (item != null) - setText(DisplayUtils.formatDateTime(item.getTradeDate())); + setText(DisplayUtils.formatDateTime(item.getDate())); else setText(""); } }; } }); - dateColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeDate)); + dateColumn.setComparator(Comparator.comparing(TradeStatistics3::getDate)); tableView.getColumns().add(dateColumn); // market @@ -603,7 +603,7 @@ public void updateItem(final TradeStatistics3 item, boolean empty) { }; } }); - marketColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeDate)); + marketColumn.setComparator(Comparator.comparing(TradeStatistics3::getDate)); tableView.getColumns().add(marketColumn); // price diff --git a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java index 3bf125ad897..831f5551a64 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java @@ -260,7 +260,7 @@ private void updateChartData() { tradeStatisticsByCurrency.forEach(e -> { for (long i = maxTicks; i > 0; --i) { Pair> p = itemsPerInterval.get(i); - if (e.getTradeDate().after(p.getKey())) { + if (e.getDate().after(p.getKey())) { p.getValue().add(e); break; } @@ -307,7 +307,7 @@ CandleData getCandleData(long tick, Set set) { Collections.sort(tradePrices); List list = new ArrayList<>(set); - list.sort(Comparator.comparingLong(TradeStatistics3::getDate)); + list.sort(Comparator.comparingLong(TradeStatistics3::getDateAsLong)); if (list.size() > 0) { open = list.get(0).getTradePrice().getValue(); close = list.get(list.size() - 1).getTradePrice().getValue(); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index 62869cd54e7..d035956cc73 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -345,8 +345,8 @@ private void setSuggestedSecurityDeposit(PaymentAccount paymentAccount) { var startDate = new Date(System.currentTimeMillis() - blocksRange * 10 * 60000); var sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() .filter(e -> e.getCurrency().equals(getTradeCurrency().getCode())) - .filter(e -> e.getTradeDate().compareTo(startDate) >= 0) - .sorted(Comparator.comparing(TradeStatistics3::getTradeDate)) + .filter(e -> e.getDate().compareTo(startDate) >= 0) + .sorted(Comparator.comparing(TradeStatistics3::getDate)) .collect(Collectors.toList()); var movingAverage = new MathUtils.MovingAverage(10, 0.2); double[] extremes = {Double.MAX_VALUE, Double.MIN_VALUE}; From 5f9d3d1f0dc977beb4893a73a6f29683625bbf86 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 13 Oct 2020 23:30:39 -0500 Subject: [PATCH 104/123] Add GetInventory messages This will be used for monitoring seed nodes. Instead of requesting all data (we cannot request all in fact as it is too large) we request the number of items the node has. This code will not have any impact atm. It will be triggered once a new monitor module gets added which will send the GetInventoryRequest to the seeds. --- .../network/CoreNetworkProtoResolver.java | 7 ++ .../java/bisq/network/p2p/P2PService.java | 13 ++- .../inventory/GetInventoryRequestHandler.java | 81 +++++++++++++++++++ .../inventory/GetInventoryRequestManager.java | 61 ++++++++++++++ .../p2p/inventory/GetInventoryRequester.java | 66 +++++++++++++++ .../messages/GetInventoryRequest.java | 55 +++++++++++++ .../messages/GetInventoryResponse.java | 56 +++++++++++++ .../network/p2p/storage/P2PDataStorage.java | 2 +- proto/src/main/proto/pb.proto | 13 +++ 9 files changed, 352 insertions(+), 2 deletions(-) create mode 100644 p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestHandler.java create mode 100644 p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java create mode 100644 p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java create mode 100644 p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryRequest.java create mode 100644 p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryResponse.java diff --git a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java index 675011cec3d..afa177c6733 100644 --- a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java @@ -64,6 +64,8 @@ import bisq.network.p2p.BundleOfEnvelopes; import bisq.network.p2p.CloseConnectionMessage; import bisq.network.p2p.PrefixedSealedAndSignedMessage; +import bisq.network.p2p.inventory.messages.GetInventoryRequest; +import bisq.network.p2p.inventory.messages.GetInventoryResponse; import bisq.network.p2p.peers.getdata.messages.GetDataResponse; import bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest; import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest; @@ -224,6 +226,11 @@ public NetworkEnvelope fromProto(protobuf.NetworkEnvelope proto) throws Protobuf case BUNDLE_OF_ENVELOPES: return BundleOfEnvelopes.fromProto(proto.getBundleOfEnvelopes(), this, messageVersion); + case GET_INVENTORY_REQUEST: + return GetInventoryRequest.fromProto(proto.getGetInventoryRequest(), messageVersion); + case GET_INVENTORY_RESPONSE: + return GetInventoryResponse.fromProto(proto.getGetInventoryResponse(), messageVersion); + default: throw new ProtobufferException("Unknown proto message case (PB.NetworkEnvelope). messageCase=" + proto.getMessageCase() + "; proto raw data=" + proto.toString()); diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 8716c81d9d1..76396125462 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -19,6 +19,8 @@ import bisq.network.Socks5ProxyProvider; import bisq.network.crypto.EncryptionService; +import bisq.network.p2p.inventory.GetInventoryRequestHandler; +import bisq.network.p2p.inventory.GetInventoryRequestManager; import bisq.network.p2p.messaging.DecryptedMailboxListener; import bisq.network.p2p.network.CloseConnectionReason; import bisq.network.p2p.network.Connection; @@ -111,6 +113,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis private final SeedNodeRepository seedNodeRepository; private final EncryptionService encryptionService; private final KeyRing keyRing; + private final GetInventoryRequestHandler getInventoryRequestHandler; + private final GetInventoryRequestManager getInventoryRequestManager; private final NetworkNode networkNode; private final PeerManager peerManager; @@ -157,7 +161,9 @@ public P2PService(NetworkNode networkNode, SeedNodeRepository seedNodeRepository, Socks5ProxyProvider socks5ProxyProvider, EncryptionService encryptionService, - KeyRing keyRing) { + KeyRing keyRing, + GetInventoryRequestHandler getInventoryRequestHandler, + GetInventoryRequestManager getInventoryRequestManager) { this.networkNode = networkNode; this.peerManager = peerManager; this.p2PDataStorage = p2PDataStorage; @@ -169,6 +175,8 @@ public P2PService(NetworkNode networkNode, this.socks5ProxyProvider = socks5ProxyProvider; this.encryptionService = encryptionService; this.keyRing = keyRing; + this.getInventoryRequestHandler = getInventoryRequestHandler; + this.getInventoryRequestManager = getInventoryRequestManager; this.networkNode.addConnectionListener(this); this.networkNode.addMessageListener(this); @@ -259,6 +267,9 @@ private void doShutDown() { } else { shutDownResultHandlers.forEach(Runnable::run); } + + getInventoryRequestHandler.shutDown(); + getInventoryRequestManager.shutDown(); } diff --git a/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestHandler.java b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestHandler.java new file mode 100644 index 00000000000..8695e71b37e --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestHandler.java @@ -0,0 +1,81 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.network.p2p.inventory; + +import bisq.network.p2p.inventory.messages.GetInventoryRequest; +import bisq.network.p2p.inventory.messages.GetInventoryResponse; +import bisq.network.p2p.network.Connection; +import bisq.network.p2p.network.MessageListener; +import bisq.network.p2p.network.NetworkNode; +import bisq.network.p2p.storage.P2PDataStorage; +import bisq.network.p2p.storage.payload.ProtectedStorageEntry; + +import bisq.common.proto.network.NetworkEnvelope; + +import javax.inject.Inject; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class GetInventoryRequestHandler implements MessageListener { + private final NetworkNode networkNode; + private final P2PDataStorage p2PDataStorage; + + @Inject + public GetInventoryRequestHandler(NetworkNode networkNode, P2PDataStorage p2PDataStorage) { + this.networkNode = networkNode; + this.p2PDataStorage = p2PDataStorage; + networkNode.addMessageListener(this); + } + + @Override + public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { + if (networkEnvelope instanceof GetInventoryRequest) { + GetInventoryRequest getInventoryRequest = (GetInventoryRequest) networkEnvelope; + + Map numPayloadsByClassName = new HashMap<>(); + p2PDataStorage.getMapForDataResponse(getInventoryRequest.getVersion()).values().stream() + .map(e -> e.getClass().getSimpleName()) + .forEach(className -> { + numPayloadsByClassName.putIfAbsent(className, 0); + int prev = numPayloadsByClassName.get(className); + numPayloadsByClassName.put(className, prev + 1); + }); + p2PDataStorage.getMap().values().stream() + .map(ProtectedStorageEntry::getProtectedStoragePayload) + .filter(Objects::nonNull) + .map(e -> e.getClass().getSimpleName()) + .forEach(className -> { + numPayloadsByClassName.putIfAbsent(className, 0); + int prev = numPayloadsByClassName.get(className); + numPayloadsByClassName.put(className, prev + 1); + }); + + GetInventoryResponse getInventoryResponse = new GetInventoryResponse(numPayloadsByClassName); + networkNode.sendMessage(connection, getInventoryResponse); + } + } + + public void shutDown() { + networkNode.removeMessageListener(this); + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java new file mode 100644 index 00000000000..dcf88b463af --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java @@ -0,0 +1,61 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.network.p2p.inventory; + +import bisq.network.p2p.NodeAddress; +import bisq.network.p2p.network.NetworkNode; + +import javax.inject.Inject; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class GetInventoryRequestManager { + private final NetworkNode networkNode; + private final Map requesterMap = new HashMap<>(); + + @Inject + public GetInventoryRequestManager(NetworkNode networkNode) { + this.networkNode = networkNode; + } + + public void request(NodeAddress nodeAddress, Consumer> resultHandler) { + if (requesterMap.containsKey(nodeAddress)) { + log.warn("There is already an open request pending"); + return; + } + + GetInventoryRequester getInventoryRequester = new GetInventoryRequester(networkNode, + nodeAddress, + resultMap -> { + requesterMap.remove(nodeAddress); + resultHandler.accept(resultMap); + }); + requesterMap.put(nodeAddress, getInventoryRequester); + getInventoryRequester.request(); + } + + public void shutDown() { + requesterMap.values().forEach(GetInventoryRequester::shutDown); + requesterMap.clear(); + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java new file mode 100644 index 00000000000..df073675262 --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java @@ -0,0 +1,66 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.network.p2p.inventory; + +import bisq.network.p2p.NodeAddress; +import bisq.network.p2p.inventory.messages.GetInventoryRequest; +import bisq.network.p2p.inventory.messages.GetInventoryResponse; +import bisq.network.p2p.network.Connection; +import bisq.network.p2p.network.MessageListener; +import bisq.network.p2p.network.NetworkNode; + +import bisq.common.app.Version; +import bisq.common.proto.network.NetworkEnvelope; + +import java.util.Map; +import java.util.function.Consumer; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class GetInventoryRequester implements MessageListener { + private final NetworkNode networkNode; + private final NodeAddress nodeAddress; + private final Consumer> resultHandler; + + public GetInventoryRequester(NetworkNode networkNode, + NodeAddress nodeAddress, + Consumer> resultHandler) { + this.networkNode = networkNode; + this.nodeAddress = nodeAddress; + this.resultHandler = resultHandler; + networkNode.addMessageListener(this); + } + + public void request() { + networkNode.sendMessage(nodeAddress, new GetInventoryRequest(Version.VERSION)); + } + + @Override + public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { + if (networkEnvelope instanceof GetInventoryResponse) { + GetInventoryResponse getInventoryResponse = (GetInventoryResponse) networkEnvelope; + resultHandler.accept(getInventoryResponse.getNumPayloadsMap()); + shutDown(); + } + } + + public void shutDown() { + networkNode.removeMessageListener(this); + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryRequest.java b/p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryRequest.java new file mode 100644 index 00000000000..dc26d377742 --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryRequest.java @@ -0,0 +1,55 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.network.p2p.inventory.messages; + + +import bisq.common.app.Version; +import bisq.common.proto.network.NetworkEnvelope; + +import lombok.Value; + +@Value +public class GetInventoryRequest extends NetworkEnvelope { + private final String version; + + public GetInventoryRequest(String version) { + this(version, Version.getP2PMessageVersion()); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private GetInventoryRequest(String version, int messageVersion) { + super(messageVersion); + + this.version = version; + } + + @Override + public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { + return getNetworkEnvelopeBuilder() + .setGetInventoryRequest(protobuf.GetInventoryRequest.newBuilder() + .setVersion(version)) + .build(); + } + + public static GetInventoryRequest fromProto(protobuf.GetInventoryRequest proto, int messageVersion) { + return new GetInventoryRequest(proto.getVersion(), messageVersion); + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryResponse.java b/p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryResponse.java new file mode 100644 index 00000000000..45b5fbf9929 --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/inventory/messages/GetInventoryResponse.java @@ -0,0 +1,56 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.network.p2p.inventory.messages; + +import bisq.common.app.Version; +import bisq.common.proto.network.NetworkEnvelope; + +import java.util.Map; + +import lombok.Value; + +@Value +public class GetInventoryResponse extends NetworkEnvelope { + private final Map numPayloadsMap; + + public GetInventoryResponse(Map numPayloadsMap) { + this(numPayloadsMap, Version.getP2PMessageVersion()); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private GetInventoryResponse(Map numPayloadsMap, int messageVersion) { + super(messageVersion); + + this.numPayloadsMap = numPayloadsMap; + } + + @Override + public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { + return getNetworkEnvelopeBuilder() + .setGetInventoryResponse(protobuf.GetInventoryResponse.newBuilder() + .putAllNumPayloadsMap(numPayloadsMap)) + .build(); + } + + public static GetInventoryResponse fromProto(protobuf.GetInventoryResponse proto, int messageVersion) { + return new GetInventoryResponse(proto.getNumPayloadsMapMap(), messageVersion); + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java index 7b9e3714cea..5453c42723a 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java @@ -294,7 +294,7 @@ private Map getMapForDataRequest() { return map; } - private Map getMapForDataResponse(String requestersVersion) { + public Map getMapForDataResponse(String requestersVersion) { Map map = new HashMap<>(); appendOnlyDataStoreService.getServices() .forEach(service -> { diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 8d7b152bd27..8323a58c4ac 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -78,6 +78,9 @@ message NetworkEnvelope { RefreshTradeStateRequest refresh_trade_state_request = 50 [deprecated = true]; TraderSignedWitnessMessage trader_signed_witness_message = 51 [deprecated = true]; + + GetInventoryRequest get_inventory_request = 52; + GetInventoryResponse get_inventory_response = 53; } } @@ -137,6 +140,16 @@ message Pong { int32 request_nonce = 1; } +// Inventory + +message GetInventoryRequest { + string version = 1; +} + +message GetInventoryResponse { + map num_payloads_map = 1; +} + // offer message OfferAvailabilityRequest { From 32b953b61aeb01cbf949d5ffd2c17c2064344081 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 14 Oct 2020 02:37:00 -0500 Subject: [PATCH 105/123] Improve GetInventoryRequester and GetInventoryRequestManager --- .../inventory/GetInventoryRequestManager.java | 12 +++++-- .../p2p/inventory/GetInventoryRequester.java | 33 ++++++++++++++++--- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java index dcf88b463af..755f2912c03 100644 --- a/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java +++ b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequestManager.java @@ -20,6 +20,8 @@ import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.NetworkNode; +import bisq.common.handlers.ErrorMessageHandler; + import javax.inject.Inject; import java.util.HashMap; @@ -38,9 +40,11 @@ public GetInventoryRequestManager(NetworkNode networkNode) { this.networkNode = networkNode; } - public void request(NodeAddress nodeAddress, Consumer> resultHandler) { + public void request(NodeAddress nodeAddress, + Consumer> resultHandler, + ErrorMessageHandler errorMessageHandler) { if (requesterMap.containsKey(nodeAddress)) { - log.warn("There is already an open request pending"); + log.warn("There is still an open request pending for {}", nodeAddress.getFullAddress()); return; } @@ -49,6 +53,10 @@ public void request(NodeAddress nodeAddress, Consumer> resu resultMap -> { requesterMap.remove(nodeAddress); resultHandler.accept(resultMap); + }, + errorMessage -> { + requesterMap.remove(nodeAddress); + errorMessageHandler.handleErrorMessage(errorMessage); }); requesterMap.put(nodeAddress, getInventoryRequester); getInventoryRequester.request(); diff --git a/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java index df073675262..b43f1d37893 100644 --- a/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java +++ b/p2p/src/main/java/bisq/network/p2p/inventory/GetInventoryRequester.java @@ -24,7 +24,10 @@ import bisq.network.p2p.network.MessageListener; import bisq.network.p2p.network.NetworkNode; +import bisq.common.Timer; +import bisq.common.UserThread; import bisq.common.app.Version; +import bisq.common.handlers.ErrorMessageHandler; import bisq.common.proto.network.NetworkEnvelope; import java.util.Map; @@ -34,33 +37,53 @@ @Slf4j public class GetInventoryRequester implements MessageListener { + private final static int TIMEOUT_SEC = 90; + private final NetworkNode networkNode; private final NodeAddress nodeAddress; private final Consumer> resultHandler; + private final ErrorMessageHandler errorMessageHandler; + private Timer timer; public GetInventoryRequester(NetworkNode networkNode, NodeAddress nodeAddress, - Consumer> resultHandler) { + Consumer> resultHandler, + ErrorMessageHandler errorMessageHandler) { this.networkNode = networkNode; this.nodeAddress = nodeAddress; this.resultHandler = resultHandler; - networkNode.addMessageListener(this); + this.errorMessageHandler = errorMessageHandler; } public void request() { + networkNode.addMessageListener(this); + timer = UserThread.runAfter(this::onTimeOut, TIMEOUT_SEC); networkNode.sendMessage(nodeAddress, new GetInventoryRequest(Version.VERSION)); } + private void onTimeOut() { + errorMessageHandler.handleErrorMessage("Timeout got triggered (" + TIMEOUT_SEC + " sec)"); + shutDown(); + } + @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetInventoryResponse) { - GetInventoryResponse getInventoryResponse = (GetInventoryResponse) networkEnvelope; - resultHandler.accept(getInventoryResponse.getNumPayloadsMap()); - shutDown(); + connection.getPeersNodeAddressOptional().ifPresent(peer -> { + if (peer.equals(nodeAddress)) { + GetInventoryResponse getInventoryResponse = (GetInventoryResponse) networkEnvelope; + resultHandler.accept(getInventoryResponse.getNumPayloadsMap()); + shutDown(); + } + }); } } public void shutDown() { + if (timer != null) { + timer.stop(); + timer = null; + } networkNode.removeMessageListener(this); } } From 866b227fcc3f6ab2f2bc74f36d01727f7b4a7573 Mon Sep 17 00:00:00 2001 From: wiz Date: Wed, 14 Oct 2020 18:24:19 +0900 Subject: [PATCH 106/123] Replace emzy's v2 seednodes with new v3 seednodes --- core/src/main/resources/btc_mainnet.seednodes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/btc_mainnet.seednodes b/core/src/main/resources/btc_mainnet.seednodes index 759abeee7a4..5f44ef5b753 100644 --- a/core/src/main/resources/btc_mainnet.seednodes +++ b/core/src/main/resources/btc_mainnet.seednodes @@ -1,7 +1,5 @@ # nodeaddress.onion:port [(@owner,@backup)] 5quyxpxheyvzmb2d.onion:8000 (@miker) -s67qglwhkgkyvr74.onion:8000 (@emzy) -723ljisnynbtdohi.onion:8000 (@emzy) rm7b56wbrcczpjvl.onion:8000 (@miker) wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000 (@wiz) wizseed3d376esppbmbjxk2fhk2jg5fpucddrzj2kxtbxbx4vrnwclad.onion:8000 (@wiz) @@ -10,4 +8,6 @@ devinv3rhon24gqf5v6ondoqgyrbzyqihzyouzv7ptltsewhfmox2zqd.onion:8000 (@devinbilec devinsn2teu33efff62bnvwbxmfgbfjlgqsu3ad4b4fudx3a725eqnyd.onion:8000 (@devinbileck) devinsn3xuzxhj6pmammrxpydhwwmwp75qkksedo5dn2tlmu7jggo7id.onion:8000 (@devinbileck) sn3emzy56u3mxzsr4geysc52feoq5qt7ja56km6gygwnszkshunn2sid.onion:8000 (@emzy) +sn4emzywye3dhjouv7jig677qepg7fnusjidw74fbwneieruhmi7fuyd.onion:8000 (@emzy) +sn5emzyvxuildv34n6jewfp2zeota4aq63fsl5yyilnvksezr3htveqd.onion:8000 (@emzy) sn2bisqad7ncazupgbd3dcedqh5ptirgwofw63djwpdtftwhddo75oid.onion:8000 (@miker) From 58f5066e0ac702fcc0f0eee3eeb3c529915cd4ee Mon Sep 17 00:00:00 2001 From: Mike Rosseel Date: Wed, 14 Oct 2020 13:54:22 +0200 Subject: [PATCH 107/123] Resolve conflict in 'core/src/main/resources/btc_mainnet.seednodes'. --- core/src/main/resources/btc_mainnet.seednodes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/btc_mainnet.seednodes b/core/src/main/resources/btc_mainnet.seednodes index 5f44ef5b753..f1ca4754faa 100644 --- a/core/src/main/resources/btc_mainnet.seednodes +++ b/core/src/main/resources/btc_mainnet.seednodes @@ -1,6 +1,4 @@ # nodeaddress.onion:port [(@owner,@backup)] -5quyxpxheyvzmb2d.onion:8000 (@miker) -rm7b56wbrcczpjvl.onion:8000 (@miker) wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000 (@wiz) wizseed3d376esppbmbjxk2fhk2jg5fpucddrzj2kxtbxbx4vrnwclad.onion:8000 (@wiz) wizseed7ab2gi3x267xahrp2pkndyrovczezzb46jk6quvguciuyqrid.onion:8000 (@wiz) @@ -11,3 +9,5 @@ sn3emzy56u3mxzsr4geysc52feoq5qt7ja56km6gygwnszkshunn2sid.onion:8000 (@emzy) sn4emzywye3dhjouv7jig677qepg7fnusjidw74fbwneieruhmi7fuyd.onion:8000 (@emzy) sn5emzyvxuildv34n6jewfp2zeota4aq63fsl5yyilnvksezr3htveqd.onion:8000 (@emzy) sn2bisqad7ncazupgbd3dcedqh5ptirgwofw63djwpdtftwhddo75oid.onion:8000 (@miker) +sn3bsq3evqkpshdmc3sbdxafkhfnk7ctop44jsxbxyys5ridsaw5abyd.onion:8000 (@miker) +sn4bsqpc7eb2ntvpsycxbzqt6fre72l4krp2fl5svphfh2eusrqtq3qd.onion:8000 (@miker) From feb4e5230f31a3dcd9edbbcd3bfb9d5af7c4d066 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 14 Oct 2020 09:52:53 -0500 Subject: [PATCH 108/123] Use toString for NullPointerException --- core/src/main/java/bisq/core/trade/TradeDataValidation.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/trade/TradeDataValidation.java b/core/src/main/java/bisq/core/trade/TradeDataValidation.java index 1e10b75ad75..ef4665a502c 100644 --- a/core/src/main/java/bisq/core/trade/TradeDataValidation.java +++ b/core/src/main/java/bisq/core/trade/TradeDataValidation.java @@ -193,8 +193,10 @@ private static void testIfDisputeTriesReplay(Dispute disputeToTest, "Trade ID: " + disputeToTestTradeId); } - } catch (IllegalArgumentException | NullPointerException e) { + } catch (IllegalArgumentException e) { throw new DisputeReplayException(disputeToTest, e.getMessage()); + } catch (NullPointerException e) { + throw new DisputeReplayException(disputeToTest, e.toString()); } } From fc875240ab2fab0be0e56c5f8e9b479ed8232c70 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 14 Oct 2020 09:59:10 -0500 Subject: [PATCH 109/123] Add dont show again check box to validation exception popups --- .../dispute/agent/DisputeAgentView.java | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java index b3111962fb4..7d4e6d75da1 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/DisputeAgentView.java @@ -62,6 +62,8 @@ import java.util.List; +import org.jetbrains.annotations.NotNull; + import static bisq.core.trade.TradeDataValidation.ValidationException; import static bisq.desktop.util.FormBuilder.getIconForLabel; @@ -134,19 +136,36 @@ protected void showWarningForValidationExceptions(List ex.getDispute() != null) .filter(ex -> !ex.getDispute().isClosed()) // we show warnings only for open cases - .forEach(ex -> { - Dispute dispute = ex.getDispute(); - if (ex instanceof TradeDataValidation.AddressException) { - new Popup().width(900).warning(Res.get("support.warning.disputesWithInvalidDonationAddress", - dispute.getDonationAddressOfDelayedPayoutTx(), - daoFacade.getAllDonationAddresses(), - dispute.getTradeId(), - "")) - .show(); - } else { - new Popup().width(900).warning(ex.getMessage()).show(); - } - }); + .filter(ex -> DontShowAgainLookup.showAgain(getKey(ex))) + .forEach(ex -> new Popup().width(900).warning(getValidationExceptionMessage(ex)).dontShowAgainId(getKey(ex)).show()); + } + + private String getKey(ValidationException exception) { + Dispute dispute = exception.getDispute(); + if (dispute != null) { + return "ValExcPopup-" + dispute.getTradeId() + "-" + dispute.getTraderId(); + } + return "ValExcPopup-" + exception.toString(); + } + + private String getValidationExceptionMessage(ValidationException exception) { + Dispute dispute = exception.getDispute(); + if (dispute != null && exception instanceof TradeDataValidation.AddressException) { + return getAddressExceptionMessage(dispute); + } else if (exception.getMessage() != null && !exception.getMessage().isEmpty()) { + return exception.getMessage(); + } else { + return exception.toString(); + } + } + + @NotNull + private String getAddressExceptionMessage(Dispute dispute) { + return Res.get("support.warning.disputesWithInvalidDonationAddress", + dispute.getDonationAddressOfDelayedPayoutTx(), + daoFacade.getAllDonationAddresses(), + dispute.getTradeId(), + ""); } @Override From fe3828e21f6f64c9e9cf26110babd7745a745547 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 14 Oct 2020 10:13:12 -0500 Subject: [PATCH 110/123] Dont include dead transactions in check for unconfirmed txs chain --- core/src/main/java/bisq/core/btc/wallet/WalletService.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index 920df501e82..7ef84c7c6a6 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -62,12 +62,9 @@ import org.bitcoinj.wallet.DecryptingKeyBag; import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.KeyBag; -import org.bitcoinj.wallet.KeyChain; import org.bitcoinj.wallet.RedeemData; import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; -import org.bitcoinj.wallet.listeners.KeyChainEventListener; -import org.bitcoinj.wallet.listeners.ScriptsChangeEventListener; import org.bitcoinj.wallet.listeners.WalletChangeEventListener; import org.bitcoinj.wallet.listeners.WalletCoinsReceivedEventListener; import org.bitcoinj.wallet.listeners.WalletCoinsSentEventListener; @@ -635,7 +632,9 @@ public int getLastBlockSeenHeight() { * @return true when queue is full */ public boolean isUnconfirmedTransactionsLimitHit() { - return 20 < getTransactions(true).stream().filter(transaction -> transaction.isPending()).count(); + return 20 < getTransactions(false).stream() + .filter(Transaction::isPending) + .count(); } public Set getTransactions(boolean includeDead) { From 8b404e195459c1a34cc9dd2151c96a2e53a2be7e Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 14 Oct 2020 20:17:48 -0500 Subject: [PATCH 111/123] Exclude time-locked txs at isUnconfirmedTransactionsLimitHit For published delayed payout transactions we do not receive the tx confidence so we cannot check if it is confirmed so we ignore it for that check. The check is any arbitrarily using a limit of 20, so we don't need to be exact here. Should just reduce the likelihood of issues with the too long chains of unconfirmed transactions. --- .../main/java/bisq/core/btc/wallet/WalletService.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index 7ef84c7c6a6..251aac97172 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -632,9 +632,14 @@ public int getLastBlockSeenHeight() { * @return true when queue is full */ public boolean isUnconfirmedTransactionsLimitHit() { - return 20 < getTransactions(false).stream() + // For published delayed payout transactions we do not receive the tx confidence + // so we cannot check if it is confirmed so we ignore it for that check. The check is any arbitrarily + // using a limit of 20, so we don't need to be exact here. Should just reduce the likelihood of issues with + // the too long chains of unconfirmed transactions. + return getTransactions(false).stream() + .filter(tx -> tx.getLockTime() == 0) .filter(Transaction::isPending) - .count(); + .count() > 20; } public Set getTransactions(boolean includeDead) { From b7c6c424754b2680785420fbb70188e50a839761 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 14 Oct 2020 15:39:21 +0200 Subject: [PATCH 112/123] Update bitcoinj checkpoints for v1.4.0 --- core/src/main/resources/wallet/checkpoints.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/wallet/checkpoints.txt b/core/src/main/resources/wallet/checkpoints.txt index 99bb07622a8..f38809740f9 100644 --- a/core/src/main/resources/wallet/checkpoints.txt +++ b/core/src/main/resources/wallet/checkpoints.txt @@ -1,6 +1,6 @@ TXT CHECKPOINTS 1 0 -317 +323 AAAAAAAAB+EH4QfhAAAH4AEAAABjl7tqvU/FIcDT9gcbVlA4nwtFUbxAtOawZzBpAAAAAKzkcK7NqciBjI/ldojNKncrWleVSgDfBCCn3VRrbSxXaw5/Sf//AB0z8Bkv AAAAAAAAD8EPwQ/BAAAPwAEAAADfP83Sx8MZ9RsrnZCvqzAwqB2Ma+ZesNAJrTfwAAAAACwESaNKhvRgz6WuE7UFdFk1xwzfRY/OIdIOPzX5yaAdjnWUSf//AB0GrNq5 AAAAAAAAF6EXoRehAAAXoAEAAADonWzAaUAKd30XT3NnHKobZMnLOuHdzm/xtehsAAAAAD8cUJA6NBIHHcqPHLc4IrfHw+6mjCGu3e+wRO81EvpnMVqrSf//AB1ffy8G @@ -318,3 +318,9 @@ EAgacoNqeblmwryDAAmowAAAACBrw4jKPLZgZpVWF50edwCXFDSCydrEBwAAAAAAAAAAAJzD7FqR8uJd EGp3iH6JaGGCuz8LAAmwoAAAQCAgBboCfncZ/s4WkKcsLjW+V4vlPQoDAwAAAAAAAAAAALs0nxXJbP7VZ+Fc3dhRpL6RN6QfYphMw8iCNdqEKGL6e/joXvLUERcyQVAj ENuF9IkxgIodMy/3AAm4gAAAACCTOuNmt+7x2vxFMkHRbDxRElZiABP9DAAAAAAAAAAAAAERAwV0KNK6qPCLxwDpQCkrbHi2jFc/Kldv0N5pJMkE+XP7XhnVERet5rYy EUyU1REypM3OngUzAAnAYAAAgCB898coFK4fvpBHu54gmeu1eEuspbAwCAAAAAAAAAAAAHQ93L5j0nbFj5FI6biHU0sUuxN+qzGCaK4mc2P3nQYZpEoMXxU6EBeWCMCJ +EcjRYNcojJYAgaG7AAnIQAAAACCjAhx+1E7mE4bleOFpzqtEG3mGLs/VDgAAAAAAAAAAAFr7gwTRRSFysbOK9K7JXVQxXaq23POoYGxre0mJgYM5pEwfX/i0EBckQiPZ +EkF8pCJpsBtpkPH6AAnQIADg/z9tCoMocT196c1IBJkoKNhgXJT3k1ysCgAAAAAAAAAAAPESYEteACbYsgdEuoUXLrDqFqut46D/RLAoOw+IITSkU6cxX6ybEBdqMa4t +ErrgKoeevYMpfx+WAAnYAADg/y/Zjrsqarpkd5PIhR21HJ55cSMyymaaBAAAAAAAAAAAAKPhdir1YiPGjqsC309lxumCEY8aSu2HOTrVU6Ihc4oti3tDX+oHEBeswM1c +EzihxS/9MDJCHTIfAAnf4AAAQCAGq48tARXjK5m5ygIMjtFJqlySqsdXBgAAAAAAAAAAAPJaEbuOk1ZnpJ4y6SR6yn+dY6fB5QaJHhb6QWgMXit2KCxWXxI6EBdJ5KeT +E7TgpleovIarz4zNAAnnwADg/zelTTDUGas2KxqgEH2WV5EilGEY8VASBgAAAAAAAAAAAJVJFZNI4Kxh1AUjFLdY7k6MLVY47YSfxE1acJIuEMlMpcRmX6qSDhc6QrMB +FD83ZUplM/sW8MloAAnvoAAAQCBUTUlenrKovQvyeNaLUYJBmxfT5hcuDQAAAAAAAAAAAIaxf5jfjr8MifLPauOJeh6OWDYxSUUPluG5E8hVh+rrWD95X96VDhf7Be0Q From c08a9bdac26b35c7182f40e85da4f4e103997d4a Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 15 Oct 2020 09:53:01 +0200 Subject: [PATCH 113/123] Only remove offer locally when necessary --- .../main/offer/takeoffer/TakeOfferDataModel.java | 6 ++++-- .../desktop/main/offer/takeoffer/TakeOfferView.java | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java index 8e2c92fc536..95cff8bc867 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -267,14 +267,16 @@ void onTabSelected(boolean isSelected) { priceFeedService.setCurrencyCode(offer.getCurrencyCode()); } - public void onClose() { + public void onClose(boolean removeOffer) { // We do not wait until the offer got removed by a network remove message but remove it // directly from the offer book. The broadcast gets now bundled and has 2 sec. delay so the // removal from the network is a bit slower as it has been before. To avoid that the taker gets // confused to see the same offer still in the offerbook we remove it manually. This removal has // only local effect. Other trader might see the offer for a few seconds // still (but cannot take it). - offerBook.removeOffer(checkNotNull(offer), tradeManager); + if (removeOffer) { + offerBook.removeOffer(checkNotNull(offer), tradeManager); + } btcWalletService.resetAddressEntriesForOpenOffer(offer.getId()); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java index 0ae302cb6d4..47316e0ace5 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java @@ -388,7 +388,7 @@ public void initWithData(Offer offer) { if (offer.getPrice() == null) new Popup().warning(Res.get("takeOffer.noPriceFeedAvailable")) - .onClose(this::close) + .onClose(() -> close(false)) .show(); } @@ -595,7 +595,11 @@ private void updateOfferElementsStyle() { /////////////////////////////////////////////////////////////////////////////////////////// private void close() { - model.dataModel.onClose(); + close(true); + } + + private void close(boolean removeOffer) { + model.dataModel.onClose(removeOffer); if (closeHandler != null) closeHandler.close(); } @@ -893,7 +897,7 @@ private void addButtons() { cancelButton1.setDefaultButton(false); cancelButton1.setOnAction(e -> { model.dataModel.swapTradeToSavings(); - close(); + close(false); }); } @@ -1040,7 +1044,7 @@ private void addFundingGroup() { }) .show(); } else { - close(); + close(false); model.dataModel.swapTradeToSavings(); } }); From 4e2e523beb98c7de8d6eb38f362c6f4934681ec4 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 15 Oct 2020 13:08:16 +0200 Subject: [PATCH 114/123] Update data stores for v1.4.0 --- p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET | 4 ++-- p2p/src/main/resources/DaoStateStore_BTC_MAINNET | 4 ++-- p2p/src/main/resources/SignedWitnessStore_BTC_MAINNET | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET b/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET index abff0907ebf..c1ca4d397fb 100644 --- a/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET +++ b/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f485f1a26f0d272074989b28815ad2e8140e2ddf9f83479849888e28e18749d2 -size 1923123 +oid sha256:fc675fd6911fb133981659742ad5717092bb76e7cfe305efb8dfd6324c46c49a +size 1986580 diff --git a/p2p/src/main/resources/DaoStateStore_BTC_MAINNET b/p2p/src/main/resources/DaoStateStore_BTC_MAINNET index ed5ce937053..419e7425766 100644 --- a/p2p/src/main/resources/DaoStateStore_BTC_MAINNET +++ b/p2p/src/main/resources/DaoStateStore_BTC_MAINNET @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f56bde9875da3bb6a0be7495af23d2c139dfd500e96104a5f295b909ed8416b4 -size 84233840 +oid sha256:ed3827d6a54097f700f4e71700cc3d2596100b95fde9f6d9fba3e42da8be728b +size 89892896 diff --git a/p2p/src/main/resources/SignedWitnessStore_BTC_MAINNET b/p2p/src/main/resources/SignedWitnessStore_BTC_MAINNET index 22809a74550..fe058e7e8d7 100644 --- a/p2p/src/main/resources/SignedWitnessStore_BTC_MAINNET +++ b/p2p/src/main/resources/SignedWitnessStore_BTC_MAINNET @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f91435a2a57eb6a72822bb379b49ca55440c14adc1059419226075c9752832b1 -size 3680403 +oid sha256:6db33a2613ff1eaac851a00ae544d3ad23a7fa76b9dc772fda27738e0618e14d +size 4059256 From 10b05aa805254be2be9f8e84553846af809b334b Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 15 Oct 2020 19:51:51 +0200 Subject: [PATCH 115/123] Revert jdk.module.illegalAccess=deny It doesn't work when running from the release binaries as JFoenix is relying on it right now. --- desktop/package/linux/package.sh | 1 - desktop/package/macosx/create_app.sh | 1 - desktop/package/windows/package.bat | 1 - 3 files changed, 3 deletions(-) diff --git a/desktop/package/linux/package.sh b/desktop/package/linux/package.sh index 553336f0cdf..7e011f89eb2 100755 --- a/desktop/package/linux/package.sh +++ b/desktop/package/linux/package.sh @@ -111,7 +111,6 @@ $JAVA_HOME/bin/javapackager \ -BjvmOptions=-Xss1280k \ -BjvmOptions=-XX:MaxRAM=4g \ -BjvmOptions=-Djava.net.preferIPv4Stack=true \ - -BjvmOptions=-Djdk.module.illegalAccess=deny \ -outfile Bisq-$version \ -v diff --git a/desktop/package/macosx/create_app.sh b/desktop/package/macosx/create_app.sh index 70781f1dc0b..f9ed5f2074c 100755 --- a/desktop/package/macosx/create_app.sh +++ b/desktop/package/macosx/create_app.sh @@ -91,7 +91,6 @@ $JAVA_HOME/bin/javapackager \ -srcfiles "Bisq-$version.jar" \ -appclass bisq.desktop.app.BisqAppMain \ -outfile Bisq \ - -BjvmOptions=-Djdk.module.illegalAccess=deny \ -v open deploy diff --git a/desktop/package/windows/package.bat b/desktop/package/windows/package.bat index 2ca93718c21..e6ac7c223fa 100644 --- a/desktop/package/windows/package.bat +++ b/desktop/package/windows/package.bat @@ -111,7 +111,6 @@ call "%JAVA_HOME%\bin\javapackager.exe" -deploy ^ -srcdir "%package_dir%" ^ -srcfiles %jar_filename% ^ -outfile Bisq ^ --BjvmOptions=-Djdk.module.illegalAccess=deny ^ -v if not exist "%package_dir%\windows\Bisq-%version%.exe" ( From bac1e7b04c85357ff0ffdc82a482f8ab490aa4f0 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 16 Oct 2020 14:47:43 +0200 Subject: [PATCH 116/123] Revert to SNAPSHOT version --- build.gradle | 2 +- desktop/package/linux/Dockerfile | 2 +- desktop/package/linux/package.sh | 2 +- desktop/package/linux/release.sh | 2 +- desktop/package/macosx/create_app.sh | 2 +- desktop/package/macosx/finalize.sh | 2 +- desktop/package/macosx/insert_snapshot_version.sh | 2 +- desktop/package/windows/package.bat | 2 +- desktop/package/windows/release.bat | 2 +- relay/src/main/resources/version.txt | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 56cdb1cc792..a731ffc0a85 100644 --- a/build.gradle +++ b/build.gradle @@ -385,7 +385,7 @@ configure(project(':desktop')) { apply plugin: 'witness' apply from: '../gradle/witness/gradle-witness.gradle' - version = '1.4.0' + version = '1.4.0-SNAPSHOT' mainClassName = 'bisq.desktop.app.BisqAppMain' diff --git a/desktop/package/linux/Dockerfile b/desktop/package/linux/Dockerfile index 1da1e5f5cc0..9108432ed0f 100644 --- a/desktop/package/linux/Dockerfile +++ b/desktop/package/linux/Dockerfile @@ -8,7 +8,7 @@ # pull base image FROM openjdk:8-jdk -ENV version 1.4.0 +ENV version 1.4.0-SNAPSHOT RUN apt-get update && apt-get install -y --no-install-recommends openjfx && rm -rf /var/lib/apt/lists/* && apt-get install -y vim fakeroot diff --git a/desktop/package/linux/package.sh b/desktop/package/linux/package.sh index 7e011f89eb2..120932617a4 100755 --- a/desktop/package/linux/package.sh +++ b/desktop/package/linux/package.sh @@ -6,7 +6,7 @@ # - Update version below # - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory -version=1.4.0 +version=1.4.0-SNAPSHOT version_base=$(echo $version | awk -F'[_-]' '{print $1}') if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then diff --git a/desktop/package/linux/release.sh b/desktop/package/linux/release.sh index a46d48d3e77..1a10ba912d2 100755 --- a/desktop/package/linux/release.sh +++ b/desktop/package/linux/release.sh @@ -4,7 +4,7 @@ # Prior to running this script: # - Update version below -version=1.4.0 +version=1.4.0-SNAPSHOT base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../.. package_dir=$base_dir/desktop/package release_dir=$base_dir/desktop/release/$version diff --git a/desktop/package/macosx/create_app.sh b/desktop/package/macosx/create_app.sh index f9ed5f2074c..a9c9f2b46e9 100755 --- a/desktop/package/macosx/create_app.sh +++ b/desktop/package/macosx/create_app.sh @@ -6,7 +6,7 @@ mkdir -p deploy set -e -version="1.4.0" +version="1.4.0-SNAPSHOT" cd .. ./gradlew :desktop:build -x test shadowJar diff --git a/desktop/package/macosx/finalize.sh b/desktop/package/macosx/finalize.sh index fc61be73d5e..5c666570559 100755 --- a/desktop/package/macosx/finalize.sh +++ b/desktop/package/macosx/finalize.sh @@ -2,7 +2,7 @@ cd ../../ -version="1.4.0" +version="1.4.0-SNAPSHOT" target_dir="releases/$version" diff --git a/desktop/package/macosx/insert_snapshot_version.sh b/desktop/package/macosx/insert_snapshot_version.sh index 16879d62764..43022d333f6 100755 --- a/desktop/package/macosx/insert_snapshot_version.sh +++ b/desktop/package/macosx/insert_snapshot_version.sh @@ -2,7 +2,7 @@ cd $(dirname $0)/../../../ -version=1.3.9 +version=1.4.0 find . -type f \( -name "finalize.sh" \ -o -name "create_app.sh" \ diff --git a/desktop/package/windows/package.bat b/desktop/package/windows/package.bat index e6ac7c223fa..4627ebb6302 100644 --- a/desktop/package/windows/package.bat +++ b/desktop/package/windows/package.bat @@ -11,7 +11,7 @@ @echo off -set version=1.4.0 +set version=1.4.0-SNAPSHOT if not exist "%JAVA_HOME%\bin\javapackager.exe" ( if not exist "%ProgramFiles%\Java\jdk-10.0.2" ( echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK. diff --git a/desktop/package/windows/release.bat b/desktop/package/windows/release.bat index d6e8597ca5d..0c1c32f3586 100644 --- a/desktop/package/windows/release.bat +++ b/desktop/package/windows/release.bat @@ -6,7 +6,7 @@ @echo off -set version=1.4.0 +set version=1.4.0-SNAPSHOT set release_dir=%~dp0..\..\..\releases\%version% set package_dir=%~dp0.. diff --git a/relay/src/main/resources/version.txt b/relay/src/main/resources/version.txt index 88c5fb891dc..8f069aebb23 100644 --- a/relay/src/main/resources/version.txt +++ b/relay/src/main/resources/version.txt @@ -1 +1 @@ -1.4.0 +1.4.0-SNAPSHOT From 3e17058bd5902e47d55650d0b40442731ac73bc3 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Fri, 16 Oct 2020 11:54:26 -0300 Subject: [PATCH 117/123] Move segwit checkbox --- .../desktop/main/funds/deposit/DepositView.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java index 1195c069369..8aeb4ac4c1c 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java @@ -198,16 +198,17 @@ public void initialize() { addressTextField.setManaged(false); amountTextField.setManaged(false); - generateNewAddressSegwitCheckbox = addCheckBox(gridPane, ++gridRow, - Res.get("funds.deposit.generateAddressSegwit"), -20); + generateNewAddressButton = addButton(gridPane, ++gridRow, Res.get("funds.deposit.generateAddress"), -20); + GridPane.setColumnIndex(generateNewAddressButton, 0); + GridPane.setHalignment(generateNewAddressButton, HPos.LEFT); + + generateNewAddressSegwitCheckbox = addCheckBox(gridPane, gridRow, + Res.get("funds.deposit.generateAddressSegwit"), 0); generateNewAddressSegwitCheckbox.setAllowIndeterminate(false); generateNewAddressSegwitCheckbox.setSelected(true); GridPane.setColumnIndex(generateNewAddressSegwitCheckbox, 0); GridPane.setHalignment(generateNewAddressSegwitCheckbox, HPos.LEFT); - - generateNewAddressButton = addButton(gridPane, ++gridRow, Res.get("funds.deposit.generateAddress"), -20); - GridPane.setColumnIndex(generateNewAddressButton, 0); - GridPane.setHalignment(generateNewAddressButton, HPos.LEFT); + GridPane.setMargin(generateNewAddressSegwitCheckbox, new Insets(15, 0, 0, 250)); generateNewAddressButton.setOnAction(event -> { boolean segwit = generateNewAddressSegwitCheckbox.isSelected(); From 6b4d77fb1b700b30a7154ad30d834d4cc40b036d Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Fri, 16 Oct 2020 11:07:36 -0500 Subject: [PATCH 118/123] Handle Capabilities for encrypted messages (offer availibility request/response) We did check in Connection for SupportedCapabilitiesMessage and if a message is of that type we set the capability. But encrypted messages are wrapped in a PrefixedSealedAndSignedMessage so the payload is not visible as SupportedCapabilitiesMessage without decrypting it. We need to call maybeHandleSupportedCapabilitiesMessage at decrypting the message. We do that only for direct messages not for mailbox messages as we likely do not have a connection open to the peer in that case (otherwise it would not be a mailbox msg) and as we don't have the connection available (we get is as AddDataMessage broadcast from an peer, so could could not apply it to the Connection of the sender. --- p2p/src/main/java/bisq/network/p2p/P2PService.java | 1 + .../java/bisq/network/p2p/network/Connection.java | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 76396125462..509419bf24d 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -419,6 +419,7 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER); try { DecryptedMessageWithPubKey decryptedMsg = encryptionService.decryptAndVerify(sealedMsg.getSealedAndSigned()); + connection.maybeHandleSupportedCapabilitiesMessage(decryptedMsg.getNetworkEnvelope()); connection.getPeersNodeAddressOptional().ifPresentOrElse(nodeAddress -> decryptedDirectMessageListeners.forEach(e -> e.onDirectMessage(decryptedMsg, nodeAddress)), () -> { diff --git a/p2p/src/main/java/bisq/network/p2p/network/Connection.java b/p2p/src/main/java/bisq/network/p2p/network/Connection.java index dca03c133de..c6d3277ee35 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Connection.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Connection.java @@ -785,11 +785,9 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { return; } - if (networkEnvelope instanceof SupportedCapabilitiesMessage) { - boolean causedShutDown = handleSupportedCapabilitiesMessage(networkEnvelope); - if (causedShutDown) { - return; - } + boolean causedShutDown = maybeHandleSupportedCapabilitiesMessage(networkEnvelope); + if (causedShutDown) { + return; } if (networkEnvelope instanceof CloseConnectionMessage) { @@ -865,7 +863,11 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { } } - protected boolean handleSupportedCapabilitiesMessage(NetworkEnvelope networkEnvelope) { + public boolean maybeHandleSupportedCapabilitiesMessage(NetworkEnvelope networkEnvelope) { + if (!(networkEnvelope instanceof SupportedCapabilitiesMessage)) { + return false; + } + Capabilities supportedCapabilities = ((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities(); if (supportedCapabilities == null || supportedCapabilities.isEmpty()) { return false; From 0b38fccfd2585a8ade78b1db0ac8545ed2e84e7e Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Fri, 16 Oct 2020 13:27:21 -0500 Subject: [PATCH 119/123] Add file name to temp file at write to disk --- .../main/java/bisq/common/persistence/PersistenceManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/bisq/common/persistence/PersistenceManager.java b/common/src/main/java/bisq/common/persistence/PersistenceManager.java index ec8d1ee005f..6cdf98c9661 100644 --- a/common/src/main/java/bisq/common/persistence/PersistenceManager.java +++ b/common/src/main/java/bisq/common/persistence/PersistenceManager.java @@ -305,7 +305,7 @@ public void writeToDisk(protobuf.PersistableEnvelope serialized, @Nullable Runna tempFile = usedTempFilePath != null ? FileUtil.createNewFile(usedTempFilePath) - : File.createTempFile("temp", null, dir); + : File.createTempFile("temp_" + fileName, null, dir); // Don't use a new temp file path each time, as that causes the delete-on-exit hook to leak memory: tempFile.deleteOnExit(); From 8cb6a053341f69d85453cd31971330278d8952c0 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Fri, 16 Oct 2020 14:02:57 -0500 Subject: [PATCH 120/123] Fix min height for trade statistics table --- .../java/bisq/desktop/main/market/trades/TradesChartsView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java index 604d572f47d..fbd035ff82d 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java @@ -544,7 +544,7 @@ private ToggleButton getToggleButton(String label, TradesChartsViewModel.TickUni private void createTable() { tableView = new TableView<>(); - tableView.setMinHeight(120); + tableView.setMinHeight(80); tableView.setPrefHeight(130); VBox.setVgrow(tableView, Priority.ALWAYS); From 294b45dbad69f0391c64bcf814b20f5a1c53d82c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Fri, 16 Oct 2020 14:13:01 -0500 Subject: [PATCH 121/123] Republish trade statistics if not found in existing trade stats. Remove republishing at SellerProtocol as we don't know the capability of the peer at that moment and as we also want to republish for completed trades. --- .../java/bisq/core/trade/TradeManager.java | 13 +++++ .../core/trade/protocol/SellerProtocol.java | 26 +--------- .../SellerPublishesTradeStatistics.java | 38 ++------------ .../trade/statistics/TradeStatistics2.java | 34 +++++++++++++ .../trade/statistics/TradeStatistics3.java | 42 ++++++++++++++++ .../statistics/TradeStatisticsManager.java | 49 +++++++++++++++++++ 6 files changed, 142 insertions(+), 60 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index 42cb6b9e000..0d8d02a2414 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -40,6 +40,7 @@ import bisq.core.trade.protocol.TakerProtocol; import bisq.core.trade.protocol.TradeProtocol; import bisq.core.trade.protocol.TradeProtocolFactory; +import bisq.core.trade.statistics.ReferralIdService; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.User; import bisq.core.util.Validator; @@ -49,6 +50,7 @@ import bisq.network.p2p.DecryptedMessageWithPubKey; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.P2PService; +import bisq.network.p2p.network.TorNetworkNode; import bisq.common.ClockWatcher; import bisq.common.config.Config; @@ -83,6 +85,7 @@ import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -133,6 +136,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi private ErrorMessageHandler takeOfferRequestErrorMessageHandler; @Getter private final LongProperty numPendingTrades = new SimpleLongProperty(); + private final ReferralIdService referralIdService; private final DumpDelayedPayoutTx dumpDelayedPayoutTx; @Getter private final boolean allowFaultyDelayedTxs; @@ -158,6 +162,7 @@ public TradeManager(User user, ProcessModelServiceProvider processModelServiceProvider, ClockWatcher clockWatcher, PersistenceManager> persistenceManager, + ReferralIdService referralIdService, DumpDelayedPayoutTx dumpDelayedPayoutTx, @Named(Config.ALLOW_FAULTY_DELAYED_TXS) boolean allowFaultyDelayedTxs) { this.user = user; @@ -174,6 +179,7 @@ public TradeManager(User user, this.mediatorManager = mediatorManager; this.processModelServiceProvider = processModelServiceProvider; this.clockWatcher = clockWatcher; + this.referralIdService = referralIdService; this.dumpDelayedPayoutTx = dumpDelayedPayoutTx; this.allowFaultyDelayedTxs = allowFaultyDelayedTxs; this.persistenceManager = persistenceManager; @@ -324,6 +330,13 @@ public TradeProtocol getTradeProtocol(Trade trade) { private void initPersistedTrades() { tradableList.forEach(this::initPersistedTrade); persistedTradesInitialized.set(true); + + // We do not include failed trades as they should not be counted anyway in the trade statistics + Set allTrades = new HashSet<>(closedTradableManager.getClosedTrades()); + allTrades.addAll(tradableList.getList()); + String referralId = referralIdService.getOptionalReferralId().orElse(null); + boolean isTorNetworkNode = p2PService.getNetworkNode() instanceof TorNetworkNode; + tradeStatisticsManager.maybeRepublishTradeStatistics(allTrades, referralId, isTorNetworkNode); } private void initPersistedTrade(Trade trade) { diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java index 9f1acab3689..dd138fc0f9b 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java @@ -39,10 +39,6 @@ import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ResultHandler; -import bisq.common.util.Utilities; - -import java.util.Date; -import java.util.GregorianCalendar; import lombok.extern.slf4j.Slf4j; @@ -57,27 +53,6 @@ public SellerProtocol(SellerTrade trade) { super(trade); } - @Override - protected void onInitialized() { - super.onInitialized(); - - // We get called the constructor with any possible state and phase. As we don't want to log an error for such - // cases we use the alternative 'given' method instead of 'expect'. - - // We only re-publish for about 2 weeks after 1.4.0 release until most nodes have updated to - // achieve sufficient resilience. - boolean currentDateBeforeCutOffDate = new Date().before(Utilities.getUTCDate(2020, GregorianCalendar.NOVEMBER, 1)); - given(anyPhase(Trade.Phase.DEPOSIT_PUBLISHED, - Trade.Phase.DEPOSIT_CONFIRMED, - Trade.Phase.FIAT_SENT, - Trade.Phase.FIAT_RECEIVED, - Trade.Phase.PAYOUT_PUBLISHED) - .with(SellerEvent.STARTUP) - .preCondition(currentDateBeforeCutOffDate)) - .setup(tasks(SellerPublishesTradeStatistics.class)) - .executeTasks(); - } - /////////////////////////////////////////////////////////////////////////////////////////// // Mailbox @@ -92,6 +67,7 @@ public void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) } } + /////////////////////////////////////////////////////////////////////////////////////////// // Incoming messages /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java index 0489ac0e325..89b6ac5a14a 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerPublishesTradeStatistics.java @@ -17,22 +17,15 @@ package bisq.core.trade.protocol.tasks.seller; -import bisq.core.offer.Offer; -import bisq.core.offer.OfferPayload; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.statistics.TradeStatistics3; -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.NetworkNode; import bisq.network.p2p.network.TorNetworkNode; import bisq.common.app.Capability; import bisq.common.taskrunner.TaskRunner; -import java.util.HashMap; -import java.util.Map; - import lombok.extern.slf4j.Slf4j; import static com.google.common.base.Preconditions.checkNotNull; @@ -56,34 +49,9 @@ protected void run() { // Our peer has updated, so as we are the seller we will publish the trade statistics. // The peer as buyer does not publish anymore with v.1.4.0 (where Capability.TRADE_STATISTICS_3 was added) - Map extraDataMap = new HashMap<>(); - if (processModel.getReferralIdService().getOptionalReferralId().isPresent()) { - extraDataMap.put(OfferPayload.REFERRAL_ID, processModel.getReferralIdService().getOptionalReferralId().get()); - } - - NodeAddress mediatorNodeAddress = checkNotNull(trade.getMediatorNodeAddress()); - // The first 4 chars are sufficient to identify a mediator. - // For testing with regtest/localhost we use the full address as its localhost and would result in - // same values for multiple mediators. - NetworkNode networkNode = model.getProcessModel().getP2PService().getNetworkNode(); - String truncatedMediatorNodeAddress = networkNode instanceof TorNetworkNode ? - mediatorNodeAddress.getFullAddress().substring(0, 4) : - mediatorNodeAddress.getFullAddress(); - - NodeAddress refundAgentNodeAddress = checkNotNull(trade.getRefundAgentNodeAddress()); - String truncatedRefundAgentNodeAddress = networkNode instanceof TorNetworkNode ? - refundAgentNodeAddress.getFullAddress().substring(0, 4) : - refundAgentNodeAddress.getFullAddress(); - - Offer offer = checkNotNull(trade.getOffer()); - TradeStatistics3 tradeStatistics = new TradeStatistics3(offer.getCurrencyCode(), - trade.getTradePrice().getValue(), - trade.getTradeAmountAsLong(), - offer.getPaymentMethod().getId(), - trade.getTakeOfferDate().getTime(), - truncatedMediatorNodeAddress, - truncatedRefundAgentNodeAddress, - extraDataMap); + String referralId = processModel.getReferralIdService().getOptionalReferralId().orElse(null); + boolean isTorNetworkNode = model.getProcessModel().getP2PService().getNetworkNode() instanceof TorNetworkNode; + TradeStatistics3 tradeStatistics = TradeStatistics3.from(trade, referralId, isTorNetworkNode); if (tradeStatistics.isValid()) { log.info("Publishing trade statistics"); processModel.getP2PService().addPersistableNetworkPayload(tradeStatistics, true); diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java index 41f753bc3b6..0b7830a34cb 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java @@ -21,9 +21,12 @@ import bisq.core.monetary.AltcoinExchangeRate; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; +import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; import bisq.core.offer.OfferUtil; +import bisq.core.trade.Trade; +import bisq.network.p2p.NodeAddress; import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload; @@ -46,6 +49,7 @@ import com.google.common.base.Charsets; import java.util.Date; +import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -67,6 +71,36 @@ public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, CapabilityRequiringPayload, Comparable { + public static TradeStatistics2 from(Trade trade, + @Nullable String referralId, + boolean isTorNetworkNode) { + Map extraDataMap = new HashMap<>(); + if (referralId != null) { + extraDataMap.put(OfferPayload.REFERRAL_ID, referralId); + } + + NodeAddress mediatorNodeAddress = trade.getMediatorNodeAddress(); + if (mediatorNodeAddress != null) { + // The first 4 chars are sufficient to identify a mediator. + // For testing with regtest/localhost we use the full address as its localhost and would result in + // same values for multiple mediators. + String address = isTorNetworkNode ? + mediatorNodeAddress.getFullAddress().substring(0, 4) : + mediatorNodeAddress.getFullAddress(); + extraDataMap.put(TradeStatistics2.MEDIATOR_ADDRESS, address); + } + + Offer offer = trade.getOffer(); + checkNotNull(offer, "offer must not ne null"); + checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not ne null"); + return new TradeStatistics2(offer.getOfferPayload(), + trade.getTradePrice(), + trade.getTradeAmount(), + trade.getDate(), + trade.getDepositTxId(), + extraDataMap); + } + @SuppressWarnings("SpellCheckingInspection") public static final String MEDIATOR_ADDRESS = "medAddr"; @SuppressWarnings("SpellCheckingInspection") diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java index 0fb4032f0aa..a3f3e996a07 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics3.java @@ -21,8 +21,12 @@ import bisq.core.monetary.AltcoinExchangeRate; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; +import bisq.core.offer.Offer; +import bisq.core.offer.OfferPayload; import bisq.core.offer.OfferUtil; +import bisq.core.trade.Trade; +import bisq.network.p2p.NodeAddress; import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; import bisq.network.p2p.storage.payload.DateSortedTruncatablePayload; import bisq.network.p2p.storage.payload.PersistableNetworkPayload; @@ -48,6 +52,7 @@ import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -66,6 +71,43 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, CapabilityRequiringPayload, DateSortedTruncatablePayload { + + public static TradeStatistics3 from(Trade trade, + @Nullable String referralId, + boolean isTorNetworkNode) { + Map extraDataMap = new HashMap<>(); + if (referralId != null) { + extraDataMap.put(OfferPayload.REFERRAL_ID, referralId); + } + + NodeAddress mediatorNodeAddress = checkNotNull(trade.getMediatorNodeAddress()); + // The first 4 chars are sufficient to identify a mediator. + // For testing with regtest/localhost we use the full address as its localhost and would result in + // same values for multiple mediators. + String truncatedMediatorNodeAddress = isTorNetworkNode ? + mediatorNodeAddress.getFullAddress().substring(0, 4) : + mediatorNodeAddress.getFullAddress(); + + // RefundAgentNodeAddress can be null if converted from old version. + String truncatedRefundAgentNodeAddress = null; + NodeAddress refundAgentNodeAddress = trade.getRefundAgentNodeAddress(); + if (refundAgentNodeAddress != null) { + truncatedRefundAgentNodeAddress = isTorNetworkNode ? + refundAgentNodeAddress.getFullAddress().substring(0, 4) : + refundAgentNodeAddress.getFullAddress(); + } + + Offer offer = checkNotNull(trade.getOffer()); + return new TradeStatistics3(offer.getCurrencyCode(), + trade.getTradePrice().getValue(), + trade.getTradeAmountAsLong(), + offer.getPaymentMethod().getId(), + trade.getTakeOfferDate().getTime(), + truncatedMediatorNodeAddress, + truncatedRefundAgentNodeAddress, + extraDataMap); + } + // This enum must not change the order as we use the ordinal for storage to reduce data size. // The payment method string can be quite long and would consume 15% more space. // When we get a new payment method we can add it to the enum at the end. Old users would add it as string if not diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java index 01a978c1a9e..83ae72b85b9 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -21,8 +21,11 @@ import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.provider.price.PriceFeedService; +import bisq.core.trade.BuyerTrade; +import bisq.core.trade.Trade; import bisq.network.p2p.P2PService; +import bisq.network.p2p.storage.P2PDataStorage; import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; import bisq.common.config.Config; @@ -46,6 +49,8 @@ import lombok.extern.slf4j.Slf4j; +import javax.annotation.Nullable; + @Singleton @Slf4j public class TradeStatisticsManager { @@ -140,4 +145,48 @@ private void maybeDumpStatistics() { list.toArray(array); jsonFileManager.writeToDiscThreaded(Utilities.objectToJson(array), "trade_statistics"); } + + public void maybeRepublishTradeStatistics(Set trades, + @Nullable String referralId, + boolean isTorNetworkNode) { + long ts = System.currentTimeMillis(); + Set hashes = tradeStatistics3StorageService.getMapOfAllData().keySet(); + trades.forEach(trade -> { + if (trade instanceof BuyerTrade) { + log.debug("Trade: {} is a buyer trade, we only republish we have been seller.", + trade.getShortId()); + return; + } + + TradeStatistics3 tradeStatistics3 = TradeStatistics3.from(trade, referralId, isTorNetworkNode); + boolean hasTradeStatistics3 = hashes.contains(new P2PDataStorage.ByteArray(tradeStatistics3.getHash())); + if (hasTradeStatistics3) { + log.debug("Trade: {}. We have already a tradeStatistics matching the hash of tradeStatistics3.", + trade.getShortId()); + return; + } + + // If we did not find a TradeStatistics3 we look up if we find a TradeStatistics3 converted from + // TradeStatistics2 where we used the original hash, which is not the native hash of the + // TradeStatistics3 but of TradeStatistics2. + TradeStatistics2 tradeStatistics2 = TradeStatistics2.from(trade, referralId, isTorNetworkNode); + boolean hasTradeStatistics2 = hashes.contains(new P2PDataStorage.ByteArray(tradeStatistics2.getHash())); + if (hasTradeStatistics2) { + log.debug("Trade: {}. We have already a tradeStatistics matching the hash of tradeStatistics2. ", + trade.getShortId()); + return; + } + + if (!tradeStatistics3.isValid()) { + log.warn("Trade: {}. Trade statistics is invalid. We do not publish it.", tradeStatistics3); + return; + } + + log.info("Trade: {}. We republish tradeStatistics3 as we did not find it in the existing trade statistics. ", + trade.getShortId()); + p2PService.addPersistableNetworkPayload(tradeStatistics3, true); + }); + log.info("maybeRepublishTradeStatistics took {} ms. Number of tradeStatistics: {}. Number of own trades: {}", + System.currentTimeMillis() - ts, hashes.size(), trades.size()); + } } From 7ae6e84e0264a9f54e011e464b259d125bffa9ac Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Sun, 18 Oct 2020 19:22:53 +0200 Subject: [PATCH 122/123] Bump version number for v1.4.1 --- build.gradle | 2 +- common/src/main/java/bisq/common/app/Version.java | 4 ++-- desktop/package/linux/Dockerfile | 2 +- desktop/package/linux/package.sh | 2 +- desktop/package/linux/release.sh | 2 +- desktop/package/macosx/Info.plist | 4 ++-- desktop/package/macosx/create_app.sh | 2 +- desktop/package/macosx/finalize.sh | 2 +- desktop/package/macosx/replace_version_number.sh | 4 ++-- desktop/package/windows/package.bat | 2 +- desktop/package/windows/release.bat | 2 +- relay/src/main/resources/version.txt | 2 +- seednode/src/main/java/bisq/seednode/SeedNodeMain.java | 2 +- 13 files changed, 16 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index a731ffc0a85..3281b156971 100644 --- a/build.gradle +++ b/build.gradle @@ -385,7 +385,7 @@ configure(project(':desktop')) { apply plugin: 'witness' apply from: '../gradle/witness/gradle-witness.gradle' - version = '1.4.0-SNAPSHOT' + version = '1.4.1' mainClassName = 'bisq.desktop.app.BisqAppMain' diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index 45fc057242b..f5390fedafa 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -30,12 +30,12 @@ public class Version { // VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update // Therefore all sub versions start again with 1 // We use semantic versioning with major, minor and patch - public static final String VERSION = "1.4.0"; + public static final String VERSION = "1.4.1"; /** * Holds a list of the versions of tagged resource files for optimizing the getData requests. */ - public static final List HISTORY = Arrays.asList("1.4.0"); + public static final List HISTORY = Arrays.asList("1.4.1"); public static int getMajorVersion(String version) { return getSubVersion(version, 0); diff --git a/desktop/package/linux/Dockerfile b/desktop/package/linux/Dockerfile index 9108432ed0f..2e6df3683b6 100644 --- a/desktop/package/linux/Dockerfile +++ b/desktop/package/linux/Dockerfile @@ -8,7 +8,7 @@ # pull base image FROM openjdk:8-jdk -ENV version 1.4.0-SNAPSHOT +ENV version 1.4.1 RUN apt-get update && apt-get install -y --no-install-recommends openjfx && rm -rf /var/lib/apt/lists/* && apt-get install -y vim fakeroot diff --git a/desktop/package/linux/package.sh b/desktop/package/linux/package.sh index 120932617a4..ed414206f1b 100755 --- a/desktop/package/linux/package.sh +++ b/desktop/package/linux/package.sh @@ -6,7 +6,7 @@ # - Update version below # - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory -version=1.4.0-SNAPSHOT +version=1.4.1 version_base=$(echo $version | awk -F'[_-]' '{print $1}') if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then diff --git a/desktop/package/linux/release.sh b/desktop/package/linux/release.sh index 1a10ba912d2..9d6c5c1031d 100755 --- a/desktop/package/linux/release.sh +++ b/desktop/package/linux/release.sh @@ -4,7 +4,7 @@ # Prior to running this script: # - Update version below -version=1.4.0-SNAPSHOT +version=1.4.1 base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../.. package_dir=$base_dir/desktop/package release_dir=$base_dir/desktop/release/$version diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index f701df84e23..fa8e62ae0d1 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.4.0 + 1.4.1 CFBundleShortVersionString - 1.4.0 + 1.4.1 CFBundleExecutable Bisq diff --git a/desktop/package/macosx/create_app.sh b/desktop/package/macosx/create_app.sh index a9c9f2b46e9..d19754e5d5c 100755 --- a/desktop/package/macosx/create_app.sh +++ b/desktop/package/macosx/create_app.sh @@ -6,7 +6,7 @@ mkdir -p deploy set -e -version="1.4.0-SNAPSHOT" +version="1.4.1" cd .. ./gradlew :desktop:build -x test shadowJar diff --git a/desktop/package/macosx/finalize.sh b/desktop/package/macosx/finalize.sh index 5c666570559..b4037d9d619 100755 --- a/desktop/package/macosx/finalize.sh +++ b/desktop/package/macosx/finalize.sh @@ -2,7 +2,7 @@ cd ../../ -version="1.4.0-SNAPSHOT" +version="1.4.1" target_dir="releases/$version" diff --git a/desktop/package/macosx/replace_version_number.sh b/desktop/package/macosx/replace_version_number.sh index 12f1963dd5a..7b0dbc9274d 100755 --- a/desktop/package/macosx/replace_version_number.sh +++ b/desktop/package/macosx/replace_version_number.sh @@ -2,8 +2,8 @@ cd $(dirname $0)/../../../ -oldVersion=1.3.9 -newVersion=1.4.0 +oldVersion=1.4.0 +newVersion=1.4.1 find . -type f \( -name "finalize.sh" \ -o -name "create_app.sh" \ diff --git a/desktop/package/windows/package.bat b/desktop/package/windows/package.bat index 4627ebb6302..2e179f484d2 100644 --- a/desktop/package/windows/package.bat +++ b/desktop/package/windows/package.bat @@ -11,7 +11,7 @@ @echo off -set version=1.4.0-SNAPSHOT +set version=1.4.1 if not exist "%JAVA_HOME%\bin\javapackager.exe" ( if not exist "%ProgramFiles%\Java\jdk-10.0.2" ( echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK. diff --git a/desktop/package/windows/release.bat b/desktop/package/windows/release.bat index 0c1c32f3586..0fe4849b832 100644 --- a/desktop/package/windows/release.bat +++ b/desktop/package/windows/release.bat @@ -6,7 +6,7 @@ @echo off -set version=1.4.0-SNAPSHOT +set version=1.4.1 set release_dir=%~dp0..\..\..\releases\%version% set package_dir=%~dp0.. diff --git a/relay/src/main/resources/version.txt b/relay/src/main/resources/version.txt index 8f069aebb23..347f5833ee6 100644 --- a/relay/src/main/resources/version.txt +++ b/relay/src/main/resources/version.txt @@ -1 +1 @@ -1.4.0-SNAPSHOT +1.4.1 diff --git a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java index 4d871ab462c..22f2a7efc03 100644 --- a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java @@ -32,7 +32,7 @@ @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { - private static final String VERSION = "1.4.0"; + private static final String VERSION = "1.4.1"; private SeedNode seedNode; public SeedNodeMain() { From b1fb57109ed1172940fd3f61a7eac20fcbbd2e5e Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 19 Oct 2020 09:41:33 +0200 Subject: [PATCH 123/123] Revert to SNAPSHOT version --- build.gradle | 2 +- desktop/package/linux/Dockerfile | 2 +- desktop/package/linux/package.sh | 2 +- desktop/package/linux/release.sh | 2 +- desktop/package/macosx/create_app.sh | 2 +- desktop/package/macosx/finalize.sh | 2 +- desktop/package/macosx/insert_snapshot_version.sh | 2 +- desktop/package/windows/package.bat | 2 +- desktop/package/windows/release.bat | 2 +- relay/src/main/resources/version.txt | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 3281b156971..2659c6591a3 100644 --- a/build.gradle +++ b/build.gradle @@ -385,7 +385,7 @@ configure(project(':desktop')) { apply plugin: 'witness' apply from: '../gradle/witness/gradle-witness.gradle' - version = '1.4.1' + version = '1.4.1-SNAPSHOT' mainClassName = 'bisq.desktop.app.BisqAppMain' diff --git a/desktop/package/linux/Dockerfile b/desktop/package/linux/Dockerfile index 2e6df3683b6..6c2de62bf76 100644 --- a/desktop/package/linux/Dockerfile +++ b/desktop/package/linux/Dockerfile @@ -8,7 +8,7 @@ # pull base image FROM openjdk:8-jdk -ENV version 1.4.1 +ENV version 1.4.1-SNAPSHOT RUN apt-get update && apt-get install -y --no-install-recommends openjfx && rm -rf /var/lib/apt/lists/* && apt-get install -y vim fakeroot diff --git a/desktop/package/linux/package.sh b/desktop/package/linux/package.sh index ed414206f1b..a1d26a8f7b8 100755 --- a/desktop/package/linux/package.sh +++ b/desktop/package/linux/package.sh @@ -6,7 +6,7 @@ # - Update version below # - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory -version=1.4.1 +version=1.4.1-SNAPSHOT version_base=$(echo $version | awk -F'[_-]' '{print $1}') if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then diff --git a/desktop/package/linux/release.sh b/desktop/package/linux/release.sh index 9d6c5c1031d..1bda2580730 100755 --- a/desktop/package/linux/release.sh +++ b/desktop/package/linux/release.sh @@ -4,7 +4,7 @@ # Prior to running this script: # - Update version below -version=1.4.1 +version=1.4.1-SNAPSHOT base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../.. package_dir=$base_dir/desktop/package release_dir=$base_dir/desktop/release/$version diff --git a/desktop/package/macosx/create_app.sh b/desktop/package/macosx/create_app.sh index d19754e5d5c..950f78fca90 100755 --- a/desktop/package/macosx/create_app.sh +++ b/desktop/package/macosx/create_app.sh @@ -6,7 +6,7 @@ mkdir -p deploy set -e -version="1.4.1" +version="1.4.1-SNAPSHOT" cd .. ./gradlew :desktop:build -x test shadowJar diff --git a/desktop/package/macosx/finalize.sh b/desktop/package/macosx/finalize.sh index b4037d9d619..f2f5e47b3a8 100755 --- a/desktop/package/macosx/finalize.sh +++ b/desktop/package/macosx/finalize.sh @@ -2,7 +2,7 @@ cd ../../ -version="1.4.1" +version="1.4.1-SNAPSHOT" target_dir="releases/$version" diff --git a/desktop/package/macosx/insert_snapshot_version.sh b/desktop/package/macosx/insert_snapshot_version.sh index 43022d333f6..b081beaa16b 100755 --- a/desktop/package/macosx/insert_snapshot_version.sh +++ b/desktop/package/macosx/insert_snapshot_version.sh @@ -2,7 +2,7 @@ cd $(dirname $0)/../../../ -version=1.4.0 +version=1.4.1 find . -type f \( -name "finalize.sh" \ -o -name "create_app.sh" \ diff --git a/desktop/package/windows/package.bat b/desktop/package/windows/package.bat index 2e179f484d2..67bc92b2dd2 100644 --- a/desktop/package/windows/package.bat +++ b/desktop/package/windows/package.bat @@ -11,7 +11,7 @@ @echo off -set version=1.4.1 +set version=1.4.1-SNAPSHOT if not exist "%JAVA_HOME%\bin\javapackager.exe" ( if not exist "%ProgramFiles%\Java\jdk-10.0.2" ( echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK. diff --git a/desktop/package/windows/release.bat b/desktop/package/windows/release.bat index 0fe4849b832..8af320917b3 100644 --- a/desktop/package/windows/release.bat +++ b/desktop/package/windows/release.bat @@ -6,7 +6,7 @@ @echo off -set version=1.4.1 +set version=1.4.1-SNAPSHOT set release_dir=%~dp0..\..\..\releases\%version% set package_dir=%~dp0.. diff --git a/relay/src/main/resources/version.txt b/relay/src/main/resources/version.txt index 347f5833ee6..d65937f100b 100644 --- a/relay/src/main/resources/version.txt +++ b/relay/src/main/resources/version.txt @@ -1 +1 @@ -1.4.1 +1.4.1-SNAPSHOT